/* $NetBSD: evp-pkcs11.c,v 1.3.8.1 2023/08/11 13:39:58 martin Exp $ */ /* * Copyright (c) 2015-2016, Secure Endpoints Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /* PKCS#11 provider */ #include #include #include #ifdef HAVE_DLFCN_H #include #ifndef RTLD_LAZY #define RTLD_LAZY 0 #endif #ifndef RTLD_LOCAL #define RTLD_LOCAL 0 #endif #ifndef RTLD_GROUP #define RTLD_GROUP 0 #endif #ifndef RTLD_NODELETE #define RTLD_NODELETE 0 #endif #else #error PKCS11 support requires dlfcn.h #endif #include #include #include #include #include #if __sun && !defined(PKCS11_MODULE_PATH) # ifdef _LP64 # define PKCS11_MODULE_PATH "/usr/lib/64/libpkcs11.so" # else # define PKCS11_MODULE_PATH "/usr/lib/libpkcs11.so" # endif #elif defined(__linux__) /* * XXX We should have an autoconf check for OpenCryptoki and such * things. However, there's no AC_CHECK_OBJECT(), and we'd have to * write one. Today I'm feeling lazy. Another possibility would be to * have a symlink from the libdir we'll install into, and then we could * dlopen() that on all platforms. * * XXX Also, we should pick an appropriate shared object based on 32- vs * 64-bits. */ # define PKCS11_MODULE_PATH "/usr/lib/pkcs11/PKCS11_API.so" #endif static CK_FUNCTION_LIST_PTR p11_module; static int p11_cleanup(EVP_CIPHER_CTX *ctx); struct pkcs11_cipher_ctx { CK_SESSION_HANDLE hSession; CK_OBJECT_HANDLE hSecret; }; struct pkcs11_md_ctx { CK_SESSION_HANDLE hSession; }; static void *pkcs11_module_handle; static CK_RV p11_module_load(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { CK_RV rv; CK_RV (*C_GetFunctionList_fn)(CK_FUNCTION_LIST_PTR_PTR); *ppFunctionList = NULL; if (!issuid()) { char *pkcs11ModulePath = getenv("PKCS11_MODULE_PATH"); if (pkcs11ModulePath != NULL) { pkcs11_module_handle = dlopen(pkcs11ModulePath, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP | RTLD_NODELETE); if (pkcs11_module_handle == NULL) fprintf(stderr, "p11_module_load(%s): %s\n", pkcs11ModulePath, dlerror()); } } #ifdef PKCS11_MODULE_PATH if (pkcs11_module_handle == NULL) { pkcs11_module_handle = dlopen(PKCS11_MODULE_PATH, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP | RTLD_NODELETE); if (pkcs11_module_handle == NULL) fprintf(stderr, "p11_module_load(%s): %s\n", PKCS11_MODULE_PATH, dlerror()); } #endif if (pkcs11_module_handle == NULL) return CKR_LIBRARY_LOAD_FAILED; C_GetFunctionList_fn = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(pkcs11_module_handle, "C_GetFunctionList"); if (C_GetFunctionList_fn == NULL) { dlclose(pkcs11_module_handle); return CKR_LIBRARY_LOAD_FAILED; } rv = C_GetFunctionList_fn(ppFunctionList); if (rv != CKR_OK) { dlclose(pkcs11_module_handle); return rv; } return CKR_OK; } static void p11_module_load_once(void *context) { p11_module_load((CK_FUNCTION_LIST_PTR_PTR)context); } static CK_RV p11_module_init(void) { static heim_base_once_t once = HEIM_BASE_ONCE_INIT; CK_RV rv; heim_base_once_f(&once, &p11_module, p11_module_load_once); if (p11_module == NULL) return CKR_LIBRARY_LOAD_FAILED; /* * Call C_Initialize() on every call, because it will be invalid after fork(). * Caching the initialization status using a once control and invalidating it * on fork provided no measurable performance benefit on Solaris 11. Other * approaches would not be thread-safe or would involve more intrusive code * changes, such as exposing heimbase's atomics. */ rv = p11_module->C_Initialize(NULL); if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) rv = CKR_OK; return rv; } static CK_RV p11_session_init(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE_PTR phSession, CK_FLAGS *pFlags) { CK_RV rv; CK_ULONG i, ulSlotCount = 0; CK_SLOT_ID_PTR pSlotList = NULL; CK_MECHANISM_INFO info; if (phSession != NULL) *phSession = CK_INVALID_HANDLE; *pFlags = 0; rv = p11_module_init(); if (rv != CKR_OK) goto cleanup; assert(p11_module != NULL); rv = p11_module->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount); if (rv != CKR_OK) goto cleanup; pSlotList = (CK_SLOT_ID_PTR)calloc(ulSlotCount, sizeof(CK_SLOT_ID)); if (pSlotList == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } rv = p11_module->C_GetSlotList(CK_FALSE, pSlotList, &ulSlotCount); if (rv != CKR_OK) goto cleanup; /* * Note that this approach of using the first slot that supports the desired * mechanism may not always be what the user wants (for example it may prefer * software to hardware crypto). We're going to assume that this code will be * principally used on Solaris (which has a meta-slot provider that sorts by * hardware first) or in situations where the user can configure the slots in * order of provider preference. In the future we should make this configurable. */ for (i = 0; i < ulSlotCount; i++) { rv = p11_module->C_GetMechanismInfo(pSlotList[i], mechanismType, &info); if (rv == CKR_OK) { *pFlags = info.flags; break; } } if (i == ulSlotCount) { rv = CKR_MECHANISM_INVALID; goto cleanup; } if (phSession != NULL) { rv = p11_module->C_OpenSession(pSlotList[i], CKF_SERIAL_SESSION, NULL, NULL, phSession); if (rv != CKR_OK) goto cleanup; } cleanup: free(pSlotList); return rv; } static int p11_mech_available_p(CK_MECHANISM_TYPE mechanismType, CK_FLAGS reqFlags) { CK_RV rv; CK_FLAGS flags; rv = p11_session_init(mechanismType, NULL, &flags); if (rv != CKR_OK) return 0; return (flags & reqFlags) == reqFlags; } static CK_KEY_TYPE p11_key_type_for_mech(CK_MECHANISM_TYPE mechanismType) { CK_KEY_TYPE keyType = 0; switch (mechanismType) { case CKM_RC2_CBC: keyType = CKK_RC2; break; case CKM_RC4: keyType = CKK_RC4; break; case CKM_DES_CBC: keyType = CKK_DES; break; case CKM_DES3_CBC: keyType = CKK_DES3; break; case CKM_AES_CBC: case CKM_AES_CFB8: keyType = CKK_AES; break; case CKM_CAMELLIA_CBC: keyType = CKK_CAMELLIA; break; default: assert(0 && "Unknown PKCS#11 mechanism type"); break; } return keyType; } static int p11_key_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int encp) { CK_RV rv; CK_BBOOL bFalse = CK_FALSE; CK_BBOOL bTrue = CK_TRUE; CK_MECHANISM_TYPE mechanismType = (CK_MECHANISM_TYPE)ctx->cipher->app_data; CK_KEY_TYPE keyType = p11_key_type_for_mech(mechanismType); CK_OBJECT_CLASS objectClass = CKO_SECRET_KEY; CK_ATTRIBUTE_TYPE op = encp ? CKA_ENCRYPT : CKA_DECRYPT; CK_ATTRIBUTE attributes[] = { { CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) }, { CKA_CLASS, &objectClass, sizeof(objectClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_TOKEN, &bFalse, sizeof(bFalse) }, { CKA_PRIVATE, &bFalse, sizeof(bFalse) }, { CKA_SENSITIVE, &bTrue, sizeof(bTrue) }, { CKA_VALUE, (void *)key, ctx->key_len }, { op, &bTrue, sizeof(bTrue) } }; CK_MECHANISM mechanism = { mechanismType, ctx->cipher->iv_len ? ctx->iv : NULL, ctx->cipher->iv_len }; struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data; CK_FLAGS flags; rv = CKR_OK; if (p11ctx->hSession != CK_INVALID_HANDLE && key != NULL) p11_cleanup(ctx); /* refresh session with new key */ if (p11ctx->hSession == CK_INVALID_HANDLE) { rv = p11_session_init(mechanismType, &p11ctx->hSession, &flags); if (rv != CKR_OK) goto cleanup; if ((flags & (CKF_ENCRYPT|CKF_DECRYPT)) != (CKF_ENCRYPT|CKF_DECRYPT)) { rv = CKR_MECHANISM_INVALID; goto cleanup; } } if (key != NULL) { assert(p11_module != NULL); assert(p11ctx->hSecret == CK_INVALID_HANDLE); rv = p11_module->C_CreateObject(p11ctx->hSession, attributes, sizeof(attributes) / sizeof(attributes[0]), &p11ctx->hSecret); if (rv != CKR_OK) goto cleanup; } if (p11ctx->hSecret != CK_INVALID_HANDLE) { if (op == CKA_ENCRYPT) rv = p11_module->C_EncryptInit(p11ctx->hSession, &mechanism, p11ctx->hSecret); else rv = p11_module->C_DecryptInit(p11ctx->hSession, &mechanism, p11ctx->hSecret); if (rv != CKR_OK) goto cleanup; } cleanup: if (rv != CKR_OK) p11_cleanup(ctx); return rv == CKR_OK; } static int p11_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, unsigned int size) { struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data; CK_RV rv; CK_ULONG ulCipherTextLen = size; assert(p11_module != NULL); assert(EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_STREAM_CIPHER || (size % ctx->cipher->block_size) == 0); if (ctx->encrypt) rv = p11_module->C_EncryptUpdate(p11ctx->hSession, (unsigned char *)in, size, out, &ulCipherTextLen); else rv = p11_module->C_DecryptUpdate(p11ctx->hSession, (unsigned char *)in, size, out, &ulCipherTextLen); return rv == CKR_OK; } static int p11_cleanup(EVP_CIPHER_CTX *ctx) { struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data; if (p11ctx->hSecret != CK_INVALID_HANDLE) { p11_module->C_DestroyObject(p11ctx->hSession, p11ctx->hSecret); p11ctx->hSecret = CK_INVALID_HANDLE; } if (p11ctx->hSession != CK_INVALID_HANDLE) { p11_module->C_CloseSession(p11ctx->hSession); p11ctx->hSession = CK_INVALID_HANDLE; } return 1; } static int p11_md_cleanup(EVP_MD_CTX *ctx); static int p11_md_hash_init(CK_MECHANISM_TYPE mechanismType, EVP_MD_CTX *ctx) { struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx; CK_RV rv; CK_FLAGS flags; CK_MECHANISM mechanism = { mechanismType, NULL, 0 }; if (p11ctx->hSession != CK_INVALID_HANDLE) p11_md_cleanup(ctx); rv = p11_session_init(mechanismType, &p11ctx->hSession, &flags); if (rv != CKR_OK) goto cleanup; if ((flags & CKF_DIGEST) != CKF_DIGEST) { rv = CKR_MECHANISM_INVALID; goto cleanup; } assert(p11_module != NULL); rv = p11_module->C_DigestInit(p11ctx->hSession, &mechanism); cleanup: return rv == CKR_OK; } static int p11_md_update(EVP_MD_CTX *ctx, const void *data, size_t length) { struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx; CK_RV rv; assert(p11_module != NULL); assert(data != NULL || length == 0); rv = p11_module->C_DigestUpdate(p11ctx->hSession, data ? (CK_BYTE_PTR)data : (CK_BYTE_PTR)"", length); return rv == CKR_OK; } static int p11_md_final(void *digest, EVP_MD_CTX *ctx) { struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx; CK_RV rv; CK_ULONG digestLen = 0; assert(p11_module != NULL); rv = p11_module->C_DigestFinal(p11ctx->hSession, NULL, &digestLen); if (rv == CKR_OK) rv = p11_module->C_DigestFinal(p11ctx->hSession, digest, &digestLen); return rv == CKR_OK; } static int p11_md_cleanup(EVP_MD_CTX *ctx) { struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx; CK_RV rv; assert(p11_module != NULL); rv = p11_module->C_CloseSession(p11ctx->hSession); if (rv == CKR_OK) p11ctx->hSession = CK_INVALID_HANDLE; return rv == CKR_OK; } #define PKCS11_CIPHER_ALGORITHM(name, mechanismType, block_size, \ key_len, iv_len, flags) \ \ static EVP_CIPHER \ pkcs11_##name = { \ 0, \ block_size, \ key_len, \ iv_len, \ (flags) | EVP_CIPH_ALWAYS_CALL_INIT, \ p11_key_init, \ p11_do_cipher, \ p11_cleanup, \ sizeof(struct pkcs11_cipher_ctx), \ NULL, \ NULL, \ NULL, \ (void *)mechanismType \ }; \ \ const EVP_CIPHER * \ hc_EVP_pkcs11_##name(void) \ { \ if (p11_mech_available_p(mechanismType, CKF_ENCRYPT|CKF_DECRYPT)) \ return &pkcs11_##name; \ else \ return NULL; \ } \ \ static void \ pkcs11_hcrypto_##name##_init_once(void *context) \ { \ const EVP_CIPHER *cipher; \ \ cipher = hc_EVP_pkcs11_ ##name(); \ if (cipher == NULL && HCRYPTO_FALLBACK) \ cipher = hc_EVP_hcrypto_ ##name(); \ \ *((const EVP_CIPHER **)context) = cipher; \ } \ \ const EVP_CIPHER * \ hc_EVP_pkcs11_hcrypto_##name(void) \ { \ static const EVP_CIPHER *__cipher; \ static heim_base_once_t __init = HEIM_BASE_ONCE_INIT; \ \ heim_base_once_f(&__init, &__cipher, \ pkcs11_hcrypto_##name##_init_once); \ \ return __cipher; \ } #define PKCS11_MD_ALGORITHM(name, mechanismType, hash_size, block_size) \ \ static int p11_##name##_init(EVP_MD_CTX *ctx) \ { \ return p11_md_hash_init(mechanismType, ctx); \ } \ \ const EVP_MD * \ hc_EVP_pkcs11_##name(void) \ { \ static struct hc_evp_md name = { \ hash_size, \ block_size, \ sizeof(struct pkcs11_md_ctx), \ p11_##name##_init, \ p11_md_update, \ p11_md_final, \ p11_md_cleanup \ }; \ \ if (p11_mech_available_p(mechanismType, CKF_DIGEST)) \ return &name; \ else \ return NULL; \ } \ \ static void \ pkcs11_hcrypto_##name##_init_once(void *context) \ { \ const EVP_MD *md; \ \ md = hc_EVP_pkcs11_ ##name(); \ if (md == NULL && HCRYPTO_FALLBACK) \ md = hc_EVP_hcrypto_ ##name(); \ \ *((const EVP_MD **)context) = md; \ } \ \ const EVP_MD * \ hc_EVP_pkcs11_hcrypto_##name(void) \ { \ static const EVP_MD *__md; \ static heim_base_once_t __init = HEIM_BASE_ONCE_INIT; \ \ heim_base_once_f(&__init, &__md, \ pkcs11_hcrypto_##name##_init_once); \ \ return __md; \ } #define PKCS11_MD_ALGORITHM_UNAVAILABLE(name) \ \ const EVP_MD * \ hc_EVP_pkcs11_##name(void) \ { \ return NULL; \ } \ \ const EVP_MD * \ hc_EVP_pkcs11_hcrypto_##name(void) \ { \ return hc_EVP_hcrypto_ ##name(); \ } /** * The triple DES cipher type (PKCS#11 provider) * * @return the DES-EDE3-CBC EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(des_ede3_cbc, CKM_DES3_CBC, 8, 24, 8, EVP_CIPH_CBC_MODE) /** * The DES cipher type (PKCS#11 provider) * * @return the DES-CBC EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(des_cbc, CKM_DES_CBC, 8, 8, 8, EVP_CIPH_CBC_MODE) /** * The AES-128 cipher type (PKCS#11 provider) * * @return the AES-128-CBC EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_128_cbc, CKM_AES_CBC, 16, 16, 16, EVP_CIPH_CBC_MODE) /** * The AES-192 cipher type (PKCS#11 provider) * * @return the AES-192-CBC EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_192_cbc, CKM_AES_CBC, 16, 24, 16, EVP_CIPH_CBC_MODE) /** * The AES-256 cipher type (PKCS#11 provider) * * @return the AES-256-CBC EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_256_cbc, CKM_AES_CBC, 16, 32, 16, EVP_CIPH_CBC_MODE) /** * The AES-128 CFB8 cipher type (PKCS#11 provider) * * @return the AES-128-CFB8 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_128_cfb8, CKM_AES_CFB8, 16, 16, 16, EVP_CIPH_CFB8_MODE) /** * The AES-192 CFB8 cipher type (PKCS#11 provider) * * @return the AES-192-CFB8 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_192_cfb8, CKM_AES_CFB8, 16, 24, 16, EVP_CIPH_CFB8_MODE) /** * The AES-256 CFB8 cipher type (PKCS#11 provider) * * @return the AES-256-CFB8 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(aes_256_cfb8, CKM_AES_CFB8, 16, 32, 16, EVP_CIPH_CFB8_MODE) /** * The RC2 cipher type - PKCS#11 * * @return the RC2 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(rc2_cbc, CKM_RC2_CBC, 8, 16, 8, EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH) /** * The RC2-40 cipher type - PKCS#11 * * @return the RC2-40 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(rc2_40_cbc, CKM_RC2_CBC, 8, 5, 8, EVP_CIPH_CBC_MODE) /** * The RC2-64 cipher type - PKCS#11 * * @return the RC2-64 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(rc2_64_cbc, CKM_RC2_CBC, 8, 8, 8, EVP_CIPH_CBC_MODE) /** * The Camellia-128 cipher type - PKCS#11 * * @return the Camellia-128 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(camellia_128_cbc, CKM_CAMELLIA_CBC, 16, 16, 16, EVP_CIPH_CBC_MODE) /** * The Camellia-198 cipher type - PKCS#11 * * @return the Camellia-198 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(camellia_192_cbc, CKM_CAMELLIA_CBC, 16, 24, 16, EVP_CIPH_CBC_MODE) /** * The Camellia-256 cipher type - PKCS#11 * * @return the Camellia-256 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(camellia_256_cbc, CKM_CAMELLIA_CBC, 16, 32, 16, EVP_CIPH_CBC_MODE) /** * The RC4 cipher type (PKCS#11 provider) * * @return the RC4 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(rc4, CKM_RC4, 1, 16, 0, EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH) /** * The RC4-40 cipher type (PKCS#11 provider) * * @return the RC4 EVP_CIPHER pointer. * * @ingroup hcrypto_evp */ PKCS11_CIPHER_ALGORITHM(rc4_40, CKM_RC4, 1, 5, 0, EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH) #ifdef CKM_MD4 /* non-standard extension */ PKCS11_MD_ALGORITHM(md4, CKM_MD4, 16, 64) #else PKCS11_MD_ALGORITHM_UNAVAILABLE(md4) #endif PKCS11_MD_ALGORITHM(md5, CKM_MD5, 16, 64) PKCS11_MD_ALGORITHM(sha1, CKM_SHA_1, 20, 64) PKCS11_MD_ALGORITHM(sha256, CKM_SHA256, 32, 64) PKCS11_MD_ALGORITHM(sha384, CKM_SHA384, 48, 128) PKCS11_MD_ALGORITHM(sha512, CKM_SHA512, 64, 128)