/* $NetBSD: pkcs11ecdsa_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 #include #include "dst_internal.h" #include "dst_parse.h" #include "dst_pkcs11.h" /* * FIPS 186-3 ECDSA keys: * mechanisms: * CKM_ECDSA, * CKM_EC_KEY_PAIR_GEN * domain parameters: * CKA_EC_PARAMS (choice with OID namedCurve) * public keys: * object class CKO_PUBLIC_KEY * key type CKK_EC * attribute CKA_EC_PARAMS (choice with OID namedCurve) * attribute CKA_EC_POINT (point Q) * private keys: * object class CKO_PRIVATE_KEY * key type CKK_EC * attribute CKA_EC_PARAMS (choice with OID namedCurve) * attribute CKA_VALUE (big int d) * point format: 0x04 (octet-string) <2*size+1> 0x4 (uncompressed) */ #define TAG_OCTECT_STRING 0x04 #define UNCOMPRESSED 0x04 #define DST_RET(a) \ { \ ret = a; \ goto err; \ } static CK_BBOOL truevalue = TRUE; static CK_BBOOL falsevalue = FALSE; static void pkcs11ecdsa_destroy(dst_key_t *key); static isc_result_t pkcs11ecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { 0, NULL, 0 }; CK_SLOT_ID slotid; pk11_context_t *pk11_ctx; pk11_object_t *ec = key->keydata.pkey; isc_result_t ret; REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 || dctx->key->key_alg == DST_ALG_ECDSA384); REQUIRE(ec != NULL); if (dctx->key->key_alg == DST_ALG_ECDSA256) { mech.mechanism = CKM_SHA256; } else { mech.mechanism = CKM_SHA384; } pk11_ctx = isc_mem_get(dctx->mctx, sizeof(*pk11_ctx)); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); if (ec->ontoken && (dctx->use == DO_SIGN)) { slotid = ec->slot; } else { slotid = pk11_get_best_token(OP_ECDSA); } ret = pk11_get_session(pk11_ctx, OP_ECDSA, true, false, ec->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); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static void pkcs11ecdsa_destroyctx(dst_context_t *dctx) { CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH]; CK_ULONG len = ISC_SHA384_DIGESTLENGTH; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 || dctx->key->key_alg == DST_ALG_ECDSA384); if (pk11_ctx != NULL) { (void)pkcs_C_DigestFinal(pk11_ctx->session, garbage, &len); memset(garbage, 0, sizeof(garbage)); pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; } } static isc_result_t pkcs11ecdsa_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; REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 || dctx->key->key_alg == DST_ALG_ECDSA384); 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 pkcs11ecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { CK_RV rv; CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 }; CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_EC; 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_SIGN, &truevalue, (CK_ULONG)sizeof(truevalue) }, { CKA_EC_PARAMS, NULL, 0 }, { CKA_VALUE, NULL, 0 } }; CK_ATTRIBUTE *attr; CK_BYTE digest[ISC_SHA384_DIGESTLENGTH]; CK_ULONG dgstlen; CK_ULONG siglen; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; dst_key_t *key = dctx->key; pk11_object_t *ec = key->keydata.pkey; isc_region_t r; isc_result_t ret = ISC_R_SUCCESS; unsigned int i; REQUIRE(key->key_alg == DST_ALG_ECDSA256 || key->key_alg == DST_ALG_ECDSA384); REQUIRE(ec != NULL); switch (key->key_alg) { case DST_ALG_ECDSA256: dgstlen = ISC_SHA256_DIGESTLENGTH; siglen = DNS_SIG_ECDSA256SIZE; break; case DST_ALG_ECDSA384: siglen = DNS_SIG_ECDSA384SIZE; dgstlen = ISC_SHA384_DIGESTLENGTH; break; default: UNREACHABLE(); } PK11_RET(pkcs_C_DigestFinal, (pk11_ctx->session, digest, &dgstlen), ISC_R_FAILURE); isc_buffer_availableregion(sig, &r); if (r.length < siglen) { DST_RET(ISC_R_NOSPACE); } if (ec->ontoken && (ec->object != CK_INVALID_HANDLE)) { pk11_ctx->ontoken = ec->ontoken; pk11_ctx->object = ec->object; goto token_key; } for (attr = pk11_attribute_first(ec); attr != NULL; attr = pk11_attribute_next(ec, attr)) { switch (attr->type) { case CKA_EC_PARAMS: 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_VALUE: 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; } } 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); 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, (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 = 5; i <= 6; i++) { if (keyTemplate[i].pValue != NULL) { { memset(keyTemplate[i].pValue, 0, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; return (ret); } static isc_result_t pkcs11ecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) { CK_RV rv; CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 }; CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_EC; 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_EC_PARAMS, NULL, 0 }, { CKA_EC_POINT, NULL, 0 } }; CK_ATTRIBUTE *attr; CK_BYTE digest[ISC_SHA384_DIGESTLENGTH]; CK_ULONG dgstlen; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; dst_key_t *key = dctx->key; pk11_object_t *ec = key->keydata.pkey; isc_result_t ret = ISC_R_SUCCESS; unsigned int i; REQUIRE(key->key_alg == DST_ALG_ECDSA256 || key->key_alg == DST_ALG_ECDSA384); REQUIRE(ec != NULL); switch (key->key_alg) { case DST_ALG_ECDSA256: dgstlen = ISC_SHA256_DIGESTLENGTH; break; case DST_ALG_ECDSA384: dgstlen = ISC_SHA384_DIGESTLENGTH; break; default: UNREACHABLE(); } PK11_RET(pkcs_C_DigestFinal, (pk11_ctx->session, digest, &dgstlen), ISC_R_FAILURE); for (attr = pk11_attribute_first(ec); attr != NULL; attr = pk11_attribute_next(ec, attr)) { switch (attr->type) { case CKA_EC_PARAMS: 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_EC_POINT: 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; } } 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) { { memset(keyTemplate[i].pValue, 0, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } } } pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); dctx->ctxdata.pk11_ctx = NULL; return (ret); } static bool pkcs11ecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) { pk11_object_t *ec1, *ec2; CK_ATTRIBUTE *attr1, *attr2; ec1 = key1->keydata.pkey; ec2 = key2->keydata.pkey; if ((ec1 == NULL) && (ec2 == NULL)) { return (true); } else if ((ec1 == NULL) || (ec2 == NULL)) { return (false); } attr1 = pk11_attribute_bytype(ec1, CKA_EC_PARAMS); attr2 = pk11_attribute_bytype(ec2, CKA_EC_PARAMS); 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(ec1, CKA_EC_POINT); attr2 = pk11_attribute_bytype(ec2, CKA_EC_POINT); 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(ec1, CKA_VALUE); attr2 = pk11_attribute_bytype(ec2, CKA_VALUE); 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 (!ec1->ontoken && !ec2->ontoken) { return (true); } else if (ec1->ontoken || ec2->ontoken || (ec1->object != ec2->object)) { return (false); } return (true); } #define SETCURVE() \ switch (key->key_alg) { \ case DST_ALG_ECDSA256: \ attr->pValue = isc_mem_get(key->mctx, \ sizeof(PK11_ECC_PRIME256V1)); \ memmove(attr->pValue, PK11_ECC_PRIME256V1, \ sizeof(PK11_ECC_PRIME256V1)); \ attr->ulValueLen = sizeof(PK11_ECC_PRIME256V1); \ break; \ case DST_ALG_ECDSA384: \ attr->pValue = isc_mem_get(key->mctx, \ sizeof(PK11_ECC_SECP384R1)); \ memmove(attr->pValue, PK11_ECC_SECP384R1, \ sizeof(PK11_ECC_SECP384R1)); \ attr->ulValueLen = sizeof(PK11_ECC_SECP384R1); \ break; \ default: \ UNREACHABLE(); \ } #define FREECURVE() \ if (attr->pValue != NULL) { \ memset(attr->pValue, 0, attr->ulValueLen); \ isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); \ attr->pValue = NULL; \ } static isc_result_t pkcs11ecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { CK_RV rv; CK_MECHANISM mech = { CKM_EC_KEY_PAIR_GEN, NULL, 0 }; CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_EC; 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_EC_PARAMS, NULL, 0 } }; CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; CK_OBJECT_HANDLE 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 *ec; pk11_context_t *pk11_ctx; isc_result_t ret; REQUIRE(key->key_alg == DST_ALG_ECDSA256 || key->key_alg == DST_ALG_ECDSA384); UNUSED(unused); UNUSED(callback); pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_ECDSA, true, false, false, NULL, pk11_get_best_token(OP_ECDSA)); if (ret != ISC_R_SUCCESS) { goto err; } ec = isc_mem_get(key->mctx, sizeof(*ec)); memset(ec, 0, sizeof(*ec)); key->keydata.pkey = ec; ec->repr = isc_mem_get(key->mctx, sizeof(*attr) * 3); memset(ec->repr, 0, sizeof(*attr) * 3); ec->attrcnt = 3; attr = ec->repr; attr[0].type = CKA_EC_PARAMS; attr[1].type = CKA_EC_POINT; attr[2].type = CKA_VALUE; attr = &pubTemplate[5]; SETCURVE(); PK11_RET(pkcs_C_GenerateKeyPair, (pk11_ctx->session, &mech, pubTemplate, (CK_ULONG)6, privTemplate, (CK_ULONG)7, &pub, &priv), DST_R_CRYPTOFAILURE); attr = &pubTemplate[5]; FREECURVE(); attr = ec->repr; SETCURVE(); attr++; PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, pub, attr, 1), DST_R_CRYPTOFAILURE); attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); memset(attr->pValue, 0, attr->ulValueLen); PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, pub, attr, 1), DST_R_CRYPTOFAILURE); attr++; PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, priv, attr, 1), DST_R_CRYPTOFAILURE); attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); memset(attr->pValue, 0, attr->ulValueLen); PK11_RET(pkcs_C_GetAttributeValue, (pk11_ctx->session, priv, attr, 1), DST_R_CRYPTOFAILURE); (void)pkcs_C_DestroyObject(pk11_ctx->session, priv); (void)pkcs_C_DestroyObject(pk11_ctx->session, pub); pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); switch (key->key_alg) { case DST_ALG_ECDSA256: key->key_size = DNS_KEY_ECDSA256SIZE * 4; break; case DST_ALG_ECDSA384: key->key_size = DNS_KEY_ECDSA384SIZE * 4; break; default: UNREACHABLE(); } return (ISC_R_SUCCESS); err: pkcs11ecdsa_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); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static bool pkcs11ecdsa_isprivate(const dst_key_t *key) { pk11_object_t *ec = key->keydata.pkey; CK_ATTRIBUTE *attr; if (ec == NULL) { return (false); } attr = pk11_attribute_bytype(ec, CKA_VALUE); return (attr != NULL || ec->ontoken); } static void pkcs11ecdsa_destroy(dst_key_t *key) { pk11_object_t *ec = key->keydata.pkey; CK_ATTRIBUTE *attr; if (ec == NULL) { return; } INSIST((ec->object == CK_INVALID_HANDLE) || ec->ontoken); for (attr = pk11_attribute_first(ec); attr != NULL; attr = pk11_attribute_next(ec, attr)) { switch (attr->type) { case CKA_LABEL: case CKA_ID: case CKA_EC_PARAMS: case CKA_EC_POINT: case CKA_VALUE: FREECURVE(); break; } } if (ec->repr != NULL) { memset(ec->repr, 0, ec->attrcnt * sizeof(*attr)); isc_mem_put(key->mctx, ec->repr, ec->attrcnt * sizeof(*attr)); } memset(ec, 0, sizeof(*ec)); isc_mem_put(key->mctx, ec, sizeof(*ec)); key->keydata.pkey = NULL; } static isc_result_t pkcs11ecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { pk11_object_t *ec; isc_region_t r; unsigned int len; CK_ATTRIBUTE *attr; REQUIRE(key->keydata.pkey != NULL); switch (key->key_alg) { case DST_ALG_ECDSA256: len = DNS_KEY_ECDSA256SIZE; break; case DST_ALG_ECDSA384: len = DNS_KEY_ECDSA384SIZE; break; default: UNREACHABLE(); } ec = key->keydata.pkey; attr = pk11_attribute_bytype(ec, CKA_EC_POINT); if ((attr == NULL) || (attr->ulValueLen != len + 3) || (((CK_BYTE_PTR)attr->pValue)[0] != TAG_OCTECT_STRING) || (((CK_BYTE_PTR)attr->pValue)[1] != len + 1) || (((CK_BYTE_PTR)attr->pValue)[2] != UNCOMPRESSED)) { return (ISC_R_FAILURE); } isc_buffer_availableregion(data, &r); if (r.length < len) { return (ISC_R_NOSPACE); } memmove(r.base, (CK_BYTE_PTR)attr->pValue + 3, len); isc_buffer_add(data, len); return (ISC_R_SUCCESS); } static isc_result_t pkcs11ecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { pk11_object_t *ec; isc_region_t r; unsigned int len; CK_ATTRIBUTE *attr; REQUIRE(key->key_alg == DST_ALG_ECDSA256 || key->key_alg == DST_ALG_ECDSA384); switch (key->key_alg) { case DST_ALG_ECDSA256: len = DNS_KEY_ECDSA256SIZE; break; case DST_ALG_ECDSA384: len = DNS_KEY_ECDSA384SIZE; break; default: UNREACHABLE(); } isc_buffer_remainingregion(data, &r); if (r.length == 0) { return (ISC_R_SUCCESS); } if (r.length != len) { return (DST_R_INVALIDPUBLICKEY); } ec = isc_mem_get(key->mctx, sizeof(*ec)); memset(ec, 0, sizeof(*ec)); ec->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); ec->attrcnt = 2; attr = ec->repr; attr->type = CKA_EC_PARAMS; SETCURVE(); attr++; attr->type = CKA_EC_POINT; attr->pValue = isc_mem_get(key->mctx, len + 3); ((CK_BYTE_PTR)attr->pValue)[0] = TAG_OCTECT_STRING; ((CK_BYTE_PTR)attr->pValue)[1] = len + 1; ((CK_BYTE_PTR)attr->pValue)[2] = UNCOMPRESSED; memmove((CK_BYTE_PTR)attr->pValue + 3, r.base, len); attr->ulValueLen = len + 3; isc_buffer_forward(data, len); key->keydata.pkey = ec; key->key_size = len * 4; return (ISC_R_SUCCESS); } static isc_result_t pkcs11ecdsa_tofile(const dst_key_t *key, const char *directory) { isc_result_t ret; pk11_object_t *ec; dst_private_t priv; unsigned char *buf = NULL; unsigned int i = 0; CK_ATTRIBUTE *attr; if (key->keydata.pkey == NULL) { return (DST_R_NULLKEY); } if (key->external) { priv.nelements = 0; return (dst__privstruct_writefile(key, &priv, directory)); } ec = key->keydata.pkey; attr = pk11_attribute_bytype(ec, CKA_VALUE); if (attr != NULL) { buf = isc_mem_get(key->mctx, attr->ulValueLen); priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY; priv.elements[i].length = (unsigned short)attr->ulValueLen; memmove(buf, attr->pValue, attr->ulValueLen); priv.elements[i].data = buf; i++; } if (key->engine != NULL) { priv.elements[i].tag = TAG_ECDSA_ENGINE; priv.elements[i].length = strlen(key->engine) + 1; priv.elements[i].data = (unsigned char *)key->engine; i++; } if (key->label != NULL) { priv.elements[i].tag = TAG_ECDSA_LABEL; priv.elements[i].length = strlen(key->label) + 1; priv.elements[i].data = (unsigned char *)key->label; i++; } priv.nelements = i; ret = dst__privstruct_writefile(key, &priv, directory); if (buf != NULL) { memset(buf, 0, attr->ulValueLen); isc_mem_put(key->mctx, buf, attr->ulValueLen); } return (ret); } static isc_result_t pkcs11ecdsa_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_EC; 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 *ec; pk11_object_t *pubec; pk11_context_t *pk11_ctx = NULL; isc_result_t ret; if (label == NULL) { return (DST_R_NOENGINE); } ec = key->keydata.pkey; pubec = pub->keydata.pkey; ec->object = CK_INVALID_HANDLE; ec->ontoken = true; ec->reqlogon = true; ec->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); memset(ec->repr, 0, sizeof(*attr) * 2); ec->attrcnt = 2; attr = ec->repr; attr->type = CKA_EC_PARAMS; pubattr = pk11_attribute_bytype(pubec, CKA_EC_PARAMS); 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_EC_POINT; pubattr = pk11_attribute_bytype(pubec, CKA_EC_POINT); 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(ec, label, key->mctx, OP_ECDSA); if (ret != ISC_R_SUCCESS) { goto err; } pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_ECDSA, true, false, ec->reqlogon, NULL, ec->slot); if (ret != ISC_R_SUCCESS) { goto err; } attr = pk11_attribute_bytype(ec, CKA_LABEL); if (attr == NULL) { attr = pk11_attribute_bytype(ec, 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, &ec->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); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ISC_R_SUCCESS); err: if (pk11_ctx != NULL) { pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); } return (ret); } static isc_result_t pkcs11ecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t ret; pk11_object_t *ec = NULL; CK_ATTRIBUTE *attr, *pattr; isc_mem_t *mctx = key->mctx; unsigned int i; const char *engine = NULL, *label = NULL; REQUIRE(key->key_alg == DST_ALG_ECDSA256 || key->key_alg == DST_ALG_ECDSA384); if ((pub == NULL) || (pub->keydata.pkey == NULL)) { DST_RET(DST_R_INVALIDPRIVATEKEY); } /* read private key file */ ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv); if (ret != ISC_R_SUCCESS) { return (ret); } if (key->external) { if (priv.nelements != 0) { 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); memset(&priv, 0, sizeof(priv)); return (ISC_R_SUCCESS); } for (i = 0; i < priv.nelements; i++) { switch (priv.elements[i].tag) { case TAG_ECDSA_ENGINE: engine = (char *)priv.elements[i].data; break; case TAG_ECDSA_LABEL: label = (char *)priv.elements[i].data; break; default: break; } } ec = isc_mem_get(key->mctx, sizeof(*ec)); memset(ec, 0, sizeof(*ec)); key->keydata.pkey = ec; /* Is this key is stored in a HSM? See if we can fetch it. */ if ((label != NULL) || (engine != NULL)) { ret = pkcs11ecdsa_fetch(key, engine, label, pub); if (ret != ISC_R_SUCCESS) { goto err; } dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); return (ret); } ec->repr = isc_mem_get(key->mctx, sizeof(*attr) * 3); memset(ec->repr, 0, sizeof(*attr) * 3); ec->attrcnt = 3; attr = ec->repr; attr->type = CKA_EC_PARAMS; pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_PARAMS); INSIST(pattr != NULL); attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen); memmove(attr->pValue, pattr->pValue, pattr->ulValueLen); attr->ulValueLen = pattr->ulValueLen; attr++; attr->type = CKA_EC_POINT; pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_POINT); INSIST(pattr != NULL); attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen); memmove(attr->pValue, pattr->pValue, pattr->ulValueLen); attr->ulValueLen = pattr->ulValueLen; attr++; attr->type = CKA_VALUE; attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length); memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length); attr->ulValueLen = priv.elements[0].length; dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); switch (key->key_alg) { case DST_ALG_ECDSA256: key->key_size = DNS_KEY_ECDSA256SIZE * 4; break; case DST_ALG_ECDSA384: key->key_size = DNS_KEY_ECDSA384SIZE * 4; break; default: UNREACHABLE(); } return (ISC_R_SUCCESS); err: pkcs11ecdsa_destroy(key); dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); return (ret); } static isc_result_t pkcs11ecdsa_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_EC; 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 *ec; pk11_context_t *pk11_ctx = NULL; isc_result_t ret; unsigned int i; UNUSED(pin); ec = isc_mem_get(key->mctx, sizeof(*ec)); memset(ec, 0, sizeof(*ec)); ec->object = CK_INVALID_HANDLE; ec->ontoken = true; ec->reqlogon = true; key->keydata.pkey = ec; ec->repr = isc_mem_get(key->mctx, sizeof(*attr) * 2); memset(ec->repr, 0, sizeof(*attr) * 2); ec->attrcnt = 2; attr = ec->repr; attr[0].type = CKA_EC_PARAMS; attr[1].type = CKA_EC_POINT; ret = pk11_parse_uri(ec, label, key->mctx, OP_ECDSA); if (ret != ISC_R_SUCCESS) { goto err; } pk11_ctx = isc_mem_get(key->mctx, sizeof(*pk11_ctx)); ret = pk11_get_session(pk11_ctx, OP_ECDSA, true, false, ec->reqlogon, NULL, ec->slot); if (ret != ISC_R_SUCCESS) { goto err; } attr = pk11_attribute_bytype(ec, CKA_LABEL); if (attr == NULL) { attr = pk11_attribute_bytype(ec, 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 = ec->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, &ec->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); switch (key->key_alg) { case DST_ALG_ECDSA256: key->key_size = DNS_KEY_ECDSA256SIZE * 4; break; case DST_ALG_ECDSA384: key->key_size = DNS_KEY_ECDSA384SIZE * 4; break; default: UNREACHABLE(); } pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ISC_R_SUCCESS); err: pkcs11ecdsa_destroy(key); if (pk11_ctx != NULL) { pk11_return_session(pk11_ctx); memset(pk11_ctx, 0, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); } return (ret); } static dst_func_t pkcs11ecdsa_functions = { pkcs11ecdsa_createctx, NULL, /*%< createctx2 */ pkcs11ecdsa_destroyctx, pkcs11ecdsa_adddata, pkcs11ecdsa_sign, pkcs11ecdsa_verify, NULL, /*%< verify2 */ NULL, /*%< computesecret */ pkcs11ecdsa_compare, NULL, /*%< paramcompare */ pkcs11ecdsa_generate, pkcs11ecdsa_isprivate, pkcs11ecdsa_destroy, pkcs11ecdsa_todns, pkcs11ecdsa_fromdns, pkcs11ecdsa_tofile, pkcs11ecdsa_parse, NULL, /*%< cleanup */ pkcs11ecdsa_fromlabel, NULL, /*%< dump */ NULL, /*%< restore */ }; isc_result_t dst__pkcs11ecdsa_init(dst_func_t **funcp) { REQUIRE(funcp != NULL); if (*funcp == NULL) { *funcp = &pkcs11ecdsa_functions; } return (ISC_R_SUCCESS); } #endif /* USE_PKCS11 */