/* * 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. */ #include #if defined(PKCS11CRYPTO) && defined(HAVE_PKCS11_GOST) #include #include #include #include #include #include #include #include #include #include #include "dst_internal.h" #include "dst_parse.h" #include "dst_pkcs11.h" #include "dst_gost.h" #include #include #define WANT_GOST_PARAMS #include #include /* * RU CryptoPro GOST keys: * mechanisms: * CKM_GOSTR3411 * CKM_GOSTR3410_WITH_GOSTR3411 * CKM_GOSTR3410_KEY_PAIR_GEN * domain parameters: * CKA_GOSTR3410_PARAMS (fixed BER OID 1.2.643.2.2.35.1) * CKA_GOSTR3411_PARAMS (fixed BER OID 1.2.643.2.2.30.1) * CKA_GOST28147_PARAMS (optional, don't use) * public keys: * object class CKO_PUBLIC_KEY * key type CKK_GOSTR3410 * attribute CKA_VALUE (point Q) * attribute CKA_GOSTR3410_PARAMS * attribute CKA_GOSTR3411_PARAMS * attribute CKA_GOST28147_PARAMS * private keys: * object class CKO_PRIVATE_KEY * key type CKK_GOSTR3410 * attribute CKA_VALUE (big int d) * attribute CKA_GOSTR3410_PARAMS * attribute CKA_GOSTR3411_PARAMS * attribute CKA_GOST28147_PARAMS * point format: (little endian) */ #define CKA_VALUE2 CKA_PRIVATE_EXPONENT #define ISC_GOST_SIGNATURELENGTH 64 #define ISC_GOST_PUBKEYLENGTH 64 #define ISC_GOST_KEYSIZE 256 /* HASH methods */ isc_result_t isc_gost_init(isc_gost_t *ctx) { CK_RV rv; CK_MECHANISM mech = { CKM_GOSTR3411, NULL, 0 }; int ret = ISC_R_SUCCESS; ret = pk11_get_session(ctx, OP_GOST, true, false, false, NULL, 0); if (ret != ISC_R_SUCCESS) return (ret); PK11_CALL(pkcs_C_DigestInit, (ctx->session, &mech), ISC_R_FAILURE); return (ret); } void isc_gost_invalidate(isc_gost_t *ctx) { CK_BYTE garbage[ISC_GOST_DIGESTLENGTH]; CK_ULONG len = ISC_GOST_DIGESTLENGTH; if (ctx->handle == NULL) return; (void) pkcs_C_DigestFinal(ctx->session, garbage, &len); isc_safe_memwipe(garbage, sizeof(garbage)); pk11_return_session(ctx); } isc_result_t isc_gost_update(isc_gost_t *ctx, const unsigned char *buf, unsigned int len) { CK_RV rv; CK_BYTE_PTR pPart; int ret = ISC_R_SUCCESS; DE_CONST(buf, pPart); PK11_CALL(pkcs_C_DigestUpdate, (ctx->session, pPart, (CK_ULONG) len), ISC_R_FAILURE); return (ret); } isc_result_t isc_gost_final(isc_gost_t *ctx, unsigned char *digest) { CK_RV rv; CK_ULONG len = ISC_GOST_DIGESTLENGTH; int ret = ISC_R_SUCCESS; PK11_CALL(pkcs_C_DigestFinal, (ctx->session, (CK_BYTE_PTR) digest, &len), ISC_R_FAILURE); pk11_return_session(ctx); return (ret); } /* DST methods */ static CK_BBOOL truevalue = TRUE; static CK_BBOOL falsevalue = FALSE; #define DST_RET(a) {ret = a; goto err;} static isc_result_t pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data); static void pkcs11gost_destroy(dst_key_t *key); static isc_result_t pkcs11gost_createctx_sign(dst_key_t *key, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 }; CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_GOSTR3410; 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_VALUE, NULL, 0 }, { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset, (CK_ULONG) sizeof(pk11_gost_a_paramset) }, { CKA_GOSTR3411_PARAMS, pk11_gost_paramset, (CK_ULONG) sizeof(pk11_gost_paramset) } }; CK_ATTRIBUTE *attr; pk11_object_t *gost; pk11_context_t *pk11_ctx; isc_result_t ret; unsigned int i; REQUIRE(key != NULL); gost = key->keydata.pkey; REQUIRE(gost != 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_GOST, true, false, gost->reqlogon, NULL, pk11_get_best_token(OP_GOST)); if (ret != ISC_R_SUCCESS) goto err; if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) { pk11_ctx->ontoken = gost->ontoken; pk11_ctx->object = gost->object; goto token_key; } for (attr = pk11_attribute_first(gost); attr != NULL; attr = pk11_attribute_next(gost, attr)) switch (attr->type) { case CKA_VALUE2: INSIST(keyTemplate[6].type == CKA_VALUE); 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; } 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_SignInit, (pk11_ctx->session, &mech, pk11_ctx->object), ISC_R_FAILURE); dctx->ctxdata.pk11_ctx = pk11_ctx; for (i = 6; i <= 6; i++) if (keyTemplate[i].pValue != NULL) { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } return (ISC_R_SUCCESS); err: if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE)) (void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object); for (i = 6; i <= 6; i++) if (keyTemplate[i].pValue != NULL) { isc_safe_memwipe(keyTemplate[i].pValue, keyTemplate[i].ulValueLen); isc_mem_put(dctx->mctx, keyTemplate[i].pValue, keyTemplate[i].ulValueLen); } pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static isc_result_t pkcs11gost_createctx_verify(dst_key_t *key, dst_context_t *dctx) { CK_RV rv; CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 }; CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; CK_KEY_TYPE keyType = CKK_GOSTR3410; 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_VALUE, NULL, 0 }, { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset, (CK_ULONG) sizeof(pk11_gost_a_paramset) }, { CKA_GOSTR3411_PARAMS, pk11_gost_paramset, (CK_ULONG) sizeof(pk11_gost_paramset) } }; CK_ATTRIBUTE *attr; pk11_object_t *gost; pk11_context_t *pk11_ctx; isc_result_t ret; unsigned int i; REQUIRE(key != NULL); gost = key->keydata.pkey; REQUIRE(gost != 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_GOST, true, false, gost->reqlogon, NULL, pk11_get_best_token(OP_GOST)); if (ret != ISC_R_SUCCESS) goto err; if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) { pk11_ctx->ontoken = gost->ontoken; pk11_ctx->object = gost->object; goto token_key; } for (attr = pk11_attribute_first(gost); attr != NULL; attr = pk11_attribute_next(gost, attr)) switch (attr->type) { case CKA_VALUE: 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; } pk11_ctx->object = CK_INVALID_HANDLE; pk11_ctx->ontoken = false; PK11_RET(pkcs_C_CreateObject, (pk11_ctx->session, keyTemplate, (CK_ULONG) 8, &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 <= 5; 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 <= 5; 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 pkcs11gost_createctx(dst_key_t *key, dst_context_t *dctx) { if (dctx->use == DO_SIGN) return (pkcs11gost_createctx_sign(key, dctx)); else return (pkcs11gost_createctx_verify(key, dctx)); } static void pkcs11gost_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 pkcs11gost_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 pkcs11gost_sign(dst_context_t *dctx, isc_buffer_t *sig) { CK_RV rv; CK_ULONG siglen = ISC_GOST_SIGNATURELENGTH; isc_region_t r; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; isc_buffer_availableregion(sig, &r); if (r.length < ISC_GOST_SIGNATURELENGTH) return (ISC_R_NOSPACE); PK11_RET(pkcs_C_SignFinal, (pk11_ctx->session, (CK_BYTE_PTR) r.base, &siglen), DST_R_SIGNFAILURE); if (siglen != ISC_GOST_SIGNATURELENGTH) return (DST_R_SIGNFAILURE); isc_buffer_add(sig, ISC_GOST_SIGNATURELENGTH); err: return (ret); } static isc_result_t pkcs11gost_verify(dst_context_t *dctx, const isc_region_t *sig) { CK_RV rv; pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx; isc_result_t ret = ISC_R_SUCCESS; PK11_CALL(pkcs_C_VerifyFinal, (pk11_ctx->session, (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length), DST_R_VERIFYFAILURE); return (ret); } static bool pkcs11gost_compare(const dst_key_t *key1, const dst_key_t *key2) { pk11_object_t *gost1, *gost2; CK_ATTRIBUTE *attr1, *attr2; gost1 = key1->keydata.pkey; gost2 = key2->keydata.pkey; if ((gost1 == NULL) && (gost2 == NULL)) return (true); else if ((gost1 == NULL) || (gost2 == NULL)) return (false); attr1 = pk11_attribute_bytype(gost1, CKA_VALUE); attr2 = pk11_attribute_bytype(gost2, 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(gost1, CKA_VALUE2); attr2 = pk11_attribute_bytype(gost2, 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 (!gost1->ontoken && !gost2->ontoken) return (true); else if (gost1->ontoken || gost2->ontoken || (gost1->object != gost2->object)) return (false); return (true); } static isc_result_t pkcs11gost_generate(dst_key_t *key, int unused, void (*callback)(int)) { CK_RV rv; CK_MECHANISM mech = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 }; CK_KEY_TYPE keyType = CKK_GOSTR3410; 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_GOSTR3410_PARAMS, pk11_gost_a_paramset, (CK_ULONG) sizeof(pk11_gost_a_paramset) }, { CKA_GOSTR3411_PARAMS, pk11_gost_paramset, (CK_ULONG) sizeof(pk11_gost_paramset) } }; 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 *gost; pk11_context_t *pk11_ctx; isc_result_t ret; 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_GOST, true, false, false, NULL, pk11_get_best_token(OP_GOST)); if (ret != ISC_R_SUCCESS) goto err; PK11_RET(pkcs_C_GenerateKeyPair, (pk11_ctx->session, &mech, pubTemplate, (CK_ULONG) 7, privTemplate, (CK_ULONG) 7, &pub, &priv), DST_R_CRYPTOFAILURE); gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost)); if (gost == NULL) DST_RET(ISC_R_NOMEMORY); memset(gost, 0, sizeof(*gost)); key->keydata.pkey = gost; key->key_size = ISC_GOST_KEYSIZE; gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2); if (gost->repr == NULL) DST_RET(ISC_R_NOMEMORY); memset(gost->repr, 0, sizeof(*attr) * 2); gost->attrcnt = 2; attr = gost->repr; attr[0].type = CKA_VALUE; attr[1].type = CKA_VALUE2; attr = gost->repr; 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); 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: pkcs11gost_destroy(key); if (priv != CK_INVALID_HANDLE) (void) pkcs_C_DestroyObject(pk11_ctx->session, priv); if (pub != CK_INVALID_HANDLE) (void) pkcs_C_DestroyObject(pk11_ctx->session, pub); pk11_return_session(pk11_ctx); isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx)); isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); return (ret); } static bool pkcs11gost_isprivate(const dst_key_t *key) { pk11_object_t *gost = key->keydata.pkey; CK_ATTRIBUTE *attr; if (gost == NULL) return (false); attr = pk11_attribute_bytype(gost, CKA_VALUE2); return (attr != NULL || gost->ontoken); } static void pkcs11gost_destroy(dst_key_t *key) { pk11_object_t *gost = key->keydata.pkey; CK_ATTRIBUTE *attr; if (gost == NULL) return; INSIST((gost->object == CK_INVALID_HANDLE) || gost->ontoken); for (attr = pk11_attribute_first(gost); attr != NULL; attr = pk11_attribute_next(gost, attr)) switch (attr->type) { 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 (gost->repr != NULL) { isc_safe_memwipe(gost->repr, gost->attrcnt * sizeof(*attr)); isc_mem_put(key->mctx, gost->repr, gost->attrcnt * sizeof(*attr)); } isc_safe_memwipe(gost, sizeof(*gost)); isc_mem_put(key->mctx, gost, sizeof(*gost)); key->keydata.pkey = NULL; } static isc_result_t pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data) { pk11_object_t *gost; isc_region_t r; CK_ATTRIBUTE *attr; REQUIRE(key->keydata.pkey != NULL); gost = key->keydata.pkey; attr = pk11_attribute_bytype(gost, CKA_VALUE); if ((attr == NULL) || (attr->ulValueLen != ISC_GOST_PUBKEYLENGTH)) return (ISC_R_FAILURE); isc_buffer_availableregion(data, &r); if (r.length < ISC_GOST_PUBKEYLENGTH) return (ISC_R_NOSPACE); memmove(r.base, (CK_BYTE_PTR) attr->pValue, ISC_GOST_PUBKEYLENGTH); isc_buffer_add(data, ISC_GOST_PUBKEYLENGTH); return (ISC_R_SUCCESS); } static isc_result_t pkcs11gost_fromdns(dst_key_t *key, isc_buffer_t *data) { pk11_object_t *gost; isc_region_t r; CK_ATTRIBUTE *attr; isc_buffer_remainingregion(data, &r); if (r.length == 0) return (ISC_R_SUCCESS); if (r.length != ISC_GOST_PUBKEYLENGTH) return (DST_R_INVALIDPUBLICKEY); gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost)); if (gost == NULL) return (ISC_R_NOMEMORY); memset(gost, 0, sizeof(*gost)); gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr)); if (gost->repr == NULL) goto nomemory; gost->attrcnt = 1; attr = gost->repr; attr->type = CKA_VALUE; attr->pValue = isc_mem_get(key->mctx, ISC_GOST_PUBKEYLENGTH); if (attr->pValue == NULL) goto nomemory; memmove((CK_BYTE_PTR) attr->pValue, r.base, ISC_GOST_PUBKEYLENGTH); attr->ulValueLen = ISC_GOST_PUBKEYLENGTH; isc_buffer_forward(data, ISC_GOST_PUBKEYLENGTH); key->keydata.pkey = gost; key->key_size = ISC_GOST_KEYSIZE; return (ISC_R_SUCCESS); nomemory: for (attr = pk11_attribute_first(gost); attr != NULL; attr = pk11_attribute_next(gost, attr)) switch (attr->type) { 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 (gost->repr != NULL) { isc_safe_memwipe(gost->repr, gost->attrcnt * sizeof(*attr)); isc_mem_put(key->mctx, gost->repr, gost->attrcnt * sizeof(*attr)); } isc_safe_memwipe(gost, sizeof(*gost)); isc_mem_put(key->mctx, gost, sizeof(*gost)); return (ISC_R_NOMEMORY); } static unsigned char gost_private_der[39] = { 0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20 }; #ifdef PREFER_GOSTASN1 static isc_result_t pkcs11gost_tofile(const dst_key_t *key, const char *directory) { isc_result_t ret; pk11_object_t *gost; dst_private_t priv; unsigned char *buf = NULL; unsigned int i = 0; CK_ATTRIBUTE *attr; int adj; if (key->keydata.pkey == NULL) return (DST_R_NULLKEY); if (key->external) { priv.nelements = 0; return (dst__privstruct_writefile(key, &priv, directory)); } gost = key->keydata.pkey; attr = pk11_attribute_bytype(gost, CKA_VALUE2); if (attr != NULL) { buf = isc_mem_get(key->mctx, attr->ulValueLen + 39); if (buf == NULL) return (ISC_R_NOMEMORY); priv.elements[i].tag = TAG_GOST_PRIVASN1; priv.elements[i].length = (unsigned short) attr->ulValueLen + 39; memmove(buf, gost_private_der, 39); memmove(buf + 39, attr->pValue, attr->ulValueLen); adj = (int) attr->ulValueLen - 32; if (adj != 0) { buf[1] += adj; buf[36] += adj; buf[38] += adj; } priv.elements[i].data = buf; i++; } else return (DST_R_CRYPTOFAILURE); priv.nelements = i; ret = dst__privstruct_writefile(key, &priv, directory); if (buf != NULL) { isc_safe_memwipe(buf, attr->ulValueLen); isc_mem_put(key->mctx, buf, attr->ulValueLen); } return (ret); } #else static isc_result_t pkcs11gost_tofile(const dst_key_t *key, const char *directory) { isc_result_t ret; pk11_object_t *gost; 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)); } gost = key->keydata.pkey; attr = pk11_attribute_bytype(gost, CKA_VALUE2); if (attr != NULL) { buf = isc_mem_get(key->mctx, attr->ulValueLen); if (buf == NULL) return (ISC_R_NOMEMORY); priv.elements[i].tag = TAG_GOST_PRIVRAW; priv.elements[i].length = (unsigned short) attr->ulValueLen; memmove(buf, attr->pValue, attr->ulValueLen); priv.elements[i].data = buf; i++; } else return (DST_R_CRYPTOFAILURE); priv.nelements = i; ret = dst__privstruct_writefile(key, &priv, directory); if (buf != NULL) { isc_safe_memwipe(buf, attr->ulValueLen); isc_mem_put(key->mctx, buf, attr->ulValueLen); } return (ret); } #endif static isc_result_t pkcs11gost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t ret; pk11_object_t *gost = NULL; CK_ATTRIBUTE *attr, *pattr; isc_mem_t *mctx = key->mctx; 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); isc_safe_memwipe(&priv, sizeof(priv)); return (ISC_R_SUCCESS); } if (priv.elements[0].tag == TAG_GOST_PRIVASN1) { int adj = (int) priv.elements[0].length - (39 + 32); unsigned char buf[39]; if ((adj > 0) || (adj < -31)) DST_RET(DST_R_INVALIDPRIVATEKEY); memmove(buf, gost_private_der, 39); if (adj != 0) { buf[1] += adj; buf[36] += adj; buf[38] += adj; } if (!isc_safe_memequal(priv.elements[0].data, buf, 39)) DST_RET(DST_R_INVALIDPRIVATEKEY); priv.elements[0].tag = TAG_GOST_PRIVRAW; priv.elements[0].length -= 39; memmove(priv.elements[0].data, priv.elements[0].data + 39, 32 + adj); } gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost)); if (gost == NULL) DST_RET(ISC_R_NOMEMORY); memset(gost, 0, sizeof(*gost)); key->keydata.pkey = gost; key->key_size = ISC_GOST_KEYSIZE; gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2); if (gost->repr == NULL) DST_RET(ISC_R_NOMEMORY); memset(gost->repr, 0, sizeof(*attr) * 2); gost->attrcnt = 2; attr = gost->repr; attr->type = CKA_VALUE; pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE); INSIST(pattr != NULL); attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen); if (attr->pValue == NULL) DST_RET(ISC_R_NOMEMORY); memmove(attr->pValue, pattr->pValue, pattr->ulValueLen); attr->ulValueLen = pattr->ulValueLen; attr++; attr->type = CKA_VALUE2; attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length); if (attr->pValue == NULL) DST_RET(ISC_R_NOMEMORY); memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length); attr->ulValueLen = priv.elements[0].length; dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ISC_R_SUCCESS); err: pkcs11gost_destroy(key); dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); return (ret); } static dst_func_t pkcs11gost_functions = { pkcs11gost_createctx, NULL, /*%< createctx2 */ pkcs11gost_destroyctx, pkcs11gost_adddata, pkcs11gost_sign, pkcs11gost_verify, NULL, /*%< verify2 */ NULL, /*%< computesecret */ pkcs11gost_compare, NULL, /*%< paramcompare */ pkcs11gost_generate, pkcs11gost_isprivate, pkcs11gost_destroy, pkcs11gost_todns, pkcs11gost_fromdns, pkcs11gost_tofile, pkcs11gost_parse, NULL, /*%< cleanup */ NULL, /*%< fromlabel */ NULL, /*%< dump */ NULL, /*%< restore */ }; isc_result_t dst__pkcs11gost_init(dst_func_t **funcp) { REQUIRE(funcp != NULL); if (*funcp == NULL) *funcp = &pkcs11gost_functions; return (ISC_R_SUCCESS); } #else /* PKCS11CRYPTO && HAVE_PKCS11_GOST */ #include EMPTY_TRANSLATION_UNIT #endif /* PKCS11CRYPTO && HAVE_PKCS11_GOST */ /*! \file */