/* * 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 #include #ifndef PK11_DH_DISABLE #include #include #include #include #include #include #include #include "dst_internal.h" #include "dst_parse.h" #include "dst_pkcs11.h" #include #include #define WANT_DH_PRIMES #include #include /* * 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 EMPTY_TRANSLATION_UNIT #endif /* PKCS11CRYPTO */ /*! \file */