/* $NetBSD: pkcs11rsa_link.c,v 1.1 2024/02/18 20:57:33 christos Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #if USE_PKCS11 #include #include #include #include #include #include #include #include #include #include "dst_internal.h" #include "dst_parse.h" #include "dst_pkcs11.h" /* * Limit the size of public exponents. */ #ifndef RSA_MAX_PUBEXP_BITS #define RSA_MAX_PUBEXP_BITS 35 #endif /* ifndef RSA_MAX_PUBEXP_BITS */ #define DST_RET(a) \ { \ ret = a; \ goto err; \ } static CK_BBOOL truevalue = TRUE; static CK_BBOOL falsevalue = FALSE; static void pkcs11rsa_destroy(dst_key_t *key); #ifndef PK11_RSA_PKCS_REPLACE static isc_result_t pkcs11rsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { 0, NULL, 0 }; CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_SENSITIVE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_SIGN, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_MODULUS, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, { CKA_PRIVATE_EXPONENT, NULL, 0 }, { CKA_PRIME_1, NULL, 0 }, { CKA_PRIME_2, NULL, 0 }, { CKA_EXPONENT_1, NULL, 0 }, { CKA_EXPONENT_2, NULL, 0 }, { CKA_COEFFICIENT, NULL, 0 } }; CK_ATTRIBUTE *attr; CK_SLOT_ID slotid; pk11_object_t *rsa; pk11_context_t *pk11_ctx; isc_result_t ret; unsigned int i; REQUIRE(key->key_alg == DST_ALG_RSASHA1 || key->key_alg == DST_ALG_NSEC3RSASHA1 || key->key_alg == DST_ALG_RSASHA256 || key->key_alg == DST_ALG_RSASHA512); /* * Reject incorrect RSA key lengths. */ switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: /* From RFC 3110 */ if (dctx->key->key_size > 4096) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA256: /* From RFC 5702 */ if ((dctx->key->key_size < 512) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA512: /* From RFC 5702 */ if ((dctx->key->key_size < 1024) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; default: UNREACHABLE(); } rsa = key->keydata.pkey; pk11_ctx = isc_mem_get(dctx->mctx, sizeof(*pk11_ctx)); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); if (rsa->ontoken) { slotid = rsa->slot; } else { slotid = pk11_get_best_token(OP_RSA); } ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, rsa->reqlogon, NULL, slotid); if (ret != ISC_R_SUCCESS) { goto err; } if (rsa->ontoken && (rsa->object != CK_INVALID_HANDLE)) { pk11_ctx->ontoken = rsa->ontoken; pk11_ctx->object = rsa->object; goto token_key; } for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { switch (attr->type) { case CKA_MODULUS: INSIST(keyTemplate[6].type == attr->type); keyTemplate[6].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen); keyTemplate[6].ulValueLen = attr->ulValueLen; break; case CKA_PUBLIC_EXPONENT: INSIST(keyTemplate[7].type == attr->type); keyTemplate[7].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[7].pValue, attr->pValue, attr->ulValueLen); keyTemplate[7].ulValueLen = attr->ulValueLen; break; case CKA_PRIVATE_EXPONENT: INSIST(keyTemplate[8].type == attr->type); keyTemplate[8].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[8].pValue, attr->pValue, attr->ulValueLen); keyTemplate[8].ulValueLen = attr->ulValueLen; break; case CKA_PRIME_1: INSIST(keyTemplate[9].type == attr->type); keyTemplate[9].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[9].pValue, attr->pValue, attr->ulValueLen); keyTemplate[9].ulValueLen = attr->ulValueLen; break; case CKA_PRIME_2: INSIST(keyTemplate[10].type == attr->type); keyTemplate[10].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[10].pValue, attr->pValue, attr->ulValueLen); keyTemplate[10].ulValueLen = attr->ulValueLen; break; case CKA_EXPONENT_1: INSIST(keyTemplate[11].type == attr->type); keyTemplate[11].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[11].pValue, attr->pValue, attr->ulValueLen); keyTemplate[11].ulValueLen = attr->ulValueLen; break; case CKA_EXPONENT_2: INSIST(keyTemplate[12].type == attr->type); keyTemplate[12].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[12].pValue, attr->pValue, attr->ulValueLen); keyTemplate[12].ulValueLen = attr->ulValueLen; break; case CKA_COEFFICIENT: INSIST(keyTemplate[13].type == attr->type); keyTemplate[13].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[13].pValue, attr->pValue, attr->ulValueLen); keyTemplate[13].ulValueLen = attr->ulValueLen; break; } } pk11_ctx->object = CK_INVALID_HANDLE; pk11_ctx->ontoken = false; PK11_RET(pkcs_C_CreateObject, (pk11_ctx->session, keyTemplate, (CK_ULONG)14, &pk11_ctx->object), ISC_R_FAILURE); token_key: switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: mech.mechanism = CKM_SHA1_RSA_PKCS; break; case DST_ALG_RSASHA256: mech.mechanism = CKM_SHA256_RSA_PKCS; break; case DST_ALG_RSASHA512: mech.mechanism = CKM_SHA512_RSA_PKCS; break; default: UNREACHABLE(); } PK11_RET(pkcs_C_SignInit, (pk11_ctx->session, &mech, pk11_ctx->object), ISC_R_FAILURE); dctx->ctxdata.pk11_ctx = pk11_ctx; for (i = 6; i <= 13; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } return (ISC_R_SUCCESS); err: if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE)) { (void)pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object); } for (i = 6; i <= 13; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static isc_result_t pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { 0, NULL, 0 }; CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_VERIFY, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_MODULUS, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, }; CK_ATTRIBUTE *attr; pk11_object_t *rsa; pk11_context_t *pk11_ctx; isc_result_t ret; unsigned int i; REQUIRE(key->key_alg == DST_ALG_RSASHA1 || key->key_alg == DST_ALG_NSEC3RSASHA1 || key->key_alg == DST_ALG_RSASHA256 || key->key_alg == DST_ALG_RSASHA512); REQUIRE(maxbits <= RSA_MAX_PUBEXP_BITS); /* * Reject incorrect RSA key lengths. */ switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: /* From RFC 3110 */ if (dctx->key->key_size > 4096) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA256: /* From RFC 5702 */ if ((dctx->key->key_size < 512) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA512: /* From RFC 5702 */ if ((dctx->key->key_size < 1024) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; default: UNREACHABLE(); } rsa = key->keydata.pkey; pk11_ctx = isc_mem_get(dctx->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, rsa->reqlogon, NULL, pk11_get_best_token(OP_RSA)); if (ret != ISC_R_SUCCESS) { goto err; } for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { unsigned int bits; switch (attr->type) { case CKA_MODULUS: INSIST(keyTemplate[5].type == attr->type); keyTemplate[5].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[5].pValue, attr->pValue, attr->ulValueLen); keyTemplate[5].ulValueLen = attr->ulValueLen; break; case CKA_PUBLIC_EXPONENT: INSIST(keyTemplate[6].type == attr->type); keyTemplate[6].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen); keyTemplate[6].ulValueLen = attr->ulValueLen; ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS || (bits > maxbits && maxbits != 0)) { DST_RET(DST_R_VERIFYFAILURE); } break; } } pk11_ctx->object = CK_INVALID_HANDLE; pk11_ctx->ontoken = false; PK11_RET(pkcs_C_CreateObject, (pk11_ctx->session, keyTemplate, (CK_ULONG)7, &pk11_ctx->object), ISC_R_FAILURE); switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: mech.mechanism = CKM_SHA1_RSA_PKCS; break; case DST_ALG_RSASHA256: mech.mechanism = CKM_SHA256_RSA_PKCS; break; case DST_ALG_RSASHA512: mech.mechanism = CKM_SHA512_RSA_PKCS; break; default: UNREACHABLE(); } PK11_RET(pkcs_C_VerifyInit, (pk11_ctx->session, &mech, pk11_ctx->object), ISC_R_FAILURE); dctx->ctxdata.pk11_ctx = pk11_ctx; for (i = 5; i <= 6; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } return (ISC_R_SUCCESS); err: if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE)) { (void)pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object); } for (i = 5; i <= 6; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static isc_result_t pkcs11rsa_createctx(dst_key_t *key, dst_context_t *dctx) { if (dctx->use == DO_SIGN) { return (pkcs11rsa_createctx_sign(key, dctx)); } else { return (pkcs11rsa_createctx_verify(key, 0U, dctx)); } } static isc_result_t pkcs11rsa_createctx2(dst_key_t *key, int maxbits, dst_context_t *dctx) { if (dctx->use == DO_SIGN) { return (pkcs11rsa_createctx_sign(key, dctx)); } else { return (pkcs11rsa_createctx_verify(key, (unsigned)maxbits, dctx)); } } static void pkcs11rsa_destroyctx(dst_context_t *dctx) { pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; if (pk11_ctx != NULL) { if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE)) { (void)pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object); } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; } } static isc_result_t pkcs11rsa_adddata(dst_context_t *dctx, const isc_region_t *data) { CK_RV rv; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; if (dctx->use == DO_SIGN) { PK11_CALL(pkcs_C_SignUpdate, (pk11_ctx->session, (CK_BYTE_PTR)data->base, (CK_ULONG)data->length), ISC_R_FAILURE); } else { PK11_CALL(pkcs_C_VerifyUpdate, (pk11_ctx->session, (CK_BYTE_PTR)data->base, (CK_ULONG)data->length), ISC_R_FAILURE); } return (ret); } static isc_result_t pkcs11rsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { CK_RV rv; CK_ULONG siglen = 0; isc_region_t r; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; PK11_RET(pkcs_C_SignFinal, (pk11_ctx->session, NULL, &siglen), DST_R_SIGNFAILURE); isc_buffer_availableregion(sig, &r); if (r.length < (unsigned int)siglen) { return (ISC_R_NOSPACE); } PK11_RET(pkcs_C_SignFinal, (pk11_ctx->session, (CK_BYTE_PTR)r.base, &siglen), DST_R_SIGNFAILURE); isc_buffer_add(sig, (unsigned int)siglen); err: return (ret); } static isc_result_t pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) { CK_RV rv; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; PK11_CALL(pkcs_C_VerifyFinal, (pk11_ctx->session, (CK_BYTE_PTR)sig->base, (CK_ULONG)sig->length), DST_R_VERIFYFAILURE); return (ret); } #else /* ifndef PK11_RSA_PKCS_REPLACE */ /* * CKM__RSA_PKCS mechanisms are not available so fall back * to CKM_RSA_PKCS and do the EMSA-PKCS#1-v1.5 encapsulation by hand. */ CK_BYTE md5_der[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; CK_BYTE sha1_der[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; CK_BYTE sha256_der[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; CK_BYTE sha512_der[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; #define MAX_DER_SIZE 19 #define MIN_PKCS1_PADLEN 11 static isc_result_t pkcs11rsa_createctx(dst_key_t *key, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { 0, NULL, 0 }; CK_SLOT_ID slotid; pk11_object_t *rsa = key->keydata.pkey; pk11_context_t *pk11_ctx; isc_result_t ret; REQUIRE(key->key_alg == DST_ALG_RSASHA1 || key->key_alg == DST_ALG_NSEC3RSASHA1 || key->key_alg == DST_ALG_RSASHA256 || key->key_alg == DST_ALG_RSASHA512); REQUIRE(rsa != NULL); /* * Reject incorrect RSA key lengths. */ switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: /* From RFC 3110 */ if (dctx->key->key_size > 4096) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA256: /* From RFC 5702 */ if ((dctx->key->key_size < 512) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA512: /* From RFC 5702 */ if ((dctx->key->key_size < 1024) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; default: UNREACHABLE(); } switch (key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: mech.mechanism = CKM_SHA_1; break; case DST_ALG_RSASHA256: mech.mechanism = CKM_SHA256; break; case DST_ALG_RSASHA512: mech.mechanism = CKM_SHA512; break; default: UNREACHABLE(); } pk11_ctx = isc_mem_get(dctx->mctx, sizeof(*pk11_ctx)); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); if (rsa->ontoken) { slotid = rsa->slot; } else { slotid = pk11_get_best_token(OP_RSA); } ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, rsa->reqlogon, NULL, slotid); if (ret != ISC_R_SUCCESS) { goto err; } PK11_RET(pkcs_C_DigestInit, (pk11_ctx->session, &mech), ISC_R_FAILURE); dctx->ctxdata.pk11_ctx = pk11_ctx; return (ISC_R_SUCCESS); err: pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static void pkcs11rsa_destroyctx(dst_context_t *dctx) { CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH]; CK_ULONG len = ISC_SHA512_DIGESTLENGTH; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; if (pk11_ctx != NULL) { (void)pkcs_C_DigestFinal(pk11_ctx->session, garbage, &len); isc_safe_memwipe(garbage, sizeof(garbage)); pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; } } static isc_result_t pkcs11rsa_adddata(dst_context_t *dctx, const isc_region_t *data) { CK_RV rv; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; PK11_CALL(pkcs_C_DigestUpdate, (pk11_ctx->session, (CK_BYTE_PTR)data->base, (CK_ULONG)data->length), ISC_R_FAILURE); return (ret); } static isc_result_t pkcs11rsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { CK_RV rv; CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 }; CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_SENSITIVE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_SIGN, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_MODULUS, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, { CKA_PRIVATE_EXPONENT, NULL, 0 }, { CKA_PRIME_1, NULL, 0 }, { CKA_PRIME_2, NULL, 0 }, { CKA_EXPONENT_1, NULL, 0 }, { CKA_EXPONENT_2, NULL, 0 }, { CKA_COEFFICIENT, NULL, 0 } }; CK_ATTRIBUTE *attr; CK_BYTE digest[MAX_DER_SIZE + ISC_SHA512_DIGESTLENGTH]; CK_BYTE *der; CK_ULONG derlen; CK_ULONG hashlen; CK_ULONG dgstlen; CK_ULONG siglen = 0; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; dst_key_t *key = dctx->key; pk11_object_t *rsa = key->keydata.pkey; isc_region_t r; isc_result_t ret = ISC_R_SUCCESS; unsigned int i; REQUIRE(key->key_alg == DST_ALG_RSASHA1 || key->key_alg == DST_ALG_NSEC3RSASHA1 || key->key_alg == DST_ALG_RSASHA256 || key->key_alg == DST_ALG_RSASHA512); REQUIRE(rsa != NULL); /* * Reject incorrect RSA key lengths. */ switch (dctx->key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: /* From RFC 3110 */ if (dctx->key->key_size > 4096) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA256: /* From RFC 5702 */ if ((dctx->key->key_size < 512) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA512: /* From RFC 5702 */ if ((dctx->key->key_size < 1024) || (dctx->key->key_size > 4096)) { return (ISC_R_FAILURE); } break; default: UNREACHABLE(); } switch (key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: der = sha1_der; derlen = sizeof(sha1_der); hashlen = ISC_SHA1_DIGESTLENGTH; break; case DST_ALG_RSASHA256: der = sha256_der; derlen = sizeof(sha256_der); hashlen = ISC_SHA256_DIGESTLENGTH; break; case DST_ALG_RSASHA512: der = sha512_der; derlen = sizeof(sha512_der); hashlen = ISC_SHA512_DIGESTLENGTH; break; default: UNREACHABLE(); } dgstlen = derlen + hashlen; INSIST(dgstlen <= sizeof(digest)); memmove(digest, der, derlen); PK11_RET(pkcs_C_DigestFinal, (pk11_ctx->session, digest + derlen, &hashlen), DST_R_SIGNFAILURE); isc_buffer_availableregion(sig, &r); if (r.length < (unsigned int)dgstlen + MIN_PKCS1_PADLEN) { return (ISC_R_NOSPACE); } if (rsa->ontoken && (rsa->object != CK_INVALID_HANDLE)) { pk11_ctx->ontoken = rsa->ontoken; pk11_ctx->object = rsa->object; goto token_key; } for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { switch (attr->type) { case CKA_MODULUS: INSIST(keyTemplate[6].type == attr->type); keyTemplate[6].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen); keyTemplate[6].ulValueLen = attr->ulValueLen; break; case CKA_PUBLIC_EXPONENT: INSIST(keyTemplate[7].type == attr->type); keyTemplate[7].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[7].pValue, attr->pValue, attr->ulValueLen); keyTemplate[7].ulValueLen = attr->ulValueLen; break; case CKA_PRIVATE_EXPONENT: INSIST(keyTemplate[8].type == attr->type); keyTemplate[8].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[8].pValue, attr->pValue, attr->ulValueLen); keyTemplate[8].ulValueLen = attr->ulValueLen; break; case CKA_PRIME_1: INSIST(keyTemplate[9].type == attr->type); keyTemplate[9].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[9].pValue, attr->pValue, attr->ulValueLen); keyTemplate[9].ulValueLen = attr->ulValueLen; break; case CKA_PRIME_2: INSIST(keyTemplate[10].type == attr->type); keyTemplate[10].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[10].pValue, attr->pValue, attr->ulValueLen); keyTemplate[10].ulValueLen = attr->ulValueLen; break; case CKA_EXPONENT_1: INSIST(keyTemplate[11].type == attr->type); keyTemplate[11].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[11].pValue, attr->pValue, attr->ulValueLen); keyTemplate[11].ulValueLen = attr->ulValueLen; break; case CKA_EXPONENT_2: INSIST(keyTemplate[12].type == attr->type); keyTemplate[12].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[12].pValue, attr->pValue, attr->ulValueLen); keyTemplate[12].ulValueLen = attr->ulValueLen; break; case CKA_COEFFICIENT: INSIST(keyTemplate[13].type == attr->type); keyTemplate[13].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[13].pValue, attr->pValue, attr->ulValueLen); keyTemplate[13].ulValueLen = attr->ulValueLen; break; } } pk11_ctx->object = CK_INVALID_HANDLE; pk11_ctx->ontoken = false; PK11_RET(pkcs_C_CreateObject, (pk11_ctx->session, keyTemplate, (CK_ULONG)14, &hKey), ISC_R_FAILURE); token_key: PK11_RET(pkcs_C_SignInit, (pk11_ctx->session, &mech, pk11_ctx->ontoken ? pk11_ctx->object : hKey), ISC_R_FAILURE); PK11_RET(pkcs_C_Sign, (pk11_ctx->session, digest, dgstlen, NULL, &siglen), DST_R_SIGNFAILURE); if (r.length < (unsigned int)siglen) { return (ISC_R_NOSPACE); } PK11_RET(pkcs_C_Sign, (pk11_ctx->session, digest, dgstlen, (CK_BYTE_PTR)r.base, &siglen), DST_R_SIGNFAILURE); isc_buffer_add(sig, (unsigned int)siglen); err: if (hKey != CK_INVALID_HANDLE) { (void)pkcs_C_DestroyObject(pk11_ctx->session, hKey); } for (i = 6; i <= 13; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; return (ret); } static isc_result_t pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) { CK_RV rv; CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 }; CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_VERIFY, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_MODULUS, NULL, 0 }, { CKA_PUBLIC_EXPONENT, NULL, 0 }, }; CK_ATTRIBUTE *attr; CK_BYTE digest[MAX_DER_SIZE + ISC_SHA512_DIGESTLENGTH]; CK_BYTE *der; CK_ULONG derlen; CK_ULONG hashlen; CK_ULONG dgstlen; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; dst_key_t *key = dctx->key; pk11_object_t *rsa = key->keydata.pkey; isc_result_t ret = ISC_R_SUCCESS; unsigned int i; REQUIRE(key->key_alg == DST_ALG_RSASHA1 || key->key_alg == DST_ALG_NSEC3RSASHA1 || key->key_alg == DST_ALG_RSASHA256 || key->key_alg == DST_ALG_RSASHA512); REQUIRE(rsa != NULL); switch (key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: der = sha1_der; derlen = sizeof(sha1_der); hashlen = ISC_SHA1_DIGESTLENGTH; break; case DST_ALG_RSASHA256: der = sha256_der; derlen = sizeof(sha256_der); hashlen = ISC_SHA256_DIGESTLENGTH; break; case DST_ALG_RSASHA512: der = sha512_der; derlen = sizeof(sha512_der); hashlen = ISC_SHA512_DIGESTLENGTH; break; default: UNREACHABLE(); } dgstlen = derlen + hashlen; INSIST(dgstlen <= sizeof(digest)); memmove(digest, der, derlen); PK11_RET(pkcs_C_DigestFinal, (pk11_ctx->session, digest + derlen, &hashlen), DST_R_SIGNFAILURE); for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { unsigned int bits; switch (attr->type) { case CKA_MODULUS: INSIST(keyTemplate[5].type == attr->type); keyTemplate[5].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[5].pValue, attr->pValue, attr->ulValueLen); keyTemplate[5].ulValueLen = attr->ulValueLen; break; case CKA_PUBLIC_EXPONENT: INSIST(keyTemplate[6].type == attr->type); keyTemplate[6].pValue = isc_mem_get(dctx->mctx, attr->ulValueLen); memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen); keyTemplate[6].ulValueLen = attr->ulValueLen; ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS || bits > RSA_MAX_PUBEXP_BITS) { DST_RET(DST_R_VERIFYFAILURE); } break; } } pk11_ctx->object = CK_INVALID_HANDLE; pk11_ctx->ontoken = false; PK11_RET(pkcs_C_CreateObject, (pk11_ctx->session, keyTemplate, (CK_ULONG)7, &hKey), ISC_R_FAILURE); PK11_RET(pkcs_C_VerifyInit, (pk11_ctx->session, &mech, hKey), ISC_R_FAILURE); PK11_RET(pkcs_C_Verify, (pk11_ctx->session, digest, dgstlen, (CK_BYTE_PTR)sig->base, (CK_ULONG)sig->length), DST_R_VERIFYFAILURE); err: if (hKey != CK_INVALID_HANDLE) { (void)pkcs_C_DestroyObject(pk11_ctx->session, hKey); } for (i = 5; i <= 6; i++) { if (keyTemplate[i].pValue != NULL) { { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; return (ret); } #endif /* ifndef PK11_RSA_PKCS_REPLACE */ static bool pkcs11rsa_compare(const dst_key_t *key1, const dst_key_t *key2) { pk11_object_t *rsa1, *rsa2; CK_ATTRIBUTE *attr1, *attr2; rsa1 = key1->keydata.pkey; rsa2 = key2->keydata.pkey; if ((rsa1 == NULL) && (rsa2 == NULL)) { return (true); } else if ((rsa1 == NULL) || (rsa2 == NULL)) { return (false); } attr1 = pk11_attribute_bytype(rsa1, CKA_MODULUS); attr2 = pk11_attribute_bytype(rsa2, CKA_MODULUS); if ((attr1 == NULL) && (attr2 == NULL)) { return (true); } else if ((attr1 == NULL) || (attr2 == NULL) || (attr1->ulValueLen != attr2->ulValueLen) || !isc_safe_memequal(attr1->pValue, attr2->pValue, attr1->ulValueLen)) { return (false); } attr1 = pk11_attribute_bytype(rsa1, CKA_PUBLIC_EXPONENT); attr2 = pk11_attribute_bytype(rsa2, CKA_PUBLIC_EXPONENT); if ((attr1 == NULL) && (attr2 == NULL)) { return (true); } else if ((attr1 == NULL) || (attr2 == NULL) || (attr1->ulValueLen != attr2->ulValueLen) || !isc_safe_memequal(attr1->pValue, attr2->pValue, attr1->ulValueLen)) { return (false); } attr1 = pk11_attribute_bytype(rsa1, CKA_PRIVATE_EXPONENT); attr2 = pk11_attribute_bytype(rsa2, CKA_PRIVATE_EXPONENT); if (((attr1 != NULL) || (attr2 != NULL)) && ((attr1 == NULL) || (attr2 == NULL) || (attr1->ulValueLen != attr2->ulValueLen) || !isc_safe_memequal(attr1->pValue, attr2->pValue, attr1->ulValueLen))) { return (false); } if (!rsa1->ontoken && !rsa2->ontoken) { return (true); } else if (rsa1->ontoken || rsa2->ontoken || (rsa1->object != rsa2->object)) { return (false); } return (true); } static isc_result_t pkcs11rsa_generate(dst_key_t *key, int exp, void (*callback)(int)) { CK_RV rv; CK_MECHANISM mech = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0 }; CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; CK_ULONG bits = 0; CK_BYTE pubexp[5]; CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE pubTemplate[] = { { CKA_CLASS, &pubClass, (CK_ULONG)sizeof(pubClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_VERIFY, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_MODULUS_BITS, &bits, (CK_ULONG)sizeof(bits) }, { CKA_PUBLIC_EXPONENT, &pubexp, (CK_ULONG)sizeof(pubexp) } }; CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; CK_ATTRIBUTE privTemplate[] = { { CKA_CLASS, &privClass, (CK_ULONG)sizeof(privClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_PRIVATE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_SENSITIVE, &falsevalue, (CK_ULONG)sizeof(falsevalue) }, { CKA_EXTRACTABLE, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_SIGN, &truevalue, (CK_ULONG)sizeof(truevalue) }, }; CK_ATTRIBUTE *attr; pk11_object_t *rsa; pk11_context_t *pk11_ctx; isc_result_t ret; unsigned int i; UNUSED(callback); /* * Reject incorrect RSA key lengths. */ switch (key->key_alg) { case DST_ALG_RSASHA1: case DST_ALG_NSEC3RSASHA1: /* From RFC 3110 */ if (key->key_size > 4096) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA256: /* From RFC 5702 */ if ((key->key_size < 512) || (key->key_size > 4096)) { return (ISC_R_FAILURE); } break; case DST_ALG_RSASHA512: /* From RFC 5702 */ if ((key->key_size < 1024) || (key->key_size > 4096)) { return (ISC_R_FAILURE); } break; default: UNREACHABLE(); } pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, false, NULL, pk11_get_best_token(OP_RSA)); if (ret != ISC_R_SUCCESS) { goto err; } bits = key->key_size; if (exp == 0) { /* RSA_F4 0x10001 */ pubexp[0] = 1; pubexp[1] = 0; pubexp[2] = 1; pubTemplate[6].ulValueLen = 3; } else { /* F5 0x100000001 */ pubexp[0] = 1; pubexp[1] = 0; pubexp[2] = 0; pubexp[3] = 0; pubexp[4] = 1; pubTemplate[6].ulValueLen = 5; } PK11_RET(pkcs_C_GenerateKeyPair, (pk11_ctx->session, &mech, pubTemplate, (CK_ULONG)7, privTemplate, (CK_ULONG)7, &pub, &priv), DST_R_CRYPTOFAILURE); rsa = isc_mem_get(key->mctx, sizeof(*rsa)); memset(rsa, 0, sizeof(*rsa)); key->keydata.pkey = rsa; rsa->repr = isc_mem_get(key->mctx, sizeof(*attr) * 8); memset(rsa->repr, 0, sizeof(*attr) * 8); rsa->attrcnt = 8; attr = rsa->repr; attr[0].type = CKA_MODULUS; attr[1].type = CKA_PUBLIC_EXPONENT; attr[2].type = CKA_PRIVATE_EXPONENT; attr[3].type = CKA_PRIME_1; attr[4].type = CKA_PRIME_2; attr[5].type = CKA_EXPONENT_1; attr[6].type = CKA_EXPONENT_2; attr[7].type = CKA_COEFFICIENT; PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, pub, attr, 2), DST_R_CRYPTOFAILURE); for (i = 0; i <= 1; i++) { attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen); memset(attr[i].pValue, 0, attr[i].ulValueLen); } PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, pub, attr, 2), DST_R_CRYPTOFAILURE); attr += 2; PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, priv, attr, 6), DST_R_CRYPTOFAILURE); for (i = 0; i <= 5; i++) { attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen); memset(attr[i].pValue, 0, attr[i].ulValueLen); } PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, priv, attr, 6), DST_R_CRYPTOFAILURE); (void)pkcs_C_DestroyObject(pk11_ctx->session, priv); (void)pkcs_C_DestroyObject(pk11_ctx->session, pub); pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ISC_R_SUCCESS); err: pkcs11rsa_destroy(key); if (priv != CK_INVALID_HANDLE) { (void)pkcs_C_DestroyObject(pk11_ctx->session, priv); } if (pub != CK_INVALID_HANDLE) { (void)pkcs_C_DestroyObject(pk11_ctx->session, pub); } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static bool pkcs11rsa_isprivate(const dst_key_t *key) { pk11_object_t *rsa = key->keydata.pkey; CK_ATTRIBUTE *attr; if (rsa == NULL) { return (false); } attr = pk11_attribute_bytype(rsa, CKA_PRIVATE_EXPONENT); return (attr != NULL || rsa->ontoken); } static void pkcs11rsa_destroy(dst_key_t *key) { pk11_object_t *rsa = key->keydata.pkey; CK_ATTRIBUTE *attr; if (rsa == NULL) { return; } INSIST((rsa->object == CK_INVALID_HANDLE) || rsa->ontoken); for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { switch (attr->type) { case CKA_LABEL: case CKA_ID: case CKA_MODULUS: case CKA_PUBLIC_EXPONENT: case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: if (attr->pValue != NULL) { isc_safe_memwipe(attr->pValue, attr->ulValueLen); isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); } break; } } if (rsa->repr != NULL) { isc_safe_memwipe(rsa->repr, rsa->attrcnt * sizeof(*attr)); isc_mem_put(key->mctx, rsa->repr, rsa->attrcnt * sizeof(*attr)); } isc_safe_memwipe(rsa, sizeof(*rsa)); isc_mem_put(key->mctx, rsa, sizeof(*rsa)); key->keydata.pkey = NULL; } static isc_result_t pkcs11rsa_todns(const dst_key_t *key, isc_buffer_t *data) { pk11_object_t *rsa; CK_ATTRIBUTE *attr; isc_region_t r; unsigned int e_bytes = 0, mod_bytes = 0; CK_BYTE *exponent = NULL, *modulus = NULL; REQUIRE(key->keydata.pkey != NULL); rsa = key->keydata.pkey; for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { switch (attr->type) { case CKA_PUBLIC_EXPONENT: exponent = (CK_BYTE *)attr->pValue; e_bytes = (unsigned int)attr->ulValueLen; break; case CKA_MODULUS: modulus = (CK_BYTE *)attr->pValue; mod_bytes = (unsigned int)attr->ulValueLen; break; } } REQUIRE((exponent != NULL) && (modulus != NULL)); isc_buffer_availableregion(data, &r); if (e_bytes < 256) { /*%< key exponent is <= 2040 bits */ if (r.length < 1) { return (ISC_R_NOSPACE); } isc_buffer_putuint8(data, (uint8_t)e_bytes); isc_region_consume(&r, 1); } else { if (r.length < 3) { return (ISC_R_NOSPACE); } isc_buffer_putuint8(data, 0); isc_buffer_putuint16(data, (uint16_t)e_bytes); isc_region_consume(&r, 3); } if (r.length < e_bytes + mod_bytes) { return (ISC_R_NOSPACE); } memmove(r.base, exponent, e_bytes); isc_region_consume(&r, e_bytes); memmove(r.base, modulus, mod_bytes); isc_buffer_add(data, e_bytes + mod_bytes); return (ISC_R_SUCCESS); } static isc_result_t pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) { pk11_object_t *rsa; isc_region_t r; unsigned int e_bytes, mod_bytes; CK_BYTE *exponent = NULL, *modulus = NULL; CK_ATTRIBUTE *attr; unsigned int length; unsigned int bits; isc_result_t ret = ISC_R_SUCCESS; isc_buffer_remainingregion(data, &r); if (r.length == 0) { return (ISC_R_SUCCESS); } length = r.length; rsa = isc_mem_get(key->mctx, sizeof(*rsa)); memset(rsa, 0, sizeof(*rsa)); e_bytes = *r.base; isc_region_consume(&r, 1); if (e_bytes == 0) { if (r.length < 2) { DST_RET(DST_R_INVALIDPUBLICKEY); } e_bytes = (*r.base) << 8; isc_region_consume(&r, 1); e_bytes += *r.base; isc_region_consume(&r, 1); } if (r.length < e_bytes) { DST_RET(DST_R_INVALIDPUBLICKEY); } exponent = r.base; isc_region_consume(&r, e_bytes); modulus = r.base; mod_bytes = r.length; ret = pk11_numbits(modulus, mod_bytes, &bits); if (ret != ISC_R_SUCCESS) { goto err; } key->key_size = bits; isc_buffer_forward(data, length); rsa->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); memset(rsa->repr, 0, sizeof(*attr) * 2); rsa->attrcnt = 2; attr = rsa->repr; attr[0].type = CKA_MODULUS; attr[0].pValue = isc_mem_get(key->mctx, mod_bytes); memmove(attr[0].pValue, modulus, mod_bytes); attr[0].ulValueLen = (CK_ULONG)mod_bytes; attr[1].type = CKA_PUBLIC_EXPONENT; attr[1].pValue = isc_mem_get(key->mctx, e_bytes); memmove(attr[1].pValue, exponent, e_bytes); attr[1].ulValueLen = (CK_ULONG)e_bytes; key->keydata.pkey = rsa; return (ISC_R_SUCCESS); err: isc_safe_memwipe(rsa, sizeof(*rsa)); isc_mem_put(key->mctx, rsa, sizeof(*rsa)); return (ret); } static isc_result_t pkcs11rsa_tofile(const dst_key_t *key, const char *directory) { int i; pk11_object_t *rsa; CK_ATTRIBUTE *attr; CK_ATTRIBUTE *modulus = NULL, *exponent = NULL; CK_ATTRIBUTE *d = NULL, *p = NULL, *q = NULL; CK_ATTRIBUTE *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; dst_private_t priv; unsigned char *bufs[10]; isc_result_t result; if (key->keydata.pkey == NULL) { return (DST_R_NULLKEY); } if (key->external) { priv.nelements = 0; return (dst__privstruct_writefile(key, &priv, directory)); } rsa = key->keydata.pkey; for (attr = pk11_attribute_first(rsa); attr != NULL; attr = pk11_attribute_next(rsa, attr)) { switch (attr->type) { case CKA_MODULUS: modulus = attr; break; case CKA_PUBLIC_EXPONENT: exponent = attr; break; case CKA_PRIVATE_EXPONENT: d = attr; break; case CKA_PRIME_1: p = attr; break; case CKA_PRIME_2: q = attr; break; case CKA_EXPONENT_1: dmp1 = attr; break; case CKA_EXPONENT_2: dmq1 = attr; break; case CKA_COEFFICIENT: iqmp = attr; break; } } if ((modulus == NULL) || (exponent == NULL)) { return (DST_R_NULLKEY); } memset(bufs, 0, sizeof(bufs)); for (i = 0; i < 10; i++) { bufs[i] = isc_mem_get(key->mctx, modulus->ulValueLen); memset(bufs[i], 0, modulus->ulValueLen); } i = 0; priv.elements[i].tag = TAG_RSA_MODULUS; priv.elements[i].length = (unsigned short)modulus->ulValueLen; memmove(bufs[i], modulus->pValue, modulus->ulValueLen); priv.elements[i].data = bufs[i]; i++; priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT; priv.elements[i].length = (unsigned short)exponent->ulValueLen; memmove(bufs[i], exponent->pValue, exponent->ulValueLen); priv.elements[i].data = bufs[i]; i++; if (d != NULL) { priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT; priv.elements[i].length = (unsigned short)d->ulValueLen; memmove(bufs[i], d->pValue, d->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (p != NULL) { priv.elements[i].tag = TAG_RSA_PRIME1; priv.elements[i].length = (unsigned short)p->ulValueLen; memmove(bufs[i], p->pValue, p->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (q != NULL) { priv.elements[i].tag = TAG_RSA_PRIME2; priv.elements[i].length = (unsigned short)q->ulValueLen; memmove(bufs[i], q->pValue, q->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (dmp1 != NULL) { priv.elements[i].tag = TAG_RSA_EXPONENT1; priv.elements[i].length = (unsigned short)dmp1->ulValueLen; memmove(bufs[i], dmp1->pValue, dmp1->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (dmq1 != NULL) { priv.elements[i].tag = TAG_RSA_EXPONENT2; priv.elements[i].length = (unsigned short)dmq1->ulValueLen; memmove(bufs[i], dmq1->pValue, dmq1->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (iqmp != NULL) { priv.elements[i].tag = TAG_RSA_COEFFICIENT; priv.elements[i].length = (unsigned short)iqmp->ulValueLen; memmove(bufs[i], iqmp->pValue, iqmp->ulValueLen); priv.elements[i].data = bufs[i]; i++; } if (key->engine != NULL) { priv.elements[i].tag = TAG_RSA_ENGINE; priv.elements[i].length = (unsigned short)strlen(key->engine) + 1; priv.elements[i].data = (unsigned char *)key->engine; i++; } if (key->label != NULL) { priv.elements[i].tag = TAG_RSA_LABEL; priv.elements[i].length = (unsigned short)strlen(key->label) + 1; priv.elements[i].data = (unsigned char *)key->label; i++; } priv.nelements = i; result = dst__privstruct_writefile(key, &priv, directory); for (i = 0; i < 10; i++) { if (bufs[i] == NULL) { break; } isc_safe_memwipe(bufs[i], modulus->ulValueLen); isc_mem_put(key->mctx, bufs[i], modulus->ulValueLen); } return (result); } static isc_result_t pkcs11rsa_fetch(dst_key_t *key, const char *engine, const char *label, dst_key_t *pub) { CK_RV rv; CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE searchTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_LABEL, NULL, 0 } }; CK_ULONG cnt; CK_ATTRIBUTE *attr; CK_ATTRIBUTE *pubattr; pk11_object_t *rsa; pk11_object_t *pubrsa; pk11_context_t *pk11_ctx = NULL; isc_result_t ret; unsigned int bits; if (label == NULL) { return (DST_R_NOENGINE); } rsa = key->keydata.pkey; pubrsa = pub->keydata.pkey; rsa->object = CK_INVALID_HANDLE; rsa->ontoken = true; rsa->reqlogon = true; rsa->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); memset(rsa->repr, 0, sizeof(*attr) * 2); rsa->attrcnt = 2; attr = rsa->repr; attr->type = CKA_MODULUS; pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS); INSIST(pubattr != NULL); attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen); memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen); attr->ulValueLen = pubattr->ulValueLen; attr++; attr->type = CKA_PUBLIC_EXPONENT; pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT); INSIST(pubattr != NULL); attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen); memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen); attr->ulValueLen = pubattr->ulValueLen; ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA); if (ret != ISC_R_SUCCESS) { goto err; } pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, rsa->reqlogon, NULL, rsa->slot); if (ret != ISC_R_SUCCESS) { goto err; } attr = pk11_attribute_bytype(rsa, CKA_LABEL); if (attr == NULL) { attr = pk11_attribute_bytype(rsa, CKA_ID); INSIST(attr != NULL); searchTemplate[3].type = CKA_ID; } searchTemplate[3].pValue = attr->pValue; searchTemplate[3].ulValueLen = attr->ulValueLen; PK11_RET(pkcs_C_FindObjectsInit, (pk11_ctx->session, searchTemplate, (CK_ULONG)4), DST_R_CRYPTOFAILURE); PK11_RET(pkcs_C_FindObjects, (pk11_ctx->session, &rsa->object, (CK_ULONG)1, &cnt), DST_R_CRYPTOFAILURE); (void)pkcs_C_FindObjectsFinal(pk11_ctx->session); if (cnt == 0) { DST_RET(ISC_R_NOTFOUND); } if (cnt > 1) { DST_RET(ISC_R_EXISTS); } if (engine != NULL) { key->engine = isc_mem_strdup(key->mctx, engine); } key->label = isc_mem_strdup(key->mctx, label); pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); attr = pk11_attribute_bytype(rsa, CKA_MODULUS); INSIST(attr != NULL); ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS) { goto err; } key->key_size = bits; return (ISC_R_SUCCESS); err: if (pk11_ctx != NULL) { pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); } return (ret); } static isc_result_t rsa_check(pk11_object_t *rsa, pk11_object_t *pubrsa) { CK_ATTRIBUTE *pubattr, *privattr; CK_BYTE *priv_exp = NULL, *priv_mod = NULL; CK_BYTE *pub_exp = NULL, *pub_mod = NULL; unsigned int priv_explen = 0, priv_modlen = 0; unsigned int pub_explen = 0, pub_modlen = 0; REQUIRE(rsa != NULL && pubrsa != NULL); privattr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT); INSIST(privattr != NULL); priv_exp = privattr->pValue; priv_explen = privattr->ulValueLen; pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT); INSIST(pubattr != NULL); pub_exp = pubattr->pValue; pub_explen = pubattr->ulValueLen; if (priv_exp != NULL) { if (priv_explen != pub_explen) { return (DST_R_INVALIDPRIVATEKEY); } if (!isc_safe_memequal(priv_exp, pub_exp, pub_explen)) { return (DST_R_INVALIDPRIVATEKEY); } } else { privattr->pValue = pub_exp; privattr->ulValueLen = pub_explen; pubattr->pValue = NULL; pubattr->ulValueLen = 0; } if (privattr->pValue == NULL) { return (DST_R_INVALIDPRIVATEKEY); } privattr = pk11_attribute_bytype(rsa, CKA_MODULUS); INSIST(privattr != NULL); priv_mod = privattr->pValue; priv_modlen = privattr->ulValueLen; pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS); INSIST(pubattr != NULL); pub_mod = pubattr->pValue; pub_modlen = pubattr->ulValueLen; if (priv_mod != NULL) { if (priv_modlen != pub_modlen) { return (DST_R_INVALIDPRIVATEKEY); } if (!isc_safe_memequal(priv_mod, pub_mod, pub_modlen)) { return (DST_R_INVALIDPRIVATEKEY); } } else { privattr->pValue = pub_mod; privattr->ulValueLen = pub_modlen; pubattr->pValue = NULL; pubattr->ulValueLen = 0; } if (privattr->pValue == NULL) { return (DST_R_INVALIDPRIVATEKEY); } return (ISC_R_SUCCESS); } static isc_result_t pkcs11rsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t ret; int i; pk11_object_t *rsa; CK_ATTRIBUTE *attr; isc_mem_t *mctx = key->mctx; const char *engine = NULL, *label = NULL; unsigned int bits; /* read private key file */ ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv); if (ret != ISC_R_SUCCESS) { return (ret); } if (key->external) { if (priv.nelements != 0) { DST_RET(DST_R_INVALIDPRIVATEKEY); } if (pub == NULL) { DST_RET(DST_R_INVALIDPRIVATEKEY); } key->keydata.pkey = pub->keydata.pkey; pub->keydata.pkey = NULL; key->key_size = pub->key_size; dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ISC_R_SUCCESS); } for (i = 0; i < priv.nelements; i++) { switch (priv.elements[i].tag) { case TAG_RSA_ENGINE: engine = (char *)priv.elements[i].data; break; case TAG_RSA_LABEL: label = (char *)priv.elements[i].data; break; default: break; } } rsa = isc_mem_get(key->mctx, sizeof(*rsa)); memset(rsa, 0, sizeof(*rsa)); key->keydata.pkey = rsa; /* Is this key is stored in a HSM? See if we can fetch it. */ if ((label != NULL) || (engine != NULL)) { ret = pkcs11rsa_fetch(key, engine, label, pub); if (ret != ISC_R_SUCCESS) { goto err; } dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ret); } rsa->repr = isc_mem_get(key->mctx, sizeof(*attr) * 8); memset(rsa->repr, 0, sizeof(*attr) * 8); rsa->attrcnt = 8; attr = rsa->repr; attr[0].type = CKA_MODULUS; attr[1].type = CKA_PUBLIC_EXPONENT; attr[2].type = CKA_PRIVATE_EXPONENT; attr[3].type = CKA_PRIME_1; attr[4].type = CKA_PRIME_2; attr[5].type = CKA_EXPONENT_1; attr[6].type = CKA_EXPONENT_2; attr[7].type = CKA_COEFFICIENT; for (i = 0; i < priv.nelements; i++) { CK_BYTE *bn; switch (priv.elements[i].tag) { case TAG_RSA_ENGINE: continue; case TAG_RSA_LABEL: continue; default: bn = isc_mem_get(key->mctx, priv.elements[i].length); memmove(bn, priv.elements[i].data, priv.elements[i].length); } switch (priv.elements[i].tag) { case TAG_RSA_MODULUS: attr = pk11_attribute_bytype(rsa, CKA_MODULUS); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_PUBLICEXPONENT: attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_PRIVATEEXPONENT: attr = pk11_attribute_bytype(rsa, CKA_PRIVATE_EXPONENT); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_PRIME1: attr = pk11_attribute_bytype(rsa, CKA_PRIME_1); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_PRIME2: attr = pk11_attribute_bytype(rsa, CKA_PRIME_2); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_EXPONENT1: attr = pk11_attribute_bytype(rsa, CKA_EXPONENT_1); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_EXPONENT2: attr = pk11_attribute_bytype(rsa, CKA_EXPONENT_2); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; case TAG_RSA_COEFFICIENT: attr = pk11_attribute_bytype(rsa, CKA_COEFFICIENT); INSIST(attr != NULL); attr->pValue = bn; attr->ulValueLen = priv.elements[i].length; break; } } if (rsa_check(rsa, pub->keydata.pkey) != ISC_R_SUCCESS) { DST_RET(DST_R_INVALIDPRIVATEKEY); } attr = pk11_attribute_bytype(rsa, CKA_MODULUS); INSIST(attr != NULL); ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS) { goto err; } key->key_size = bits; attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT); INSIST(attr != NULL); ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS) { goto err; } if (bits > RSA_MAX_PUBEXP_BITS) { DST_RET(ISC_R_RANGE); } dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ISC_R_SUCCESS); err: pkcs11rsa_destroy(key); dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ret); } static isc_result_t pkcs11rsa_fromlabel(dst_key_t *key, const char *engine, const char *label, const char *pin) { CK_RV rv; CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE searchTemplate[] = { { CKA_CLASS, &keyClass, (CK_ULONG)sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, (CK_ULONG)sizeof(keyType) }, { CKA_TOKEN, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_LABEL, NULL, 0 } }; CK_ULONG cnt; CK_ATTRIBUTE *attr; pk11_object_t *rsa; pk11_context_t *pk11_ctx = NULL; isc_result_t ret; unsigned int i; unsigned int bits; UNUSED(pin); rsa = isc_mem_get(key->mctx, sizeof(*rsa)); memset(rsa, 0, sizeof(*rsa)); rsa->object = CK_INVALID_HANDLE; rsa->ontoken = true; rsa->reqlogon = true; key->keydata.pkey = rsa; rsa->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); memset(rsa->repr, 0, sizeof(*attr) * 2); rsa->attrcnt = 2; attr = rsa->repr; attr[0].type = CKA_MODULUS; attr[1].type = CKA_PUBLIC_EXPONENT; ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA); if (ret != ISC_R_SUCCESS) { goto err; } pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_RSA, true, false, rsa->reqlogon, NULL, rsa->slot); if (ret != ISC_R_SUCCESS) { goto err; } attr = pk11_attribute_bytype(rsa, CKA_LABEL); if (attr == NULL) { attr = pk11_attribute_bytype(rsa, CKA_ID); INSIST(attr != NULL); searchTemplate[3].type = CKA_ID; } searchTemplate[3].pValue = attr->pValue; searchTemplate[3].ulValueLen = attr->ulValueLen; PK11_RET(pkcs_C_FindObjectsInit, (pk11_ctx->session, searchTemplate, (CK_ULONG)4), DST_R_CRYPTOFAILURE); PK11_RET(pkcs_C_FindObjects, (pk11_ctx->session, &hKey, (CK_ULONG)1, &cnt), DST_R_CRYPTOFAILURE); (void)pkcs_C_FindObjectsFinal(pk11_ctx->session); if (cnt == 0) { DST_RET(ISC_R_NOTFOUND); } if (cnt > 1) { DST_RET(ISC_R_EXISTS); } attr = rsa->repr; PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, hKey, attr, 2), DST_R_CRYPTOFAILURE); for (i = 0; i <= 1; i++) { attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen); memset(attr[i].pValue, 0, attr[i].ulValueLen); } PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, hKey, attr, 2), DST_R_CRYPTOFAILURE); keyClass = CKO_PRIVATE_KEY; PK11_RET(pkcs_C_FindObjectsInit, (pk11_ctx->session, searchTemplate, (CK_ULONG)4), DST_R_CRYPTOFAILURE); PK11_RET(pkcs_C_FindObjects, (pk11_ctx->session, &rsa->object, (CK_ULONG)1, &cnt), DST_R_CRYPTOFAILURE); (void)pkcs_C_FindObjectsFinal(pk11_ctx->session); if (cnt == 0) { DST_RET(ISC_R_NOTFOUND); } if (cnt > 1) { DST_RET(ISC_R_EXISTS); } if (engine != NULL) { key->engine = isc_mem_strdup(key->mctx, engine); } key->label = isc_mem_strdup(key->mctx, label); attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT); INSIST(attr != NULL); ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS) { goto err; } if (bits > RSA_MAX_PUBEXP_BITS) { DST_RET(ISC_R_RANGE); } attr = pk11_attribute_bytype(rsa, CKA_MODULUS); INSIST(attr != NULL); ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits); if (ret != ISC_R_SUCCESS) { goto err; } key->key_size = bits; pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ISC_R_SUCCESS); err: pkcs11rsa_destroy(key); if (pk11_ctx != NULL) { pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); } return (ret); } static dst_func_t pkcs11rsa_functions = { pkcs11rsa_createctx, #ifndef PK11_RSA_PKCS_REPLACE pkcs11rsa_createctx2, #else /* ifndef PK11_RSA_PKCS_REPLACE */ NULL, /*%< createctx2 */ #endif /* ifndef PK11_RSA_PKCS_REPLACE */ pkcs11rsa_destroyctx, pkcs11rsa_adddata, pkcs11rsa_sign, pkcs11rsa_verify, NULL, /*%< verify2 */ NULL, /*%< computesecret */ pkcs11rsa_compare, NULL, /*%< paramcompare */ pkcs11rsa_generate, pkcs11rsa_isprivate, pkcs11rsa_destroy, pkcs11rsa_todns, pkcs11rsa_fromdns, pkcs11rsa_tofile, pkcs11rsa_parse, NULL, /*%< cleanup */ pkcs11rsa_fromlabel, NULL, /*%< dump */ NULL, /*%< restore */ }; isc_result_t dst__pkcs11rsa_init(dst_func_t **funcp) { REQUIRE(funcp != NULL); if (*funcp == NULL) { *funcp = &pkcs11rsa_functions; } return (ISC_R_SUCCESS); } #endif /* USE_PKCS11 */