diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index 493310882..d4869ef3e 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -2211,18 +2211,32 @@ */ #define MBEDTLS_PKCS5_C +/** + * \def MBEDTLS_PKCS11_CLIENT_C + * + * Enable support for keys stored in an external token, using the + * cryptoki (PKCS#11) interface. + * + * Module: library/pkcs11_client.c + * + * This module requires a PKCS#11 library. + * + */ +#define MBEDTLS_PKCS11_CLIENT_C + /** * \def MBEDTLS_PKCS11_C * - * Enable wrapper for PKCS#11 smartcard support. + * Enable wrapper for PKCS#11 token support with libpkcs11-helper. * * Module: library/pkcs11.c * Caller: library/pk.c * * Requires: MBEDTLS_PK_C * - * This module enables SSL/TLS PKCS #11 smartcard support. - * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) + * This module enables PKCS #11 token support, e.g. to use a private + * key stored in a smartcard for an SSL/TLS connection. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper). */ //#define MBEDTLS_PKCS11_C diff --git a/include/mbedtls/pkcs11_client.h b/include/mbedtls/pkcs11_client.h new file mode 100644 index 000000000..83aed5157 --- /dev/null +++ b/include/mbedtls/pkcs11_client.h @@ -0,0 +1,150 @@ +/** + * \file pkcs11_client.h + * + * \brief Generic wrapper for Cryptoki (PKCS#11) support + * + * Copyright (C) 2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef MBEDTLS_PKCS11_CLIENT_H +#define MBEDTLS_PKCS11_CLIENT_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PKCS11_CLIENT_C) + +#include + +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_PKCS11_FLAG_TOKEN ( (uint32_t) 0x80000000 ) + +#if defined(MBEDTLS_PK_C) + +#define MBEDTLS_PK_FLAG_SENSITIVE ( (uint32_t) 0x00000001 ) +#define MBEDTLS_PK_FLAG_EXTRACTABLE ( (uint32_t) 0x00000002 ) +#define MBEDTLS_PK_FLAG_SIGN ( (uint32_t) 0x00000010 ) +#define MBEDTLS_PK_FLAG_VERIFY ( (uint32_t) 0x00000020 ) +#define MBEDTLS_PK_FLAG_DECRYPT ( (uint32_t) 0x00000040 ) +#define MBEDTLS_PK_FLAG_ENCRYPT ( (uint32_t) 0x00000080 ) + +#include "pk.h" + +/** + * \brief Set up a PK context for a key pair in a PKCS#11 token + * + * \param ctx PK context to fill, which must have been initialized + * with mbedtls_pk_init(). + * \param hSession Cryptoki session. + * \param hPublicKey Cryptoki handle of the public key. + * \param hPrivateKey Cryptoki handle of the private key, or + * CK_INVALID_HANDLE for a public key rather than a key + * pair. + * + * \return 0 on success, + * or MBEDTLS_ERR_PK_XXX error code. + * + * \note The session and the key(s) must remain valid until the + * PK context is closed with mbedtls_pk_free(). As an + * exception, it's ok to call mbedtls_pk_free() itself + * even if the Cryptoki handles have become invalid. + */ +int mbedtls_pk_setup_pkcs11( mbedtls_pk_context *ctx, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hPublicKey, + CK_OBJECT_HANDLE hPrivateKey ); + +/** + * \brief Import a transparent key into a PKCS#11 token + * + * This function imports a PK object containing a + * public key or a private-public key pair into a + * PKCS#11 token. + * + * \param ctx PK context, which must contain a transparent pk + * object (type \c MBEDTLS_PK_RSA, + * \c MBEDTLS_PK_RSASSA_PSS, \c MBEDTLS_PK_ECKEY or + * \c MBEDTLS_PK_ECDSA). + * \param flags Mask of \c MBEDTLS_PKCS11_FLAG_XXX and + * \c MBEDTLS_PK_FLAG_XXX, applying as follows: + * - \c MBEDTLS_PKCS11_FLAG_TOKEN: PKCS#11 \c CKA_TOKEN + * flag: if set, import as token object; if clear, + * import as session object. + * - \c MBEDTLS_PK_FLAG_EXTRACTABLE: PKCS#11 + * \c CKA_EXTRACTABLE flag: if set, the key will be + * extractable at least in wrapped form; if clear, + * the key will not be extractable at all. + * - \c MBEDTLS_PK_FLAG_SENSITIVE: PKCS#11 + * \c CKA_SENSITIVE flag: if set, the key will be + * not be extractable in plain form; if clear, the + * key will be extractable at least in wrapped form. + * - \c MBEDTLS_PK_FLAG_SIGN: if set, the private key + * will be authorized for signing. + * - \c MBEDTLS_PK_FLAG_VERIFY: if set, the public key + * will be authorized for verification. + * - \c MBEDTLS_PK_FLAG_DECRYPT: if set, the private key + * will be authorized for signing. + * - \c MBEDTLS_PK_FLAG_ENCRYPT: if set, the public key + * will be authorized for encryption. + * + * \param hSession Cryptoki session. + * \param hPublicKey If non-null, on output, Cryptoki handle of the public + * key. If null, the public key is not imported. + * \param hPrivateKey If non-null, on output, Cryptoki handle of the private + * key. If null, the private key is not imported. + * + * \return 0 on success, + * or MBEDTLS_ERR_PK_XXX error code. + * + * \note If \c hPrivateKey is non-null then \c ctx must contain + * a full key pair. If \c hPrivateKey is null then \c ctx + * may contain a full key pair or just a public key. + * + * \note On failure, the values returned in \c hPublicKey and + * \c hPrivateKey will normally be \c CK_HANDLE_INVALID. + * One of them may be a valid handle in the unlikely case + * where the creation of one key object succeeded but + * the second one failed and destroying the first one + * also failed, for example because the token was + * disconnected. + */ +int mbedtls_pk_import_to_pkcs11( const mbedtls_pk_context *ctx, + uint32_t flags, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE *hPublicKey, + CK_OBJECT_HANDLE *hPrivateKey ); + +#endif /* MBEDTLS_PK_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_PKCS11_CLIENT_C */ + +#endif /* MBEDTLS_PKCS11_H */ diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index e02229d03..1c5b71b7f 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -41,6 +41,7 @@ set(src_crypto pem.c pk.c pk_wrap.c + pkcs11_client.c pkcs12.c pkcs5.c pkparse.c diff --git a/library/Makefile b/library/Makefile index 541d47fe9..573edd9cf 100644 --- a/library/Makefile +++ b/library/Makefile @@ -57,7 +57,8 @@ OBJS_CRYPTO= aes.o aesni.o arc4.o \ md4.o md5.o md_wrap.o \ memory_buffer_alloc.o oid.o \ padlock.o pem.o pk.o \ - pk_wrap.o pkcs12.o pkcs5.o \ + pk_wrap.o pkcs11_client.o \ + pkcs12.o pkcs5.o \ pkparse.o pkwrite.o platform.o \ ripemd160.o rsa_internal.o rsa.o \ sha1.o sha256.o sha512.o \ diff --git a/library/pkcs11_client.c b/library/pkcs11_client.c new file mode 100644 index 000000000..79d6648fd --- /dev/null +++ b/library/pkcs11_client.c @@ -0,0 +1,478 @@ +/* + * Generic wrapper for Cryptoki (PKCS#11) support + * + * Copyright (C) 2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PKCS11_CLIENT_C) + +#include +#include +#include + +#include "mbedtls/pkcs11_client.h" + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#define mbedtls_calloc calloc +#define mbedtls_free free +#endif + + + +#if defined(MBEDTLS_PK_C) +#include "mbedtls/pk.h" +#include "mbedtls/pk_info.h" + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/bignum.h" +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/asn1.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/bignum.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/ecp.h" +#include "mbedtls/oid.h" +#endif + +#define ARRAY_LENGTH( a ) ( sizeof( a ) / sizeof( *( a ) ) ) + +typedef struct { + mbedtls_pk_type_t key_type; /**< key type */ + CK_SESSION_HANDLE hSession; /**< session handle */ + CK_OBJECT_HANDLE hPublicKey; /**< public key handle (must not be null) */ + CK_OBJECT_HANDLE hPrivateKey; /**< private key handle (may be null) */ + uint16_t bit_length; /**< key length in bits */ +} mbedtls_pk_pkcs11_context_t; + +static int pkcs11_err_to_mbedtls_pk_err( CK_RV rv ) +{ + switch( rv ) + { + case CKR_OK: + return( 0 ); + case CKR_HOST_MEMORY: + return( MBEDTLS_ERR_PK_ALLOC_FAILED ); + case CKR_ARGUMENTS_BAD: + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + case CKR_KEY_FUNCTION_NOT_PERMITTED: + return( MBEDTLS_ERR_PK_NOT_PERMITTED ); + case CKR_MECHANISM_INVALID: + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + case CKR_MECHANISM_PARAM_INVALID: + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + case CKR_OBJECT_HANDLE_INVALID: + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + case CKR_SIGNATURE_INVALID: + return( MBEDTLS_ERR_PK_INVALID_SIGNATURE ); + case CKR_SIGNATURE_LEN_RANGE: + return( MBEDTLS_ERR_PK_SIG_LEN_MISMATCH ); + case CKR_TEMPLATE_INCOMPLETE: + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + case CKR_BUFFER_TOO_SMALL: + return( MBEDTLS_ERR_PK_BUFFER_TOO_SMALL ); + default: + return( MBEDTLS_ERR_PK_FILE_IO_ERROR ); + } +} + +static size_t pkcs11_pk_get_bitlen( const void *ctx_arg ) +{ + const mbedtls_pk_pkcs11_context_t *ctx = ctx_arg; + return( ctx->bit_length ); +} + +static int pkcs11_pk_can_do( const void *ctx_arg, mbedtls_pk_type_t type ) +{ + const mbedtls_pk_pkcs11_context_t *ctx = ctx_arg; + return ctx->key_type == mbedtls_pk_representation_type( type ); +} + +static void *pkcs11_pk_alloc( ) +{ + return( mbedtls_calloc( 1, sizeof( mbedtls_pk_pkcs11_context_t ) ) ); +} + +static void pkcs11_pk_free( void *ctx ) +{ + mbedtls_free( ctx ); +} + +static size_t pkcs11_pk_signature_size( const void *ctx_arg ) +{ + const mbedtls_pk_pkcs11_context_t *ctx = ctx_arg; + switch( ctx->key_type ) + { + case MBEDTLS_PK_RSA: + return( ( ctx->bit_length + 7 ) / 8 ); + case MBEDTLS_PK_ECKEY: + return( MBEDTLS_ECDSA_MAX_SIG_LEN( ctx->bit_length ) ); + default: + return( 0 ); + } +} + +static int pkcs11_sign( void *ctx_arg, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + mbedtls_pk_pkcs11_context_t *ctx = ctx_arg; + CK_RV rv; + CK_MECHANISM mechanism = {0, NULL_PTR, 0}; + CK_ULONG ck_sig_len; + + /* This function takes size_t arguments but the underlying layer + takes unsigned long. Either type may be smaller than the other. + Legitimate values won't overflow either type but we still need + to check for overflow for robustness. */ + if( hash_len > (CK_ULONG)( -1 ) ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + (void) f_rng; + (void) p_rng; + + switch( ctx->key_type ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + ck_sig_len = ( ctx->bit_length + 7 ) / 8; + // FIXME: these mechanisms perform hashing as well as signing. + // But here we get the hash as input. So we need to invoke + // CKM_RSA_PKCS. But CKM_RSA_PKCS doesn't perform the hash + // encoding, only a part of the padding. + switch( md_alg ) + { + case MBEDTLS_MD_MD5: + mechanism.mechanism = CKM_MD5_RSA_PKCS; + break; + case MBEDTLS_MD_SHA1: + mechanism.mechanism = CKM_SHA1_RSA_PKCS; + break; + case MBEDTLS_MD_SHA256: + mechanism.mechanism = CKM_SHA256_RSA_PKCS; + break; + case MBEDTLS_MD_SHA384: + mechanism.mechanism = CKM_SHA384_RSA_PKCS; + break; + case MBEDTLS_MD_SHA512: + mechanism.mechanism = CKM_SHA512_RSA_PKCS; + break; + default: + return( MBEDTLS_ERR_PK_INVALID_ALG ); + } + break; +#endif /* MBEDTLS_RSA_C */ + default: + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + } + + rv = C_SignInit( ctx->hSession, &mechanism, ctx->hPrivateKey ); + if( rv != CKR_OK ) + goto exit; + rv = C_Sign( ctx->hSession, (CK_BYTE_PTR) hash, hash_len, + sig, &ck_sig_len ); + if( rv != CKR_OK ) + goto exit; + + *sig_len = ck_sig_len; + +exit: + if( rv != CKR_OK ) + memset( sig, 0, ck_sig_len ); + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); +} + +static const mbedtls_pk_info_t mbedtls_pk_pkcs11_info = { + MBEDTLS_PK_OPAQUE, + "pkcs11", + pkcs11_pk_get_bitlen, + pkcs11_pk_can_do, //can_do + NULL, //pkcs11_verify, + pkcs11_sign, + NULL, //pkcs11_decrypt, + NULL, //pkcs11_encrypt, + NULL, //check_pair_func + pkcs11_pk_alloc, + pkcs11_pk_free, + NULL, //debug_func + pkcs11_pk_signature_size, +}; + +int mbedtls_pk_setup_pkcs11( mbedtls_pk_context *ctx, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hPublicKey, + CK_OBJECT_HANDLE hPrivateKey ) +{ + CK_OBJECT_CLASS public_key_class = -1, private_key_class = -1; + CK_KEY_TYPE public_key_type = -1, private_key_type = -1; + mbedtls_pk_type_t can_do; + CK_ATTRIBUTE attributes[] = { + {CKA_CLASS, &public_key_class, sizeof( public_key_class )}, + {CKA_KEY_TYPE, &public_key_type, sizeof( public_key_type )}, + }; + CK_RV rv; + uint16_t key_size = 0; + + rv = C_GetAttributeValue( hSession, hPublicKey, + attributes, ARRAY_LENGTH( attributes ) ); + if( rv != CKR_OK ) + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); + if( public_key_class != CKO_PUBLIC_KEY ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + + if( hPrivateKey != CK_INVALID_HANDLE ) + { + attributes[0].pValue = &private_key_class; + attributes[1].pValue = &private_key_type; + rv = C_GetAttributeValue( hSession, hPrivateKey, + attributes, ARRAY_LENGTH( attributes ) ); + if( rv != CKR_OK ) + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); + if( private_key_class != CKO_PRIVATE_KEY ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + if( public_key_type != private_key_type ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + } + + switch( public_key_type ) { +#if defined(MBEDTLS_RSA_C) + case CKK_RSA: + can_do = MBEDTLS_PK_RSA; + { + CK_ULONG modulus_bits; + attributes[0].type = CKA_MODULUS_BITS; + attributes[0].pValue = &modulus_bits; + attributes[0].ulValueLen = sizeof( modulus_bits ); + rv = C_GetAttributeValue( hSession, hPrivateKey, attributes, 1 ); + if( rv != CKR_OK ) + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); + if( modulus_bits > (uint16_t)( -1 ) ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + key_size = modulus_bits; + } + break; +#endif /* MBEDTLS_RSA_C */ + default: + can_do = MBEDTLS_PK_OPAQUE; + break; + } + + { + int ret = mbedtls_pk_setup( ctx, &mbedtls_pk_pkcs11_info ); + if( ret != 0 ) + return( MBEDTLS_ERR_PK_ALLOC_FAILED ); + } + { + mbedtls_pk_pkcs11_context_t *pkcs11_ctx = ctx->pk_ctx; + pkcs11_ctx->key_type = can_do; + pkcs11_ctx->bit_length = key_size; + pkcs11_ctx->hSession = hSession; + pkcs11_ctx->hPublicKey = hPublicKey; + pkcs11_ctx->hPrivateKey = hPrivateKey; + } + return( 0 ); +} + +#if defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECDSA_C) +static int mpi_to_ck( const mbedtls_mpi *mpi, + CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE at, + unsigned char **p, size_t len ) +{ + if( mbedtls_mpi_write_binary( mpi, *p, len ) != 0 ) + return( 0 ); + attr->type = at; + attr->pValue = *p; + attr->ulValueLen = len; + *p += len; + return( 1 ); +} +#define MPI_TO_CK( mpi, attr, at, p, len ) \ + do \ + { \ + if( !mpi_to_ck( ( mpi ), ( attr ), ( at ), ( p ), ( len ) ) ) \ + { \ + rv = CKR_ARGUMENTS_BAD; \ + goto exit; \ + } \ + } \ + while( 0 ) +#endif /* defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECDSA_C) */ + +#define CK_BOOL( x ) ( ( x ) ? CK_TRUE : CK_FALSE ) + +int mbedtls_pk_import_to_pkcs11( const mbedtls_pk_context *ctx, + uint32_t flags, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE *hPublicKey, + CK_OBJECT_HANDLE *hPrivateKey ) +{ + CK_OBJECT_CLASS cko_private_key = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS cko_public_key = CKO_PUBLIC_KEY; + CK_KEY_TYPE ck_key_type; + CK_BBOOL ck_sensitive = CK_BOOL( flags & MBEDTLS_PK_FLAG_SENSITIVE ); + CK_BBOOL ck_extractable = CK_BOOL( flags & MBEDTLS_PK_FLAG_EXTRACTABLE ); + CK_BBOOL ck_sign = CK_BOOL( flags & MBEDTLS_PK_FLAG_SIGN ); + CK_BBOOL ck_verify = CK_BOOL( flags & MBEDTLS_PK_FLAG_VERIFY ); + CK_BBOOL ck_decrypt = CK_BOOL( flags & MBEDTLS_PK_FLAG_DECRYPT ); + CK_BBOOL ck_encrypt = CK_BOOL( flags & MBEDTLS_PK_FLAG_ENCRYPT ); + CK_BBOOL ck_token = CK_BOOL( flags & MBEDTLS_PKCS11_FLAG_TOKEN ); + CK_ATTRIBUTE public_attributes[] = { + {CKA_CLASS, &cko_public_key, sizeof( cko_public_key )}, + {CKA_KEY_TYPE, &ck_key_type, sizeof( ck_key_type )}, + {CKA_TOKEN, &ck_token, sizeof( ck_token )}, + {CKA_ENCRYPT, &ck_encrypt, sizeof( ck_encrypt )}, + {CKA_VERIFY, &ck_verify, sizeof( ck_verify )}, +#define COMMON_PUBLIC_ATTRIBUTES 5 // number of attributes above + {-1, NULL, 0}, + {-1, NULL, 0}, + }; + CK_ATTRIBUTE private_attributes[] = { + {CKA_CLASS, &cko_private_key, sizeof( cko_private_key )}, + {CKA_KEY_TYPE, &ck_key_type, sizeof( ck_key_type )}, + {CKA_TOKEN, &ck_token, sizeof( ck_token )}, + {CKA_DECRYPT, &ck_decrypt, sizeof( ck_decrypt )}, + {CKA_SIGN, &ck_sign, sizeof( ck_sign )}, + {CKA_SENSITIVE, &ck_sensitive, sizeof( ck_sensitive )}, + {CKA_EXTRACTABLE, &ck_extractable, sizeof( ck_extractable )}, +#define COMMON_PRIVATE_ATTRIBUTES 7 // number of attributes above + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + {-1, NULL, 0}, + }; + CK_ATTRIBUTE *public_end = public_attributes + COMMON_PUBLIC_ATTRIBUTES; + CK_ATTRIBUTE *private_end = private_attributes + COMMON_PRIVATE_ATTRIBUTES; +#undef COMMON_PUBLIC_ATTRIBUTES +#undef COMMON_PRIVATE_ATTRIBUTES + unsigned char *data = NULL; + CK_RV rv; + + if( hPublicKey != NULL ) + *hPublicKey = CK_INVALID_HANDLE; + if( hPrivateKey != NULL ) + *hPrivateKey = CK_INVALID_HANDLE; + + /* Prepare the data-dependent key attributes */ + switch( mbedtls_pk_representation_type( mbedtls_pk_get_type( ctx ) ) ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + { + const mbedtls_rsa_context *rsa = mbedtls_pk_rsa( *ctx ); + unsigned char *p; + size_t half_len = ( rsa->len + 1 ) / 2; + data = mbedtls_calloc( 1, 3 * rsa->len + 5 * half_len ); + if( data == NULL ) + { + rv = CKR_HOST_MEMORY; + goto exit; + } + p = data; + ck_key_type = CKK_RSA; + MPI_TO_CK( &rsa->N, public_end, CKA_MODULUS, &p, rsa->len ); + *private_end++ = *public_end++; + MPI_TO_CK( &rsa->E, public_end, CKA_PUBLIC_EXPONENT, &p, rsa->len ); + *private_end++ = *public_end++; + if( hPrivateKey != NULL ) + { + MPI_TO_CK( &rsa->D, private_end++, + CKA_PRIVATE_EXPONENT, &p, rsa->len ); + MPI_TO_CK( &rsa->P, private_end++, + CKA_PRIME_1, &p, half_len ); + MPI_TO_CK( &rsa->Q, private_end++, + CKA_PRIME_2, &p, half_len ); + MPI_TO_CK( &rsa->DP, private_end++, + CKA_EXPONENT_1, &p, half_len ); + MPI_TO_CK( &rsa->DQ, private_end++, + CKA_EXPONENT_2, &p, half_len ); + MPI_TO_CK( &rsa->QP, private_end++, + CKA_COEFFICIENT, &p, half_len ); + } + } + break; +#endif /* MBEDTLS_RSA_C */ + default: + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + } + + if( hPublicKey != NULL ) + { + *hPublicKey = CK_INVALID_HANDLE; + rv = C_CreateObject( hSession, + public_attributes, + public_end - public_attributes, + hPublicKey ); + if( rv != CKR_OK ) + goto exit; + } + + if( hPrivateKey != NULL ) + { + rv = C_CreateObject( hSession, + private_attributes, + private_end - private_attributes, + hPrivateKey ); + if( rv != CKR_OK ) + goto exit; + } + +exit: + if( rv != CKR_OK ) + { + /* In case an error happened, destroy any object that we + created. In case C_DestroyObject failed, we report the original + error, but *hPublicKey may contain a valid handle if + creating the private key failed and then destroying the public key + also failed (e.g. because the token disconnected). */ + if( hPublicKey != NULL && *hPublicKey != CK_INVALID_HANDLE ) + { + if( C_DestroyObject( hSession, *hPublicKey ) == CKR_OK ) + *hPublicKey = CK_INVALID_HANDLE; + } + if( hPrivateKey != NULL && *hPrivateKey != CK_INVALID_HANDLE ) + { + if( C_DestroyObject( hSession, *hPrivateKey ) == CKR_OK ) + *hPrivateKey = CK_INVALID_HANDLE; + } + } + mbedtls_free( data ); + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); +} + +#endif /* MBEDTLS_PK_C */ + + + +#endif /* MBEDTLS_PKCS11_CLIENT_C */ diff --git a/tests/.gitignore b/tests/.gitignore index 3c9b0cf25..d273db02c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -7,3 +7,5 @@ data_files/mpi_write data_files/hmac_drbg_seed data_files/ctr_drbg_seed data_files/entropy_seed + +softhsm2.d diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dc2797968..a29aceef6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -90,6 +90,7 @@ add_test_suite(mdx) add_test_suite(memory_buffer_alloc) add_test_suite(mpi) add_test_suite(pem) +add_test_suite(pkcs11_client) add_test_suite(pkcs1_v15) add_test_suite(pkcs1_v21) add_test_suite(pkcs5) diff --git a/tests/Makefile b/tests/Makefile index 4787f2508..624160d21 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -78,6 +78,7 @@ APPS = test_suite_aes.ecb$(EXEXT) test_suite_aes.cbc$(EXEXT) \ test_suite_memory_buffer_alloc$(EXEXT) \ test_suite_mpi$(EXEXT) \ test_suite_pem$(EXEXT) test_suite_pkcs1_v15$(EXEXT) \ + test_suite_pkcs11_client$(EXEXT) \ test_suite_pkcs1_v21$(EXEXT) test_suite_pkcs5$(EXEXT) \ test_suite_pkparse$(EXEXT) test_suite_pkwrite$(EXEXT) \ test_suite_pk$(EXEXT) \ @@ -377,6 +378,10 @@ test_suite_pem$(EXEXT): test_suite_pem.c $(DEP) echo " CC $<" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +test_suite_pkcs11_client$(EXEXT): test_suite_pkcs11_client.c $(DEP) + echo " CC $<" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + test_suite_pkcs1_v15$(EXEXT): test_suite_pkcs1_v15.c $(DEP) echo " CC $<" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ diff --git a/tests/scripts/pkcs11-client-test.sh b/tests/scripts/pkcs11-client-test.sh new file mode 100755 index 000000000..aaf7d9427 --- /dev/null +++ b/tests/scripts/pkcs11-client-test.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -u -e + +TOKEN_DIR=softhsm2.d + +if [ -e library/aes.c ]; then + TOPDIR="$PWD" +elif [ -e ../library/aes.c ]; then + TOPDIR="${PWD%/*}" +elif [ -e ../../library/aes.c ]; then + TOPDIR="${PWD%/*/*}" +elif [ -e ../../../library/aes.c ]; then + TOPDIR="${PWD%/*/*/*}" +else + unset TOPDIR +fi +if [ -n "${TOPDIR+1}" ] && + make -C "$TOPDIR/programs" util/syslog2stderr.so >/dev/null 2>&1 +then + case $(uname) in + Darwin) + export DYLD_PRELOAD="${DYLD_PRELOAD-}:$TOPDIR/programs/util/syslog2stderr.so";; + *) + export LD_PRELOAD="${LD_PRELOAD-}:$TOPDIR/programs/util/syslog2stderr.so";; + esac +fi + +# softhsm2_find_token LABEL +softhsm2_find_token () { + softhsm2-util --show-slots | awk -v label="$1" ' + $1 == "Slot" && $2 ~ /^[0-9]+$/ {slot = $2} + $1 == "Label:" && $2 == label {print slot; found=1; exit} + END {exit(!found)} + ' +} + +# softhsm2_create_token LABEL +softhsm2_create_token () { + softhsm2_find_token "$1" || { + softhsm2-util --init-token --free --so-pin 0000 --pin 0000 --label "$1" && + softhsm2_find_token "$1" + } +} + +softhsm2_init () { + test -d "$TOKEN_DIR" || mkdir "$TOKEN_DIR" + scratch_token=$(softhsm2_create_token "scratch") +} + +case $1 in + find_slot) softhsm2_find_token "$2";; + init) softhsm2_init;; + *) echo >&2 "$0: Unknown command: $1"; exit 120;; +esac diff --git a/tests/softhsm2.conf b/tests/softhsm2.conf new file mode 100644 index 000000000..08e94d53a --- /dev/null +++ b/tests/softhsm2.conf @@ -0,0 +1,4 @@ +objectstore.backend=file +slots.removable=false +directories.tokendir=softhsm2.d +log.level=INFO diff --git a/tests/suites/test_suite_pkcs11_client.data b/tests/suites/test_suite_pkcs11_client.data new file mode 100644 index 000000000..3f4d4cfa3 --- /dev/null +++ b/tests/suites/test_suite_pkcs11_client.data @@ -0,0 +1,7 @@ +PKCS#11 RSA import and sign +depends_on:MBEDTLS_PK_C:MBEDTLS_ECDSA_C +pk_import_sign:"data_files/server1.key" + +PKCS#11 RSA generate and sign +depends_on:MBEDTLS_PK_C:MBEDTLS_RSA_C +pk_generate_sign:MBEDTLS_PK_RSA diff --git a/tests/suites/test_suite_pkcs11_client.function b/tests/suites/test_suite_pkcs11_client.function new file mode 100644 index 000000000..9c16a3536 --- /dev/null +++ b/tests/suites/test_suite_pkcs11_client.function @@ -0,0 +1,296 @@ +/* BEGIN_HEADER */ +#include + +#include + +#include "mbedtls/pkcs11_client.h" + +#if defined(MBEDTLS_PK_C) + +#include "mbedtls/oid.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/bignum.h" +#include "mbedtls/rsa.h" +#include "mbedtls/pk.h" + +#define ARRAY_LENGTH( a ) ( sizeof( a ) / sizeof( *( a ) ) ) + +#define CK_ASSERT( expr ) \ + do { \ + CK_RV CK_ASSERT_rv = ( expr ); \ + char CK_ASSERT_msg[sizeof( #expr ) + 20] = #expr; \ + if( CK_ASSERT_rv != CKR_OK ) { \ + sprintf( CK_ASSERT_msg + strlen( CK_ASSERT_msg ), \ + " -> 0x%x", (unsigned) CK_ASSERT_rv ); \ + test_fail( CK_ASSERT_msg, __LINE__, __FILE__ ); \ + goto exit; \ + } \ + } while( 0 ) + +#define RSA_KEY_SIZE_BITS 512 +#define RSA_KEY_SIZE_BYTES ( ( RSA_KEY_SIZE_BITS + 7 ) / 8 ) +#define ECP_GROUP_ID MBEDTLS_ECP_DP_SECP256R1 +#define ECP_GROUP_NAME( id ) #id + +//static CK_BBOOL ck_false = CK_FALSE; +static CK_BBOOL ck_true = CK_TRUE; + +static int pkcs11_token_label_is( const CK_TOKEN_INFO *info, const char *label ) +{ + size_t n = strlen( label ); + if( n > sizeof( info->label ) ) + return( 0 ); + if( memcmp( info->label, label, n ) ) + return( 0 ); + for( ; n < sizeof( info->label ); n++ ) + { + if( info->label[n] != ' ' ) + return( 0 ); + } + return( 1 ); +} + +static int pkcs11_get_slot_id( const char *label, CK_SLOT_ID *slot ) +{ + CK_SLOT_ID *slots = NULL; + CK_ULONG count; + CK_ULONG i; + CK_TOKEN_INFO info; + int found = 0; + CK_ASSERT( C_GetSlotList( CK_TRUE, NULL_PTR, &count ) ); + slots = mbedtls_calloc( sizeof( *slots ), count ); + TEST_ASSERT( slots != NULL ); + CK_ASSERT( C_GetSlotList( CK_TRUE, slots, &count ) ); + for( i = 0; i < count; i++ ) + { + CK_ASSERT( C_GetTokenInfo( slots[i], &info ) ); + if( pkcs11_token_label_is( &info, label ) ) + { + *slot = slots[i]; + found = 1; + break; + } + } + if( !found ) + mbedtls_fprintf( stdout, "No token found with label %s\n", label ); +exit: + mbedtls_free( slots ); + return( found ); +} + +static CK_OBJECT_HANDLE pkcs11_init( void ) +{ + CK_SESSION_HANDLE hSession; + CK_SLOT_ID slot; + unsigned char user_pin[4] = "0000"; + CK_ASSERT( C_Initialize( NULL_PTR ) ); + TEST_ASSERT( pkcs11_get_slot_id( "scratch", &slot ) ); + CK_ASSERT( C_OpenSession( slot, + CKF_RW_SESSION | CKF_SERIAL_SESSION, + NULL_PTR, NULL_PTR, + &hSession ) ); + CK_ASSERT( C_Login( hSession, CKU_USER, user_pin, sizeof( user_pin ) ) ); + return( hSession ); +exit: + return( CK_INVALID_HANDLE ); +} + +static CK_RV pkcs11_generate_key( mbedtls_pk_type_t key_type, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE *phPublicKey, + CK_OBJECT_HANDLE *phPrivateKey ) +{ + CK_MECHANISM mechanism = {0, NULL_PTR, 0}; + CK_ATTRIBUTE public_attributes[] = { + {0, 0, 0}, + {CKA_ENCRYPT, &ck_true, sizeof( ck_true )}, + {CKA_VERIFY, &ck_true, sizeof( ck_true )}, + }; + CK_ATTRIBUTE private_attributes[] = { + {CKA_DECRYPT, &ck_true, sizeof( ck_true )}, + {CKA_SIGN, &ck_true, sizeof( ck_true )}, + }; + CK_ULONG ck_rsa_key_size = RSA_KEY_SIZE_BITS; + unsigned char ecParams[16]; + size_t ecParams_length; + + switch( key_type ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + mechanism.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + public_attributes[0].type = CKA_MODULUS_BITS; + public_attributes[0].pValue = &ck_rsa_key_size; + public_attributes[0].ulValueLen = sizeof( ck_rsa_key_size ); + break; +#endif /* MBEDTLS_RSA_C */ + default: + test_fail( "Unsupported key type in test data", __LINE__, __FILE__ ); + break; + } + + return( C_GenerateKeyPair( hSession, + &mechanism, + public_attributes, + ARRAY_LENGTH( public_attributes ), + private_attributes, + ARRAY_LENGTH( private_attributes ), + phPublicKey, phPrivateKey ) ); +exit: + /* Shouldn't happen except if there's a test error (e.g. trying to + use a curve that isn't compiled in). */ + return( -1 ); +} + + +#endif /* MBEDTLS_PK_C */ + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PKCS11_CLIENT_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_PK_C:MBEDTLS_SHA256_C */ +void pk_generate_sign( int key_type ) +{ + mbedtls_pk_context pkcs11_ctx; + mbedtls_pk_context transparent_ctx; + CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPublicKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrivateKey = CK_INVALID_HANDLE; + unsigned char hash_value[32] = "Fake hash, it doesn't matter...."; + unsigned char sig_buffer[RSA_KEY_SIZE_BYTES]; + size_t sig_length = sizeof( sig_buffer ); + + mbedtls_pk_init( &pkcs11_ctx ); + mbedtls_pk_init( &transparent_ctx ); + + /* Initialize cryptoki and generate a key in the token */ + hSession = pkcs11_init( ); + TEST_ASSERT( hSession != CK_INVALID_HANDLE ); + + CK_ASSERT( pkcs11_generate_key( key_type, + hSession, + &hPublicKey, &hPrivateKey ) ); + TEST_ASSERT( hPublicKey != CK_INVALID_HANDLE ); + TEST_ASSERT( hPrivateKey != CK_INVALID_HANDLE ); + + /* Prepare the mbed TLS contexts */ + TEST_ASSERT( mbedtls_pk_setup( &transparent_ctx, + mbedtls_pk_info_from_type( key_type ) ) == 0 ); + TEST_ASSERT( mbedtls_pk_setup_pkcs11( &pkcs11_ctx, + hSession, + hPublicKey, + hPrivateKey ) == 0 ); + + /* Retrieve the public key from the token */ + switch( key_type ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + { + unsigned char n_buffer[RSA_KEY_SIZE_BYTES]; + unsigned char e_buffer[RSA_KEY_SIZE_BYTES]; + CK_ATTRIBUTE public_attributes[] = { + {CKA_MODULUS, n_buffer, sizeof( n_buffer )}, + {CKA_PUBLIC_EXPONENT, e_buffer, sizeof( e_buffer )}, + }; + CK_ULONG *n_length = &public_attributes[0].ulValueLen; + CK_ULONG *e_length = &public_attributes[1].ulValueLen; + mbedtls_rsa_context *rsa_ctx = mbedtls_pk_rsa( transparent_ctx ); + + CK_ASSERT( C_GetAttributeValue( hSession, hPublicKey, + public_attributes, ARRAY_LENGTH( public_attributes ) ) ); + TEST_ASSERT( mbedtls_mpi_read_binary( &rsa_ctx->N, + n_buffer, *n_length ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_binary( &rsa_ctx->E, + e_buffer, *e_length ) == 0 ); + rsa_ctx->len = mbedtls_mpi_size( &rsa_ctx->N ); + } + break; +#endif /* MBEDTLS_RSA_C */ + + default: + TEST_ASSERT( !"Unsupported key type in test data" ); + break; + } + + /* Sign with the token and verify in software */ + TEST_ASSERT( mbedtls_pk_sign( &pkcs11_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, &sig_length, + NULL, NULL ) == 0 ); + TEST_ASSERT( mbedtls_pk_verify( &transparent_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, sig_length ) == 0 ); + +exit: + if( hPublicKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPublicKey ); + if( hPrivateKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPrivateKey ); + C_CloseSession( hSession ); + C_Finalize( NULL_PTR ); + mbedtls_pk_free( &pkcs11_ctx ); + mbedtls_pk_free( &transparent_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_PK_C:MBEDTLS_SHA256_C */ +void pk_import_sign( char *file ) +{ + mbedtls_pk_context pkcs11_ctx; + mbedtls_pk_context transparent_ctx; + CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPublicKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrivateKey = CK_INVALID_HANDLE; + unsigned char hash_value[32] = "Fake hash, it doesn't matter...."; + unsigned char sig_buffer[4096]; + size_t sig_length = sizeof( sig_buffer ); + + mbedtls_pk_init( &pkcs11_ctx ); + mbedtls_pk_init( &transparent_ctx ); + + /* Read a transparent key */ + TEST_ASSERT( mbedtls_pk_parse_keyfile( &transparent_ctx, file, NULL ) == 0 ); + + /* Initialize cryptoki and import the key into the token */ + hSession = pkcs11_init( ); + TEST_ASSERT( hSession != CK_INVALID_HANDLE ); + + TEST_ASSERT( mbedtls_pk_import_to_pkcs11( &transparent_ctx, + MBEDTLS_PK_FLAG_SIGN | + MBEDTLS_PK_FLAG_VERIFY, + hSession, + &hPublicKey, + &hPrivateKey ) == 0 ); + TEST_ASSERT( hPublicKey != CK_INVALID_HANDLE ); + TEST_ASSERT( hPrivateKey != CK_INVALID_HANDLE ); + TEST_ASSERT( mbedtls_pk_setup_pkcs11( &pkcs11_ctx, + hSession, + hPublicKey, + hPrivateKey ) == 0 ); + + /* Sign with the token and verify in software */ + TEST_ASSERT( sizeof( sig_buffer ) >= mbedtls_pk_signature_size( &pkcs11_ctx ) ); + TEST_ASSERT( mbedtls_pk_sign( &pkcs11_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, &sig_length, + NULL, NULL ) == 0 ); + TEST_ASSERT( mbedtls_pk_verify( &transparent_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, sig_length ) == 0 ); + +exit: + if( hPublicKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPublicKey ); + if( hPrivateKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPrivateKey ); + C_CloseSession( hSession ); + C_Finalize( NULL_PTR ); + mbedtls_pk_free( &pkcs11_ctx ); + mbedtls_pk_free( &transparent_ctx ); +} +/* END_CASE */ diff --git a/visualc/VS2010/mbedTLS.vcxproj b/visualc/VS2010/mbedTLS.vcxproj index 1a55eaaa3..a9daabed1 100644 --- a/visualc/VS2010/mbedTLS.vcxproj +++ b/visualc/VS2010/mbedTLS.vcxproj @@ -194,6 +194,7 @@ + @@ -262,6 +263,7 @@ +