diff options
Diffstat (limited to '')
-rw-r--r-- | lib/dns/pkcs11dsa_link.c | 1126 |
1 files changed, 1126 insertions, 0 deletions
diff --git a/lib/dns/pkcs11dsa_link.c b/lib/dns/pkcs11dsa_link.c new file mode 100644 index 0000000..12d707a --- /dev/null +++ b/lib/dns/pkcs11dsa_link.c @@ -0,0 +1,1126 @@ +/* + * 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_DSA_DISABLE + +#include <string.h> +#include <stdbool.h> + +#include <isc/entropy.h> +#include <isc/mem.h> +#include <isc/safe.h> +#include <isc/sha1.h> +#include <isc/util.h> + +#include <dst/result.h> + +#include "dst_internal.h" +#include "dst_parse.h" +#include "dst_pkcs11.h" + +#include <pk11/internal.h> + +/* + * FIPS 186-2 DSA keys: + * mechanisms: + * CKM_DSA_SHA1, + * CKM_DSA_KEY_PAIR_GEN, + * CKM_DSA_PARAMETER_GEN + * domain parameters: + * object class CKO_DOMAIN_PARAMETERS + * key type CKK_DSA + * attribute CKA_PRIME (prime p) + * attribute CKA_SUBPRIME (subprime q) + * attribute CKA_BASE (base g) + * optional attribute CKA_PRIME_BITS (p length in bits) + * public keys: + * object class CKO_PUBLIC_KEY + * key type CKK_DSA + * attribute CKA_PRIME (prime p) + * attribute CKA_SUBPRIME (subprime q) + * attribute CKA_BASE (base g) + * attribute CKA_VALUE (public value y) + * private keys: + * object class CKO_PRIVATE_KEY + * key type CKK_DSA + * attribute CKA_PRIME (prime p) + * attribute CKA_SUBPRIME (subprime q) + * attribute CKA_BASE (base g) + * attribute CKA_VALUE (private value x) + * reuse CKA_PRIVATE_EXPONENT for key pair private value + */ + +#define CKA_VALUE2 CKA_PRIVATE_EXPONENT + +#define DST_RET(a) {ret = a; goto err;} + +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; + +static isc_result_t pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data); +static void pkcs11dsa_destroy(dst_key_t *key); + +static isc_result_t +pkcs11dsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) { + CK_RV rv; + CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_DSA; + 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_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ATTRIBUTE *attr; + pk11_object_t *dsa; + pk11_context_t *pk11_ctx; + isc_result_t ret; + unsigned int i; + + REQUIRE(key != NULL); + dsa = key->keydata.pkey; + REQUIRE(dsa != NULL); + + pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_DSA, true, false, + dsa->reqlogon, NULL, + pk11_get_best_token(OP_DSA)); + if (ret != ISC_R_SUCCESS) + goto err; + + if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) { + pk11_ctx->ontoken = dsa->ontoken; + pk11_ctx->object = dsa->object; + goto token_key; + } + + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + INSIST(keyTemplate[6].type == attr->type); + keyTemplate[6].pValue = isc_mem_get(dctx->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; + break; + case CKA_SUBPRIME: + INSIST(keyTemplate[7].type == attr->type); + keyTemplate[7].pValue = isc_mem_get(dctx->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; + break; + case CKA_BASE: + INSIST(keyTemplate[8].type == attr->type); + keyTemplate[8].pValue = isc_mem_get(dctx->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; + break; + case CKA_VALUE2: + INSIST(keyTemplate[9].type == CKA_VALUE); + keyTemplate[9].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[9].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[9].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[9].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) 10, + &pk11_ctx->object), + ISC_R_FAILURE); + + token_key: + + 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 <= 9; 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 <= 9; 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 +pkcs11dsa_createctx_verify(dst_key_t *key, dst_context_t *dctx) { + CK_RV rv; + CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_DSA; + 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_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ATTRIBUTE *attr; + pk11_object_t *dsa; + pk11_context_t *pk11_ctx; + isc_result_t ret; + unsigned int i; + + dsa = key->keydata.pkey; + REQUIRE(dsa != NULL); + pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_DSA, true, false, + dsa->reqlogon, NULL, + pk11_get_best_token(OP_DSA)); + if (ret != ISC_R_SUCCESS) + goto err; + + if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) { + pk11_ctx->ontoken = dsa->ontoken; + pk11_ctx->object = dsa->object; + goto token_key; + } + + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + INSIST(keyTemplate[5].type == attr->type); + keyTemplate[5].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[5].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[5].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[5].ulValueLen = attr->ulValueLen; + break; + case CKA_SUBPRIME: + INSIST(keyTemplate[6].type == attr->type); + keyTemplate[6].pValue = isc_mem_get(dctx->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; + break; + case CKA_BASE: + INSIST(keyTemplate[7].type == attr->type); + keyTemplate[7].pValue = isc_mem_get(dctx->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; + break; + case CKA_VALUE: + INSIST(keyTemplate[8].type == attr->type); + keyTemplate[8].pValue = isc_mem_get(dctx->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; + break; + } + pk11_ctx->object = CK_INVALID_HANDLE; + pk11_ctx->ontoken = false; + PK11_RET(pkcs_C_CreateObject, + (pk11_ctx->session, + keyTemplate, (CK_ULONG) 9, + &pk11_ctx->object), + ISC_R_FAILURE); + + token_key: + + 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 <= 8; 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 <= 8; 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 +pkcs11dsa_createctx(dst_key_t *key, dst_context_t *dctx) { + if (dctx->use == DO_SIGN) + return (pkcs11dsa_createctx_sign(key, dctx)); + else + return (pkcs11dsa_createctx_verify(key, dctx)); +} + +static void +pkcs11dsa_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 +pkcs11dsa_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 +pkcs11dsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + CK_RV rv; + CK_ULONG siglen = ISC_SHA1_DIGESTLENGTH * 2; + isc_region_t r; + pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; + isc_result_t ret = ISC_R_SUCCESS; + unsigned int klen; + + isc_buffer_availableregion(sig, &r); + if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1) + return (ISC_R_NOSPACE); + + PK11_RET(pkcs_C_SignFinal, + (pk11_ctx->session, (CK_BYTE_PTR) r.base + 1, &siglen), + DST_R_SIGNFAILURE); + if (siglen != ISC_SHA1_DIGESTLENGTH * 2) + return (DST_R_SIGNFAILURE); + + klen = (dctx->key->key_size - 512)/64; + if (klen > 255) + return (ISC_R_FAILURE); + *r.base = klen; + isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1); + + err: + return (ret); +} + +static isc_result_t +pkcs11dsa_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 + 1, + (CK_ULONG) sig->length - 1), + DST_R_VERIFYFAILURE); + return (ret); +} + +static bool +pkcs11dsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + pk11_object_t *dsa1, *dsa2; + CK_ATTRIBUTE *attr1, *attr2; + + dsa1 = key1->keydata.pkey; + dsa2 = key2->keydata.pkey; + + if ((dsa1 == NULL) && (dsa2 == NULL)) + return (true); + else if ((dsa1 == NULL) || (dsa2 == NULL)) + return (false); + + attr1 = pk11_attribute_bytype(dsa1, CKA_PRIME); + attr2 = pk11_attribute_bytype(dsa2, 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(dsa1, CKA_SUBPRIME); + attr2 = pk11_attribute_bytype(dsa2, CKA_SUBPRIME); + 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(dsa1, CKA_BASE); + attr2 = pk11_attribute_bytype(dsa2, 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(dsa1, CKA_VALUE); + attr2 = pk11_attribute_bytype(dsa2, 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(dsa1, CKA_VALUE2); + attr2 = pk11_attribute_bytype(dsa2, 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 (!dsa1->ontoken && !dsa2->ontoken) + return (true); + else if (dsa1->ontoken || dsa2->ontoken || + (dsa1->object != dsa2->object)) + return (false); + + return (true); +} + +static isc_result_t +pkcs11dsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { + CK_RV rv; + CK_MECHANISM mech = { CKM_DSA_PARAMETER_GEN, NULL, 0 }; + CK_OBJECT_HANDLE dp = CK_INVALID_HANDLE; + CK_OBJECT_CLASS dpClass = CKO_DOMAIN_PARAMETERS; + CK_KEY_TYPE keyType = CKK_DSA; + CK_ULONG bits = 0; + CK_ATTRIBUTE dpTemplate[] = + { + { CKA_CLASS, &dpClass, (CK_ULONG) sizeof(dpClass) }, + { 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_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_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_PRIME, NULL, 0 }, + { CKA_SUBPRIME, 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_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + }; + CK_ATTRIBUTE *attr; + pk11_object_t *dsa; + pk11_context_t *pk11_ctx; + isc_result_t ret; + unsigned int i; + + UNUSED(unused); + 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_DSA, true, false, + false, NULL, pk11_get_best_token(OP_DSA)); + if (ret != ISC_R_SUCCESS) + goto err; + + bits = key->key_size; + PK11_RET(pkcs_C_GenerateKey, + (pk11_ctx->session, &mech, dpTemplate, (CK_ULONG) 5, &dp), + DST_R_CRYPTOFAILURE); + + dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa)); + if (dsa == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dsa, 0, sizeof(*dsa)); + key->keydata.pkey = dsa; + dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5); + if (dsa->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dsa->repr, 0, sizeof(*attr) * 5); + dsa->attrcnt = 5; + + attr = dsa->repr; + attr[0].type = CKA_PRIME; + attr[1].type = CKA_SUBPRIME; + attr[2].type = CKA_BASE; + attr[3].type = CKA_VALUE; + attr[4].type = CKA_VALUE2; + + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, dp, attr, 3), + DST_R_CRYPTOFAILURE); + + for (i = 0; i <= 2; i++) { + attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen); + if (attr[i].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr[i].pValue, 0, attr[i].ulValueLen); + } + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, dp, attr, 3), + DST_R_CRYPTOFAILURE); + pubTemplate[5].pValue = attr[0].pValue; + pubTemplate[5].ulValueLen = attr[0].ulValueLen; + pubTemplate[6].pValue = attr[1].pValue; + pubTemplate[6].ulValueLen = attr[1].ulValueLen; + pubTemplate[7].pValue = attr[2].pValue; + pubTemplate[7].ulValueLen = attr[2].ulValueLen; + + mech.mechanism = CKM_DSA_KEY_PAIR_GEN; + PK11_RET(pkcs_C_GenerateKeyPair, + (pk11_ctx->session, &mech, + pubTemplate, (CK_ULONG) 8, + privTemplate, (CK_ULONG) 7, + &pub, &priv), + DST_R_CRYPTOFAILURE); + + attr = dsa->repr; + attr += 3; + 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, dp); + 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: + pkcs11dsa_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 (dp != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, dp); + 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 +pkcs11dsa_isprivate(const dst_key_t *key) { + pk11_object_t *dsa = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (dsa == NULL) + return (false); + attr = pk11_attribute_bytype(dsa, CKA_VALUE2); + return (attr != NULL || dsa->ontoken); +} + +static void +pkcs11dsa_destroy(dst_key_t *key) { + pk11_object_t *dsa = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (dsa == NULL) + return; + + INSIST((dsa->object == CK_INVALID_HANDLE) || dsa->ontoken); + + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + case CKA_SUBPRIME: + case CKA_BASE: + case CKA_VALUE: + case CKA_VALUE2: + if (attr->pValue != NULL) { + isc_safe_memwipe(attr->pValue, + attr->ulValueLen); + isc_mem_put(key->mctx, + attr->pValue, + attr->ulValueLen); + } + break; + } + if (dsa->repr != NULL) { + isc_safe_memwipe(dsa->repr, dsa->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, + dsa->repr, + dsa->attrcnt * sizeof(*attr)); + } + isc_safe_memwipe(dsa, sizeof(*dsa)); + isc_mem_put(key->mctx, dsa, sizeof(*dsa)); + key->keydata.pkey = NULL; +} + + +static isc_result_t +pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *dsa; + CK_ATTRIBUTE *attr; + isc_region_t r; + int dnslen; + unsigned int t, p_bytes; + CK_ATTRIBUTE *prime = NULL, *subprime = NULL; + CK_ATTRIBUTE *base = NULL, *pub_key = NULL; + CK_BYTE *cp; + + REQUIRE(key->keydata.pkey != NULL); + + dsa = key->keydata.pkey; + + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + prime = attr; + break; + case CKA_SUBPRIME: + subprime = attr; + break; + case CKA_BASE: + base = attr; + break; + case CKA_VALUE: + pub_key = attr; + break; + } + REQUIRE((prime != NULL) && (subprime != NULL) && + (base != NULL) && (pub_key != NULL)); + + isc_buffer_availableregion(data, &r); + + t = (prime->ulValueLen - 64) / 8; + if (t > 8) + return (DST_R_INVALIDPUBLICKEY); + p_bytes = 64 + 8 * t; + + dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH; + if (r.length < (unsigned int) dnslen) + return (ISC_R_NOSPACE); + + memset(r.base, 0, dnslen); + *r.base = t; + isc_region_consume(&r, 1); + + cp = (CK_BYTE *) subprime->pValue; + memmove(r.base + ISC_SHA1_DIGESTLENGTH - subprime->ulValueLen, + cp, subprime->ulValueLen); + isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH); + cp = (CK_BYTE *) prime->pValue; + memmove(r.base + key->key_size/8 - prime->ulValueLen, + cp, prime->ulValueLen); + isc_region_consume(&r, p_bytes); + cp = (CK_BYTE *) base->pValue; + memmove(r.base + key->key_size/8 - base->ulValueLen, + cp, base->ulValueLen); + isc_region_consume(&r, p_bytes); + cp = (CK_BYTE *) pub_key->pValue; + memmove(r.base + key->key_size/8 - pub_key->ulValueLen, + cp, pub_key->ulValueLen); + isc_region_consume(&r, p_bytes); + + isc_buffer_add(data, dnslen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +pkcs11dsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *dsa; + isc_region_t r; + unsigned int t, p_bytes; + CK_BYTE *prime, *subprime, *base, *pub_key; + CK_ATTRIBUTE *attr; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa)); + if (dsa == NULL) + return (ISC_R_NOMEMORY); + memset(dsa, 0, sizeof(*dsa)); + + t = (unsigned int) *r.base; + isc_region_consume(&r, 1); + if (t > 8) { + isc_safe_memwipe(dsa, sizeof(*dsa)); + isc_mem_put(key->mctx, dsa, sizeof(*dsa)); + return (DST_R_INVALIDPUBLICKEY); + } + p_bytes = 64 + 8 * t; + + if (r.length < ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) { + isc_safe_memwipe(dsa, sizeof(*dsa)); + isc_mem_put(key->mctx, dsa, sizeof(*dsa)); + return (DST_R_INVALIDPUBLICKEY); + } + + subprime = r.base; + isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH); + + prime = r.base; + isc_region_consume(&r, p_bytes); + + base = r.base; + isc_region_consume(&r, p_bytes); + + pub_key = r.base; + isc_region_consume(&r, p_bytes); + + key->key_size = p_bytes * 8; + + isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes); + + dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4); + if (dsa->repr == NULL) + goto nomemory; + memset(dsa->repr, 0, sizeof(*attr) * 4); + dsa->attrcnt = 4; + + attr = dsa->repr; + attr[0].type = CKA_PRIME; + attr[0].pValue = isc_mem_get(key->mctx, p_bytes); + if (attr[0].pValue == NULL) + goto nomemory; + memmove(attr[0].pValue, prime, p_bytes); + attr[0].ulValueLen = p_bytes; + + attr[1].type = CKA_SUBPRIME; + attr[1].pValue = isc_mem_get(key->mctx, ISC_SHA1_DIGESTLENGTH); + if (attr[1].pValue == NULL) + goto nomemory; + memmove(attr[1].pValue, subprime, ISC_SHA1_DIGESTLENGTH); + attr[1].ulValueLen = ISC_SHA1_DIGESTLENGTH; + + attr[2].type = CKA_BASE; + attr[2].pValue = isc_mem_get(key->mctx, p_bytes); + if (attr[2].pValue == NULL) + goto nomemory; + memmove(attr[2].pValue, base, p_bytes); + attr[2].ulValueLen = p_bytes; + + attr[3].type = CKA_VALUE; + attr[3].pValue = isc_mem_get(key->mctx, p_bytes); + if (attr[3].pValue == NULL) + goto nomemory; + memmove(attr[3].pValue, pub_key, p_bytes); + attr[3].ulValueLen = p_bytes; + + key->keydata.pkey = dsa; + + return (ISC_R_SUCCESS); + + nomemory: + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + case CKA_SUBPRIME: + case CKA_BASE: + case CKA_VALUE: + if (attr->pValue != NULL) { + isc_safe_memwipe(attr->pValue, + attr->ulValueLen); + isc_mem_put(key->mctx, + attr->pValue, + attr->ulValueLen); + } + break; + } + if (dsa->repr != NULL) { + isc_safe_memwipe(dsa->repr, dsa->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, + dsa->repr, + dsa->attrcnt * sizeof(*attr)); + } + isc_safe_memwipe(dsa, sizeof(*dsa)); + isc_mem_put(key->mctx, dsa, sizeof(*dsa)); + return (ISC_R_NOMEMORY); +} + +static isc_result_t +pkcs11dsa_tofile(const dst_key_t *key, const char *directory) { + int cnt = 0; + pk11_object_t *dsa; + CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *prime = NULL, *subprime = NULL, *base = NULL; + CK_ATTRIBUTE *pub_key = NULL, *priv_key = NULL; + dst_private_t priv; + unsigned char bufs[5][128]; + + if (key->keydata.pkey == NULL) + return (DST_R_NULLKEY); + + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } + + dsa = key->keydata.pkey; + + for (attr = pk11_attribute_first(dsa); + attr != NULL; + attr = pk11_attribute_next(dsa, attr)) + switch (attr->type) { + case CKA_PRIME: + prime = attr; + break; + case CKA_SUBPRIME: + subprime = attr; + break; + case CKA_BASE: + base = attr; + break; + case CKA_VALUE: + pub_key = attr; + break; + case CKA_VALUE2: + priv_key = attr; + break; + } + if ((prime == NULL) || (subprime == NULL) || (base == NULL) || + (pub_key == NULL) || (priv_key ==NULL)) + return (DST_R_NULLKEY); + + priv.elements[cnt].tag = TAG_DSA_PRIME; + priv.elements[cnt].length = (unsigned short) prime->ulValueLen; + memmove(bufs[cnt], prime->pValue, prime->ulValueLen); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_SUBPRIME; + priv.elements[cnt].length = (unsigned short) subprime->ulValueLen; + memmove(bufs[cnt], subprime->pValue, subprime->ulValueLen); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_BASE; + priv.elements[cnt].length = (unsigned short) base->ulValueLen; + memmove(bufs[cnt], base->pValue, base->ulValueLen); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PRIVATE; + priv.elements[cnt].length = (unsigned short) priv_key->ulValueLen; + memmove(bufs[cnt], priv_key->pValue, priv_key->ulValueLen); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PUBLIC; + priv.elements[cnt].length = (unsigned short) pub_key->ulValueLen; + memmove(bufs[cnt], pub_key->pValue, pub_key->ulValueLen); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.nelements = cnt; + return (dst__privstruct_writefile(key, &priv, directory)); +} + +static isc_result_t +pkcs11dsa_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 *dsa = NULL; + CK_ATTRIBUTE *attr; + isc_mem_t *mctx = key->mctx; + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_DSA, 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); + } + + dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa)); + if (dsa == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dsa, 0, sizeof(*dsa)); + key->keydata.pkey = dsa; + + dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5); + if (dsa->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(dsa->repr, 0, sizeof(*attr) * 5); + dsa->attrcnt = 5; + attr = dsa->repr; + attr[0].type = CKA_PRIME; + attr[1].type = CKA_SUBPRIME; + attr[2].type = CKA_BASE; + attr[3].type = CKA_VALUE; + attr[4].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_DSA_PRIME: + attr = pk11_attribute_bytype(dsa, CKA_PRIME); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DSA_SUBPRIME: + attr = pk11_attribute_bytype(dsa, + CKA_SUBPRIME); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DSA_BASE: + attr = pk11_attribute_bytype(dsa, CKA_BASE); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DSA_PRIVATE: + attr = pk11_attribute_bytype(dsa, CKA_VALUE2); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + case TAG_DSA_PUBLIC: + attr = pk11_attribute_bytype(dsa, CKA_VALUE); + INSIST(attr != NULL); + attr->pValue = bn; + attr->ulValueLen = priv.elements[i].length; + break; + } + } + dst__privstruct_free(&priv, mctx); + + attr = pk11_attribute_bytype(dsa, CKA_PRIME); + INSIST(attr != NULL); + key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen); + + return (ISC_R_SUCCESS); + + err: + pkcs11dsa_destroy(key); + dst__privstruct_free(&priv, mctx); + isc_safe_memwipe(&priv, sizeof(priv)); + return (ret); +} + +static dst_func_t pkcs11dsa_functions = { + pkcs11dsa_createctx, + NULL, /*%< createctx2 */ + pkcs11dsa_destroyctx, + pkcs11dsa_adddata, + pkcs11dsa_sign, + pkcs11dsa_verify, + NULL, /*%< verify2 */ + NULL, /*%< computesecret */ + pkcs11dsa_compare, + NULL, /*%< paramcompare */ + pkcs11dsa_generate, + pkcs11dsa_isprivate, + pkcs11dsa_destroy, + pkcs11dsa_todns, + pkcs11dsa_fromdns, + pkcs11dsa_tofile, + pkcs11dsa_parse, + NULL, /*%< cleanup */ + NULL, /*%< fromlabel */ + NULL, /*%< dump */ + NULL, /*%< restore */ +}; + +isc_result_t +dst__pkcs11dsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &pkcs11dsa_functions; + return (ISC_R_SUCCESS); +} +#endif /* !PK11_DSA_DISABLE */ + +#else /* PKCS11CRYPTO */ + +#include <isc/util.h> + +EMPTY_TRANSLATION_UNIT + +#endif /* PKCS11CRYPTO */ +/*! \file */ |