diff options
Diffstat (limited to 'lib/dns/pkcs11dh_link.c')
-rw-r--r-- | lib/dns/pkcs11dh_link.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/lib/dns/pkcs11dh_link.c b/lib/dns/pkcs11dh_link.c new file mode 100644 index 0000000..e2b60ea --- /dev/null +++ b/lib/dns/pkcs11dh_link.c @@ -0,0 +1,1136 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifdef PKCS11CRYPTO + +#include <config.h> + +#include <pk11/site.h> + +#ifndef PK11_DH_DISABLE + +#include <ctype.h> +#include <stdbool.h> + +#include <isc/mem.h> +#include <isc/safe.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dst/result.h> + +#include "dst_internal.h" +#include "dst_parse.h" +#include "dst_pkcs11.h" + +#include <pk11/pk11.h> +#include <pk11/internal.h> +#define WANT_DH_PRIMES +#include <pk11/constants.h> + +#include <pkcs11/pkcs11.h> + +/* + * PKCS#3 DH keys: + * mechanisms: + * CKM_DH_PKCS_PARAMETER_GEN, + * CKM_DH_PKCS_KEY_PAIR_GEN, + * CKM_DH_PKCS_DERIVE + * domain parameters: + * object class CKO_DOMAIN_PARAMETERS + * key type CKK_DH + * attribute CKA_PRIME (prime p) + * attribute CKA_BASE (base g) + * optional attribute CKA_PRIME_BITS (p length in bits) + * public key: + * object class CKO_PUBLIC_KEY + * key type CKK_DH + * attribute CKA_PRIME (prime p) + * attribute CKA_BASE (base g) + * attribute CKA_VALUE (public value y) + * private key: + * object class CKO_PRIVATE_KEY + * key type CKK_DH + * attribute CKA_PRIME (prime p) + * attribute CKA_BASE (base g) + * attribute CKA_VALUE (private value x) + * optional attribute CKA_VALUE_BITS (x length in bits) + * reuse CKA_PRIVATE_EXPONENT for key pair private value + */ + +#define CKA_VALUE2 CKA_PRIVATE_EXPONENT + +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; + +#define DST_RET(a) {ret = a; goto err;} + +static void pkcs11dh_destroy(dst_key_t *key); +static isc_result_t pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data); + +static isc_result_t +pkcs11dh_loadpriv(const dst_key_t *key, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE *hKey) +{ + CK_RV rv; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_DH; + 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_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ATTRIBUTE *attr; + const pk11_object_t *priv; + isc_result_t ret; + unsigned int i; + + priv = key->keydata.pkey; + if ((priv->object != CK_INVALID_HANDLE) && priv->ontoken) { + *hKey = priv->object; + return (ISC_R_SUCCESS); + } + + attr = pk11_attribute_bytype(priv, CKA_PRIME); + if (attr == NULL) + return (DST_R_INVALIDPRIVATEKEY); + keyTemplate[6].pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (keyTemplate[6].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen); + keyTemplate[6].ulValueLen = attr->ulValueLen; + + attr = pk11_attribute_bytype(priv, CKA_BASE); + if (attr == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + keyTemplate[7].pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (keyTemplate[7].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[7].pValue, attr->pValue, attr->ulValueLen); + keyTemplate[7].ulValueLen = attr->ulValueLen; + + attr = pk11_attribute_bytype(priv, CKA_VALUE2); + if (attr == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + keyTemplate[8].pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (keyTemplate[8].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[8].pValue, attr->pValue, attr->ulValueLen); + keyTemplate[8].ulValueLen = attr->ulValueLen; + + PK11_CALL(pkcs_C_CreateObject, + (session, keyTemplate, (CK_ULONG) 9, hKey), + DST_R_COMPUTESECRETFAILURE); + if (rv == CKR_OK) + ret = ISC_R_SUCCESS; + + err: + for (i = 6; i <= 8; i++) + if (keyTemplate[i].pValue != NULL) { + isc_safe_memwipe(keyTemplate[i].pValue, + keyTemplate[i].ulValueLen); + isc_mem_put(key->mctx, + keyTemplate[i].pValue, + keyTemplate[i].ulValueLen); + } + return (ret); +} + +static isc_result_t +pkcs11dh_computesecret(const dst_key_t *pub, const dst_key_t *priv, + isc_buffer_t *secret) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_DH_PKCS_DERIVE, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_OBJECT_HANDLE hDerived = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_ATTRIBUTE *attr; + CK_ULONG secLen; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE_LEN, &secLen, (CK_ULONG) sizeof(secLen) } + }; + CK_ATTRIBUTE valTemplate[] = + { + { CKA_VALUE, NULL, 0 } + }; + CK_BYTE *secValue; + pk11_context_t ctx; + isc_result_t ret; + unsigned int i; + isc_region_t r; + + REQUIRE(pub->keydata.pkey != NULL); + REQUIRE(priv->keydata.pkey != NULL); + REQUIRE(priv->keydata.pkey->repr != NULL); + attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_PRIME); + if (attr == NULL) + return (DST_R_INVALIDPUBLICKEY); + REQUIRE(attr != NULL); + secLen = attr->ulValueLen; + attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE); + if (attr == NULL) + return (DST_R_INVALIDPUBLICKEY); + + ret = pk11_get_session(&ctx, OP_DH, true, false, + priv->keydata.pkey->reqlogon, NULL, + pk11_get_best_token(OP_DH)); + if (ret != ISC_R_SUCCESS) + return (ret); + + mech.ulParameterLen = attr->ulValueLen; + mech.pParameter = isc_mem_get(pub->mctx, mech.ulParameterLen); + if (mech.pParameter == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(mech.pParameter, attr->pValue, mech.ulParameterLen); + + ret = pkcs11dh_loadpriv(priv, ctx.session, &hKey); + if (ret != ISC_R_SUCCESS) + goto err; + + PK11_RET(pkcs_C_DeriveKey, + (ctx.session, &mech, hKey, + keyTemplate, (CK_ULONG) 6, &hDerived), + DST_R_COMPUTESECRETFAILURE); + + attr = valTemplate; + PK11_RET(pkcs_C_GetAttributeValue, + (ctx.session, hDerived, attr, (CK_ULONG) 1), + DST_R_CRYPTOFAILURE); + attr->pValue = isc_mem_get(pub->mctx, attr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr->pValue, 0, attr->ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (ctx.session, hDerived, attr, (CK_ULONG) 1), + DST_R_CRYPTOFAILURE); + + /* strip leading zeros */ + secValue = (CK_BYTE_PTR) attr->pValue; + for (i = 0; i < attr->ulValueLen; i++) + if (secValue[i] != 0) + break; + isc_buffer_availableregion(secret, &r); + if (r.length < attr->ulValueLen - i) + DST_RET(ISC_R_NOSPACE); + memmove(r.base, secValue + i, attr->ulValueLen - i); + isc_buffer_add(secret, attr->ulValueLen - i); + ret = ISC_R_SUCCESS; + + err: + if (hDerived != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx.session, hDerived); + if (valTemplate[0].pValue != NULL) { + isc_safe_memwipe(valTemplate[0].pValue, + valTemplate[0].ulValueLen); + isc_mem_put(pub->mctx, + valTemplate[0].pValue, + valTemplate[0].ulValueLen); + } + if ((hKey != CK_INVALID_HANDLE) && !priv->keydata.pkey->ontoken) + (void) pkcs_C_DestroyObject(ctx.session, hKey); + if (mech.pParameter != NULL) { + isc_safe_memwipe(mech.pParameter, mech.ulParameterLen); + isc_mem_put(pub->mctx, mech.pParameter, mech.ulParameterLen); + } + pk11_return_session(&ctx); + return (ret); +} + +static bool +pkcs11dh_compare(const dst_key_t *key1, const dst_key_t *key2) { + pk11_object_t *dh1, *dh2; + CK_ATTRIBUTE *attr1, *attr2; + + dh1 = key1->keydata.pkey; + dh2 = key2->keydata.pkey; + + if ((dh1 == NULL) && (dh2 == NULL)) + return (true); + else if ((dh1 == NULL) || (dh2 == NULL)) + return (false); + + attr1 = pk11_attribute_bytype(dh1, CKA_PRIME); + attr2 = pk11_attribute_bytype(dh2, CKA_PRIME); + 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(dh1, CKA_BASE); + attr2 = pk11_attribute_bytype(dh2, CKA_BASE); + 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(dh1, CKA_VALUE); + attr2 = pk11_attribute_bytype(dh2, CKA_VALUE); + 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(dh1, CKA_VALUE2); + attr2 = pk11_attribute_bytype(dh2, CKA_VALUE2); + 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 (!dh1->ontoken && !dh2->ontoken) + return (true); + else if (dh1->ontoken || dh2->ontoken || + (dh1->object != dh2->object)) + return (false); + + return (true); +} + +static bool +pkcs11dh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) { + pk11_object_t *dh1, *dh2; + CK_ATTRIBUTE *attr1, *attr2; + + dh1 = key1->keydata.pkey; + dh2 = key2->keydata.pkey; + + if ((dh1 == NULL) && (dh2 == NULL)) + return (true); + else if ((dh1 == NULL) || (dh2 == NULL)) + return (false); + + attr1 = pk11_attribute_bytype(dh1, CKA_PRIME); + attr2 = pk11_attribute_bytype(dh2, CKA_PRIME); + 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(dh1, CKA_BASE); + attr2 = pk11_attribute_bytype(dh2, CKA_BASE); + 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); + + return (true); +} + +static isc_result_t +pkcs11dh_generate(dst_key_t *key, int generator, void (*callback)(int)) { + CK_RV rv; + CK_MECHANISM mech = { CKM_DH_PKCS_PARAMETER_GEN, NULL, 0 }; + CK_OBJECT_HANDLE domainparams = CK_INVALID_HANDLE; + CK_OBJECT_CLASS dClass = CKO_DOMAIN_PARAMETERS; + CK_KEY_TYPE keyType = CKK_DH; + CK_ULONG bits = 0; + CK_ATTRIBUTE dTemplate[] = + { + { CKA_CLASS, &dClass, (CK_ULONG) sizeof(dClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) } + }; + CK_ATTRIBUTE pTemplate[] = + { + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 } + }; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; + 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_PRIME, NULL, 0 }, + { CKA_BASE, 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_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) }, + }; + CK_ATTRIBUTE *attr; + pk11_object_t *dh = NULL; + pk11_context_t *pk11_ctx; + isc_result_t ret; + + UNUSED(callback); + + pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_DH, true, false, + false, NULL, pk11_get_best_token(OP_DH)); + if (ret != ISC_R_SUCCESS) + goto err; + + bits = key->key_size; + if ((generator == 0) && + ((bits == 768) || (bits == 1024) || (bits == 1536))) { + if (bits == 768) { + pubTemplate[4].pValue = + isc_mem_get(key->mctx, sizeof(pk11_dh_bn768)); + if (pubTemplate[4].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(pubTemplate[4].pValue, + pk11_dh_bn768, sizeof(pk11_dh_bn768)); + pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn768); + } else if (bits == 1024) { + pubTemplate[4].pValue = + isc_mem_get(key->mctx, sizeof(pk11_dh_bn1024)); + if (pubTemplate[4].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(pubTemplate[4].pValue, + pk11_dh_bn1024, sizeof(pk11_dh_bn1024)); + pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1024); + } else { + pubTemplate[4].pValue = + isc_mem_get(key->mctx, sizeof(pk11_dh_bn1536)); + if (pubTemplate[4].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(pubTemplate[4].pValue, + pk11_dh_bn1536, sizeof(pk11_dh_bn1536)); + pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1536); + } + pubTemplate[5].pValue = isc_mem_get(key->mctx, + sizeof(pk11_dh_bn2)); + if (pubTemplate[5].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(pubTemplate[5].pValue, pk11_dh_bn2, + sizeof(pk11_dh_bn2)); + pubTemplate[5].ulValueLen = sizeof(pk11_dh_bn2); + } else { + PK11_RET(pkcs_C_GenerateKey, + (pk11_ctx->session, &mech, + dTemplate, (CK_ULONG) 5, &domainparams), + DST_R_CRYPTOFAILURE); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, domainparams, + pTemplate, (CK_ULONG) 2), + DST_R_CRYPTOFAILURE); + pTemplate[0].pValue = isc_mem_get(key->mctx, + pTemplate[0].ulValueLen); + if (pTemplate[0].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(pTemplate[0].pValue, 0, pTemplate[0].ulValueLen); + pTemplate[1].pValue = isc_mem_get(key->mctx, + pTemplate[1].ulValueLen); + if (pTemplate[1].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(pTemplate[1].pValue, 0, pTemplate[1].ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, domainparams, + pTemplate, (CK_ULONG) 2), + DST_R_CRYPTOFAILURE); + + pubTemplate[4].pValue = pTemplate[0].pValue; + pubTemplate[4].ulValueLen = pTemplate[0].ulValueLen; + pTemplate[0].pValue = NULL; + pubTemplate[5].pValue = pTemplate[1].pValue; + pubTemplate[5].ulValueLen = pTemplate[1].ulValueLen; + pTemplate[1].pValue = NULL; + } + + mech.mechanism = CKM_DH_PKCS_KEY_PAIR_GEN; + PK11_RET(pkcs_C_GenerateKeyPair, + (pk11_ctx->session, &mech, + pubTemplate, (CK_ULONG) 6, + privTemplate, (CK_ULONG) 7, + &pub, &priv), + DST_R_CRYPTOFAILURE); + + dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh)); + if (dh == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dh, 0, sizeof(*dh)); + key->keydata.pkey = dh; + dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4); + if (dh->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dh->repr, 0, sizeof(*attr) * 4); + dh->attrcnt = 4; + + attr = dh->repr; + attr[0].type = CKA_PRIME; + attr[0].pValue = pubTemplate[4].pValue; + attr[0].ulValueLen = pubTemplate[4].ulValueLen; + pubTemplate[4].pValue = NULL; + + attr[1].type = CKA_BASE; + attr[1].pValue = pubTemplate[5].pValue; + attr[1].ulValueLen = pubTemplate[5].ulValueLen; + pubTemplate[5].pValue =NULL; + + attr += 2; + attr->type = CKA_VALUE; + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, pub, attr, 1), + DST_R_CRYPTOFAILURE); + attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr->pValue, 0, attr->ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, pub, attr, 1), + DST_R_CRYPTOFAILURE); + + attr++; + attr->type = CKA_VALUE; + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, priv, attr, 1), + DST_R_CRYPTOFAILURE); + attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr->pValue, 0, attr->ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, priv, attr, 1), + DST_R_CRYPTOFAILURE); + attr->type = CKA_VALUE2; + + (void) pkcs_C_DestroyObject(pk11_ctx->session, priv); + (void) pkcs_C_DestroyObject(pk11_ctx->session, pub); + (void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams); + 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: + pkcs11dh_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); + if (domainparams != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams); + + if (pubTemplate[4].pValue != NULL) { + isc_safe_memwipe(pubTemplate[4].pValue, + pubTemplate[4].ulValueLen); + isc_mem_put(key->mctx, + pubTemplate[4].pValue, + pubTemplate[4].ulValueLen); + } + if (pubTemplate[5].pValue != NULL) { + isc_safe_memwipe(pubTemplate[5].pValue, + pubTemplate[5].ulValueLen); + isc_mem_put(key->mctx, + pubTemplate[5].pValue, + pubTemplate[5].ulValueLen); + } + if (pTemplate[0].pValue != NULL) { + isc_safe_memwipe(pTemplate[0].pValue, + pTemplate[0].ulValueLen); + isc_mem_put(key->mctx, + pTemplate[0].pValue, + pTemplate[0].ulValueLen); + } + if (pTemplate[1].pValue != NULL) { + isc_safe_memwipe(pTemplate[1].pValue, + pTemplate[1].ulValueLen); + isc_mem_put(key->mctx, + pTemplate[1].pValue, + pTemplate[1].ulValueLen); + } + + 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 +pkcs11dh_isprivate(const dst_key_t *key) { + pk11_object_t *dh = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (dh == NULL) + return (false); + attr = pk11_attribute_bytype(dh, CKA_VALUE2); + return (attr != NULL || dh->ontoken); +} + +static void +pkcs11dh_destroy(dst_key_t *key) { + pk11_object_t *dh = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (dh == NULL) + return; + + INSIST((dh->object == CK_INVALID_HANDLE) || dh->ontoken); + + for (attr = pk11_attribute_first(dh); + attr != NULL; + attr = pk11_attribute_next(dh, attr)) + switch (attr->type) { + case CKA_VALUE: + case CKA_VALUE2: + case CKA_PRIME: + case CKA_BASE: + if (attr->pValue != NULL) { + isc_safe_memwipe(attr->pValue, + attr->ulValueLen); + isc_mem_put(key->mctx, + attr->pValue, + attr->ulValueLen); + } + break; + } + if (dh->repr != NULL) { + isc_safe_memwipe(dh->repr, dh->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr)); + } + isc_safe_memwipe(dh, sizeof(*dh)); + isc_mem_put(key->mctx, dh, sizeof(*dh)); + key->keydata.pkey = NULL; +} + +static void +uint16_toregion(uint16_t val, isc_region_t *region) { + *region->base = (val & 0xff00) >> 8; + isc_region_consume(region, 1); + *region->base = (val & 0x00ff); + isc_region_consume(region, 1); +} + +static uint16_t +uint16_fromregion(isc_region_t *region) { + uint16_t val; + unsigned char *cp = region->base; + + val = ((unsigned int)(cp[0])) << 8; + val |= ((unsigned int)(cp[1])); + + isc_region_consume(region, 2); + + return (val); +} + +static isc_result_t +pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *dh; + CK_ATTRIBUTE *attr; + isc_region_t r; + uint16_t dnslen, plen = 0, glen = 0, publen = 0; + CK_BYTE *prime = NULL, *base = NULL, *pub = NULL; + + REQUIRE(key->keydata.pkey != NULL); + + dh = key->keydata.pkey; + + for (attr = pk11_attribute_first(dh); + attr != NULL; + attr = pk11_attribute_next(dh, attr)) + switch (attr->type) { + case CKA_VALUE: + pub = (CK_BYTE *) attr->pValue; + publen = (uint16_t) attr->ulValueLen; + break; + case CKA_PRIME: + prime = (CK_BYTE *) attr->pValue; + plen = (uint16_t) attr->ulValueLen; + break; + case CKA_BASE: + base = (CK_BYTE *) attr->pValue; + glen = (uint16_t) attr->ulValueLen; + break; + } + REQUIRE((prime != NULL) && (base != NULL) && (pub != NULL)); + + isc_buffer_availableregion(data, &r); + + if ((glen == 1) && isc_safe_memequal(pk11_dh_bn2, base, glen) && + (((plen == sizeof(pk11_dh_bn768)) && + isc_safe_memequal(pk11_dh_bn768, prime, plen)) || + ((plen == sizeof(pk11_dh_bn1024)) && + isc_safe_memequal(pk11_dh_bn1024, prime, plen)) || + ((plen == sizeof(pk11_dh_bn1536)) && + isc_safe_memequal(pk11_dh_bn1536, prime, plen)))) { + plen = 1; + glen = 0; + } + + dnslen = plen + glen + publen + 6; + if (r.length < (unsigned int) dnslen) + return (ISC_R_NOSPACE); + + uint16_toregion(plen, &r); + if (plen == 1) { + if (isc_safe_memequal(pk11_dh_bn768, prime, + sizeof(pk11_dh_bn768))) + *r.base = 1; + else if (isc_safe_memequal(pk11_dh_bn1024, prime, + sizeof(pk11_dh_bn1024))) + *r.base = 2; + else + *r.base = 3; + } + else + memmove(r.base, prime, plen); + isc_region_consume(&r, plen); + + uint16_toregion(glen, &r); + if (glen > 0) + memmove(r.base, base, glen); + isc_region_consume(&r, glen); + + uint16_toregion(publen, &r); + memmove(r.base, pub, publen); + isc_region_consume(&r, publen); + + isc_buffer_add(data, dnslen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +pkcs11dh_fromdns(dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *dh = NULL; + isc_region_t r; + uint16_t plen, glen, plen_, glen_, publen; + CK_BYTE *prime = NULL, *base = NULL, *pub = NULL; + CK_ATTRIBUTE *attr; + int special = 0; + isc_result_t result; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) { + result = ISC_R_SUCCESS; + goto cleanup; + } + + dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh)); + if (dh == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + memset(dh, 0, sizeof(*dh)); + result = DST_R_INVALIDPUBLICKEY; + + /* + * Read the prime length. 1 & 2 are table entries, > 16 means a + * prime follows, otherwise an error. + */ + if (r.length < 2) + goto cleanup; + + plen = uint16_fromregion(&r); + if (plen < 16 && plen != 1 && plen != 2) + goto cleanup; + + if (r.length < plen) + goto cleanup; + + plen_ = plen; + if (plen == 1 || plen == 2) { + if (plen == 1) { + special = *r.base; + isc_region_consume(&r, 1); + } else { + special = uint16_fromregion(&r); + } + switch (special) { + case 1: + prime = pk11_dh_bn768; + plen_ = sizeof(pk11_dh_bn768); + break; + case 2: + prime = pk11_dh_bn1024; + plen_ = sizeof(pk11_dh_bn1024); + break; + case 3: + prime = pk11_dh_bn1536; + plen_ = sizeof(pk11_dh_bn1536); + break; + default: + goto cleanup; + } + } + else { + prime = r.base; + isc_region_consume(&r, plen); + } + + /* + * Read the generator length. This should be 0 if the prime was + * special, but it might not be. If it's 0 and the prime is not + * special, we have a problem. + */ + if (r.length < 2) + goto cleanup; + + glen = uint16_fromregion(&r); + if (r.length < glen) + goto cleanup; + + glen_ = glen; + if (special != 0) { + if (glen == 0) { + base = pk11_dh_bn2; + glen_ = sizeof(pk11_dh_bn2); + } + else { + base = r.base; + if (!isc_safe_memequal(base, pk11_dh_bn2, glen)) + goto cleanup; + base = pk11_dh_bn2; + glen_ = sizeof(pk11_dh_bn2); + } + } + else { + if (glen == 0) + goto cleanup; + base = r.base; + } + isc_region_consume(&r, glen); + + if (r.length < 2) + goto cleanup; + + publen = uint16_fromregion(&r); + if (r.length < publen) + goto cleanup; + + pub = r.base; + isc_region_consume(&r, publen); + + key->key_size = pk11_numbits(prime, plen_); + + dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3); + if (dh->repr == NULL) + goto nomemory; + memset(dh->repr, 0, sizeof(*attr) * 3); + dh->attrcnt = 3; + + attr = dh->repr; + attr[0].type = CKA_PRIME; + attr[0].pValue = isc_mem_get(key->mctx, plen_); + if (attr[0].pValue == NULL) + goto nomemory; + memmove(attr[0].pValue, prime, plen_); + attr[0].ulValueLen = (CK_ULONG) plen_; + + attr[1].type = CKA_BASE; + attr[1].pValue = isc_mem_get(key->mctx, glen_); + if (attr[1].pValue == NULL) + goto nomemory; + memmove(attr[1].pValue, base, glen_); + attr[1].ulValueLen = (CK_ULONG) glen_; + + attr[2].type = CKA_VALUE; + attr[2].pValue = isc_mem_get(key->mctx, publen); + if (attr[2].pValue == NULL) + goto nomemory; + memmove(attr[2].pValue, pub, publen); + attr[2].ulValueLen = (CK_ULONG) publen; + + isc_buffer_forward(data, plen + glen + publen + 6); + + key->keydata.pkey = dh; + + return (ISC_R_SUCCESS); + + nomemory: + for (attr = pk11_attribute_first(dh); + attr != NULL; + attr = pk11_attribute_next(dh, attr)) + switch (attr->type) { + case CKA_VALUE: + case CKA_PRIME: + case CKA_BASE: + if (attr->pValue != NULL) { + isc_safe_memwipe(attr->pValue, + attr->ulValueLen); + isc_mem_put(key->mctx, + attr->pValue, + attr->ulValueLen); + } + break; + } + if (dh->repr != NULL) { + isc_safe_memwipe(dh->repr, dh->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr)); + } + + result = ISC_R_NOMEMORY; + + cleanup: + if (dh != NULL) { + isc_safe_memwipe(dh, sizeof(*dh)); + isc_mem_put(key->mctx, dh, sizeof(*dh)); + } + return (result); +} + +static isc_result_t +pkcs11dh_tofile(const dst_key_t *key, const char *directory) { + int i; + pk11_object_t *dh; + CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *prime = NULL, *base = NULL, *pub = NULL, *prv = NULL; + dst_private_t priv; + unsigned char *bufs[4]; + isc_result_t result; + + if (key->keydata.pkey == NULL) + return (DST_R_NULLKEY); + + if (key->external) + return (DST_R_EXTERNALKEY); + + dh = key->keydata.pkey; + + for (attr = pk11_attribute_first(dh); + attr != NULL; + attr = pk11_attribute_next(dh, attr)) + switch (attr->type) { + case CKA_VALUE: + pub = attr; + break; + case CKA_VALUE2: + prv = attr; + break; + case CKA_PRIME: + prime = attr; + break; + case CKA_BASE: + base = attr; + break; + } + if ((prime == NULL) || (base == NULL) || + (pub == NULL) || (prv == NULL)) + return (DST_R_NULLKEY); + + memset(bufs, 0, sizeof(bufs)); + for (i = 0; i < 4; i++) { + bufs[i] = isc_mem_get(key->mctx, prime->ulValueLen); + if (bufs[i] == NULL) { + result = ISC_R_NOMEMORY; + goto fail; + } + memset(bufs[i], 0, prime->ulValueLen); + } + + i = 0; + + priv.elements[i].tag = TAG_DH_PRIME; + priv.elements[i].length = (unsigned short) prime->ulValueLen; + memmove(bufs[i], prime->pValue, prime->ulValueLen); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_GENERATOR; + priv.elements[i].length = (unsigned short) base->ulValueLen; + memmove(bufs[i], base->pValue, base->ulValueLen); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_PRIVATE; + priv.elements[i].length = (unsigned short) prv->ulValueLen; + memmove(bufs[i], prv->pValue, prv->ulValueLen); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_PUBLIC; + priv.elements[i].length = (unsigned short) pub->ulValueLen; + memmove(bufs[i], pub->pValue, pub->ulValueLen); + priv.elements[i].data = bufs[i]; + i++; + + priv.nelements = i; + result = dst__privstruct_writefile(key, &priv, directory); + fail: + for (i = 0; i < 4; i++) { + if (bufs[i] == NULL) + break; + isc_safe_memwipe(bufs[i], prime->ulValueLen); + isc_mem_put(key->mctx, bufs[i], prime->ulValueLen); + } + return (result); +} + +static isc_result_t +pkcs11dh_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 *dh = NULL; + CK_ATTRIBUTE *attr; + isc_mem_t *mctx; + + UNUSED(pub); + mctx = key->mctx; + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + if (key->external) + DST_RET(DST_R_EXTERNALKEY); + + dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh)); + if (dh == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dh, 0, sizeof(*dh)); + key->keydata.pkey = dh; + dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4); + if (dh->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dh->repr, 0, sizeof(*attr) * 4); + dh->attrcnt = 4; + attr = dh->repr; + attr[0].type = CKA_PRIME; + attr[1].type = CKA_BASE; + attr[2].type = CKA_VALUE; + attr[3].type = CKA_VALUE2; + + for (i = 0; i < priv.nelements; i++) { + CK_BYTE *bn; + + bn = isc_mem_get(key->mctx, priv.elements[i].length); + if (bn == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(bn, priv.elements[i].data, priv.elements[i].length); + + switch (priv.elements[i].tag) { + case TAG_DH_PRIME: + attr = pk11_attribute_bytype(dh, CKA_PRIME); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DH_GENERATOR: + attr = pk11_attribute_bytype(dh, CKA_BASE); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DH_PRIVATE: + attr = pk11_attribute_bytype(dh, CKA_VALUE2); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DH_PUBLIC: + attr = pk11_attribute_bytype(dh, CKA_VALUE); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + } + } + dst__privstruct_free(&priv, mctx); + + attr = pk11_attribute_bytype(dh, CKA_PRIME); + INSIST(attr != NULL); + key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen); + + return (ISC_R_SUCCESS); + + err: + pkcs11dh_destroy(key); + dst__privstruct_free(&priv, mctx); + isc_safe_memwipe(&priv, sizeof(priv)); + return (ret); +} + +static dst_func_t pkcs11dh_functions = { + NULL, /*%< createctx */ + NULL, /*%< createctx2 */ + NULL, /*%< destroyctx */ + NULL, /*%< adddata */ + NULL, /*%< sign */ + NULL, /*%< verify */ + NULL, /*%< verify2 */ + pkcs11dh_computesecret, + pkcs11dh_compare, + pkcs11dh_paramcompare, + pkcs11dh_generate, + pkcs11dh_isprivate, + pkcs11dh_destroy, + pkcs11dh_todns, + pkcs11dh_fromdns, + pkcs11dh_tofile, + pkcs11dh_parse, + NULL, /*%< cleanup */ + NULL, /*%< fromlabel */ + NULL, /*%< dump */ + NULL, /*%< restore */ +}; + +isc_result_t +dst__pkcs11dh_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &pkcs11dh_functions; + return (ISC_R_SUCCESS); +} +#endif /* !PK11_DH_DISABLE */ + +#else /* PKCS11CRYPTO */ + +#include <isc/util.h> + +EMPTY_TRANSLATION_UNIT + +#endif /* PKCS11CRYPTO */ +/*! \file */ |