1119418Sobrien/* 291398Stmm * Copyright (C) 2010-2014 Internet Systems Consortium, Inc. ("ISC") 3108832Stmm * 4174987Smarius * Permission to use, copy, modify, and/or distribute this software for any 591398Stmm * purpose with or without fee is hereby granted, provided that the above 691398Stmm * copyright notice and this permission notice appear in all copies. 791398Stmm * 891398Stmm * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 991398Stmm * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 1091398Stmm * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 1191398Stmm * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 1291398Stmm * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 1391398Stmm * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 1491398Stmm * PERFORMANCE OF THIS SOFTWARE. 1591398Stmm */ 1691398Stmm 1791398Stmm/* $Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp $ */ 1891398Stmm 1991398Stmm#include <config.h> 2091398Stmm 2191398Stmm#ifdef HAVE_OPENSSL_GOST 2291398Stmm 2391398Stmm#include <isc/entropy.h> 2491398Stmm#include <isc/mem.h> 2591398Stmm#include <isc/string.h> 2691398Stmm#include <isc/util.h> 2791398Stmm 2899726Sbenno#include <dst/result.h> 2991398Stmm 3091398Stmm#include "dst_internal.h" 31119418Sobrien#include "dst_openssl.h" 32119418Sobrien#include "dst_parse.h" 33119418Sobrien 3491398Stmm#include <openssl/err.h> 35172334Smarius#include <openssl/objects.h> 3691398Stmm#include <openssl/rsa.h> 3791398Stmm#include <openssl/engine.h> 38115030Stmm 3991398Stmmstatic ENGINE *e = NULL; 40115030Stmmstatic const EVP_MD *opensslgost_digest; 4191398Stmmextern const EVP_MD *EVP_gost(void); 42148368Smarius 43148368Smariusconst EVP_MD *EVP_gost(void) { 44148368Smarius return (opensslgost_digest); 45148368Smarius} 4691398Stmm 4791398Stmm#define DST_RET(a) {ret = a; goto err;} 4891398Stmm 4991398Stmmstatic isc_result_t opensslgost_todns(const dst_key_t *key, 5095533Smike isc_buffer_t *data); 5191398Stmm 5291398Stmmstatic isc_result_t 5391398Stmmopensslgost_createctx(dst_key_t *key, dst_context_t *dctx) { 54148369Smarius EVP_MD_CTX *evp_md_ctx; 55130026Sphk const EVP_MD *md = EVP_gost(); 56148369Smarius 5791398Stmm UNUSED(key); 5891398Stmm 59169269Sphk if (md == NULL) 6091398Stmm return (DST_R_OPENSSLFAILURE); 61105982Stmm 6291398Stmm evp_md_ctx = EVP_MD_CTX_create(); 6391398Stmm if (evp_md_ctx == NULL) 6491398Stmm return (ISC_R_NOMEMORY); 6591398Stmm 6691398Stmm if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) { 67147256Sbrooks EVP_MD_CTX_destroy(evp_md_ctx); 68149552Smarius return (ISC_R_FAILURE); 6991398Stmm } 70170273Syongari dctx->ctxdata.evp_md_ctx = evp_md_ctx; 71170273Syongari 72170273Syongari return (ISC_R_SUCCESS); 73170273Syongari} 74170273Syongari 75170273Syongaristatic void 7691398Stmmopensslgost_destroyctx(dst_context_t *dctx) { 7791398Stmm EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 7891398Stmm 7991398Stmm if (evp_md_ctx != NULL) { 8091398Stmm EVP_MD_CTX_destroy(evp_md_ctx); 81119355Simp dctx->ctxdata.evp_md_ctx = NULL; 82119355Simp } 8391398Stmm} 84172334Smarius 85172334Smariusstatic isc_result_t 86172334Smariusopensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) { 87194763Smarius EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 88172334Smarius 89170273Syongari if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) 90182060Smarius return (ISC_R_FAILURE); 91170273Syongari 92174987Smarius return (ISC_R_SUCCESS); 93174987Smarius} 94170273Syongari 95170273Syongaristatic isc_result_t 96170273Syongariopensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) { 9791398Stmm dst_key_t *key = dctx->key; 98174987Smarius isc_region_t r; 99177560Smarius unsigned int siglen = 0; 100177560Smarius EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 101174987Smarius EVP_PKEY *pkey = key->keydata.pkey; 102174987Smarius 103174987Smarius isc_buffer_availableregion(sig, &r); 104174987Smarius 105174987Smarius if (r.length < (unsigned int) EVP_PKEY_size(pkey)) 106174987Smarius return (ISC_R_NOSPACE); 107174987Smarius 108174987Smarius if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey)) 109174987Smarius return (ISC_R_FAILURE); 110174987Smarius 111174987Smarius isc_buffer_add(sig, siglen); 112174987Smarius 113174987Smarius return (ISC_R_SUCCESS); 114174987Smarius} 115172334Smarius 116174987Smariusstatic isc_result_t 117174987Smariusopensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) { 118174987Smarius dst_key_t *key = dctx->key; 119148368Smarius int status = 0; 120174987Smarius EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 121100587Sjake EVP_PKEY *pkey = key->keydata.pkey; 122194763Smarius 123174987Smarius status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey); 124174987Smarius switch (status) { 125174987Smarius case 1: 126174987Smarius return (ISC_R_SUCCESS); 127174987Smarius case 0: 128174987Smarius return (dst__openssl_toresult(DST_R_VERIFYFAILURE)); 129174987Smarius default: 130194763Smarius return (dst__openssl_toresult3(dctx->category, 131174987Smarius "EVP_VerifyFinal", 13291398Stmm DST_R_VERIFYFAILURE)); 13391398Stmm } 13491398Stmm} 13591398Stmm 13691398Stmmstatic isc_boolean_t 13791398Stmmopensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) { 13891398Stmm EVP_PKEY *pkey1, *pkey2; 13991398Stmm 14091398Stmm pkey1 = key1->keydata.pkey; 14191398Stmm pkey2 = key2->keydata.pkey; 142177560Smarius 143177560Smarius if (pkey1 == NULL && pkey2 == NULL) 144177560Smarius return (ISC_TRUE); 145177560Smarius else if (pkey1 == NULL || pkey2 == NULL) 146177560Smarius return (ISC_FALSE); 14791398Stmm 148174987Smarius if (EVP_PKEY_cmp(pkey1, pkey2) != 1) 14991398Stmm return (ISC_FALSE); 150174987Smarius return (ISC_TRUE); 151147256Sbrooks} 152174987Smarius 153174987Smariusstatic int 15491398Stmmprogress_cb(EVP_PKEY_CTX *ctx) 155194763Smarius{ 156194763Smarius union { 157194763Smarius void *dptr; 158194763Smarius void (*fptr)(int); 159147256Sbrooks } u; 160147256Sbrooks int p; 161147256Sbrooks 162194763Smarius u.dptr = EVP_PKEY_CTX_get_app_data(ctx); 163194763Smarius p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); 164194763Smarius if (u.fptr != NULL) 165194763Smarius u.fptr(p); 166194763Smarius return (1); 167194763Smarius} 168194763Smarius 169194763Smariusstatic isc_result_t 170194763Smariusopensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) { 171194763Smarius EVP_PKEY_CTX *ctx; 172194763Smarius union { 173147256Sbrooks void *dptr; 174150285Smarius void (*fptr)(int); 175150285Smarius } u; 176150285Smarius EVP_PKEY *pkey = NULL; 177150285Smarius isc_result_t ret; 178150285Smarius 17991398Stmm UNUSED(unused); 18091398Stmm ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL); 18191398Stmm if (ctx == NULL) 182161928Sjmg DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id", 183161928Sjmg DST_R_OPENSSLFAILURE)); 184174987Smarius if (callback != NULL) { 185174987Smarius u.fptr = callback; 186194763Smarius EVP_PKEY_CTX_set_app_data(ctx, u.dptr); 187147256Sbrooks EVP_PKEY_CTX_set_cb(ctx, &progress_cb); 18891398Stmm } 18991398Stmm if (EVP_PKEY_keygen_init(ctx) <= 0) 190170273Syongari DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init", 191170273Syongari DST_R_OPENSSLFAILURE)); 192194763Smarius if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0) 193108832Stmm DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str", 19491398Stmm DST_R_OPENSSLFAILURE)); 195108832Stmm if (EVP_PKEY_keygen(ctx, &pkey) <= 0) 196170273Syongari DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen", 197170273Syongari DST_R_OPENSSLFAILURE)); 198117126Sscottl key->keydata.pkey = pkey; 199194763Smarius EVP_PKEY_CTX_free(ctx); 200108832Stmm return (ISC_R_SUCCESS); 201108832Stmm 20291398Stmmerr: 203170273Syongari if (pkey != NULL) 20491398Stmm EVP_PKEY_free(pkey); 205170273Syongari if (ctx != NULL) 206170273Syongari EVP_PKEY_CTX_free(ctx); 207194763Smarius return (ret); 208108832Stmm} 20991398Stmm 21091398Stmmstatic isc_boolean_t 211174987Smariusopensslgost_isprivate(const dst_key_t *key) { 21291398Stmm EVP_PKEY *pkey = key->keydata.pkey; 21391398Stmm EC_KEY *ec; 21491398Stmm 215170273Syongari INSIST(pkey != NULL); 216170273Syongari 217194763Smarius ec = EVP_PKEY_get0(pkey); 218174987Smarius return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL)); 219174987Smarius} 220108832Stmm 22191398Stmmstatic void 22291398Stmmopensslgost_destroy(dst_key_t *key) { 22391398Stmm EVP_PKEY *pkey = key->keydata.pkey; 22491398Stmm 22591398Stmm EVP_PKEY_free(pkey); 22691398Stmm key->keydata.pkey = NULL; 227174987Smarius} 228174987Smarius 229174987Smariusunsigned char gost_prefix[37] = { 230108832Stmm 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 23191398Stmm 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 23291398Stmm 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 23391398Stmm 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 23491398Stmm 0x03, 0x43, 0x00, 0x04, 0x40 23591398Stmm}; 23691398Stmm 23791398Stmmstatic isc_result_t 23891398Stmmopensslgost_todns(const dst_key_t *key, isc_buffer_t *data) { 23991398Stmm EVP_PKEY *pkey; 24091398Stmm isc_region_t r; 24191398Stmm unsigned char der[37 + 64], *p; 24291398Stmm int len; 24391398Stmm 24491398Stmm REQUIRE(key->keydata.pkey != NULL); 24591398Stmm 24691398Stmm pkey = key->keydata.pkey; 247108832Stmm 24891398Stmm isc_buffer_availableregion(data, &r); 249174987Smarius if (r.length < 64) 250174987Smarius return (ISC_R_NOSPACE); 251174987Smarius 252108832Stmm p = der; 25391398Stmm len = i2d_PUBKEY(pkey, &p); 25491398Stmm INSIST(len == sizeof(der)); 25591398Stmm INSIST(memcmp(gost_prefix, der, 37) == 0); 25691398Stmm memmove(r.base, der + 37, 64); 25791398Stmm isc_buffer_add(data, 64); 25891398Stmm 25991398Stmm return (ISC_R_SUCCESS); 26091398Stmm} 261108832Stmm 26291398Stmmstatic isc_result_t 263174987Smariusopensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) { 264174987Smarius isc_region_t r; 265174987Smarius EVP_PKEY *pkey = NULL; 266108832Stmm unsigned char der[37 + 64]; 26791398Stmm const unsigned char *p; 26891398Stmm 26991398Stmm isc_buffer_remainingregion(data, &r); 27091398Stmm if (r.length == 0) 271172334Smarius return (ISC_R_SUCCESS); 272172334Smarius 273177560Smarius if (r.length != 64) 274172334Smarius return (DST_R_INVALIDPUBLICKEY); 275172334Smarius memmove(der, gost_prefix, 37); 27691398Stmm memmove(der + 37, r.base, 64); 27791398Stmm isc_buffer_forward(data, 64); 278172334Smarius 279172334Smarius p = der; 280172334Smarius if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL) 281172334Smarius return (dst__openssl_toresult2("d2i_PUBKEY", 282177560Smarius DST_R_OPENSSLFAILURE)); 283172334Smarius key->keydata.pkey = pkey; 284172334Smarius 285177560Smarius return (ISC_R_SUCCESS); 286172334Smarius} 287172334Smarius 288172334Smariusstatic isc_result_t 289172334Smariusopensslgost_tofile(const dst_key_t *key, const char *directory) { 290172334Smarius EVP_PKEY *pkey; 291172334Smarius dst_private_t priv; 292172334Smarius isc_result_t result; 293172334Smarius unsigned char *der, *p; 294172334Smarius int len; 295172334Smarius 296172334Smarius if (key->keydata.pkey == NULL) 297172334Smarius return (DST_R_NULLKEY); 298172334Smarius 299172334Smarius if (key->external) { 300172334Smarius priv.nelements = 0; 301172334Smarius return (dst__privstruct_writefile(key, &priv, directory)); 302172334Smarius } 303177560Smarius 304172334Smarius pkey = key->keydata.pkey; 305172334Smarius 306172334Smarius len = i2d_PrivateKey(pkey, NULL); 307172334Smarius der = isc_mem_get(key->mctx, (size_t) len); 308172334Smarius if (der == NULL) 309172334Smarius return (ISC_R_NOMEMORY); 310172334Smarius 311172334Smarius p = der; 312172334Smarius if (i2d_PrivateKey(pkey, &p) != len) { 313172334Smarius result = dst__openssl_toresult2("i2d_PrivateKey", 314172334Smarius DST_R_OPENSSLFAILURE); 315172334Smarius goto fail; 316172334Smarius } 317172334Smarius 318172334Smarius priv.elements[0].tag = TAG_GOST_PRIVASN1; 319172334Smarius priv.elements[0].length = len; 320172334Smarius priv.elements[0].data = der; 321172334Smarius priv.nelements = GOST_NTAGS; 322172334Smarius 323172334Smarius result = dst__privstruct_writefile(key, &priv, directory); 324177560Smarius fail: 325172334Smarius if (der != NULL) 326177560Smarius isc_mem_put(key->mctx, der, (size_t) len); 327172334Smarius return (result); 328177560Smarius} 329172334Smarius 330172334Smariusstatic isc_result_t 331172334Smariusopensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { 332172334Smarius dst_private_t priv; 333172334Smarius isc_result_t ret; 334172334Smarius isc_mem_t *mctx = key->mctx; 335172334Smarius EVP_PKEY *pkey = NULL; 336172334Smarius const unsigned char *p; 337108832Stmm 33891398Stmm UNUSED(pub); 33991398Stmm 34091398Stmm /* read private key file */ 34191398Stmm ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv); 34291398Stmm if (ret != ISC_R_SUCCESS) 34391398Stmm return (ret); 34491398Stmm 34591398Stmm if (key->external) { 34691398Stmm INSIST(priv.nelements == 0); 347176996Smarius if (pub == NULL) 34899726Sbenno DST_RET(DST_R_INVALIDPRIVATEKEY); 349177560Smarius key->keydata.pkey = pub->keydata.pkey; 35099726Sbenno pub->keydata.pkey = NULL; 351176996Smarius } else { 352177560Smarius INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1); 353128588Stmm p = priv.elements[0].data; 354128588Stmm if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p, 35599726Sbenno (long) priv.elements[0].length) == NULL) 35691398Stmm DST_RET(dst__openssl_toresult2("d2i_PrivateKey", 357147256Sbrooks DST_R_INVALIDPRIVATEKEY)); 35891398Stmm key->keydata.pkey = pkey; 35991398Stmm } 360170273Syongari key->key_size = EVP_PKEY_bits(pkey); 361149552Smarius dst__privstruct_free(&priv, mctx); 362149552Smarius memset(&priv, 0, sizeof(priv)); 363170273Syongari return (ISC_R_SUCCESS); 364170273Syongari 365170273Syongari err: 366149552Smarius if (pkey != NULL) 36791398Stmm EVP_PKEY_free(pkey); 36891398Stmm opensslgost_destroy(key); 36991398Stmm dst__privstruct_free(&priv, mctx); 37091398Stmm memset(&priv, 0, sizeof(priv)); 37191398Stmm return (ret); 37291398Stmm} 373174987Smarius 374174987Smariusstatic void 37591398Stmmopensslgost_cleanup(void) { 376108832Stmm if (e != NULL) { 37791398Stmm ENGINE_finish(e); 378174987Smarius ENGINE_free(e); 379174987Smarius e = NULL; 38091398Stmm } 381108832Stmm} 38291398Stmm 383108832Stmmstatic dst_func_t opensslgost_functions = { 384174987Smarius opensslgost_createctx, 38591398Stmm opensslgost_destroyctx, 38691398Stmm opensslgost_adddata, 387174987Smarius opensslgost_sign, 38891398Stmm opensslgost_verify, 389174987Smarius NULL, /*%< verify2 */ 390108832Stmm NULL, /*%< computesecret */ 391174987Smarius opensslgost_compare, 392108832Stmm NULL, /*%< paramcompare */ 393174987Smarius opensslgost_generate, 39491398Stmm opensslgost_isprivate, 395174987Smarius opensslgost_destroy, 396147256Sbrooks opensslgost_todns, 39791398Stmm opensslgost_fromdns, 39891398Stmm opensslgost_tofile, 39991398Stmm opensslgost_parse, 400108964Stmm opensslgost_cleanup, 401174987Smarius NULL, /*%< fromlabel */ 402108964Stmm NULL, /*%< dump */ 403147256Sbrooks NULL /*%< restore */ 404108964Stmm}; 405108964Stmm 406194886Smariusisc_result_t 407148369Smariusdst__opensslgost_init(dst_func_t **funcp) { 408147317Sbrooks isc_result_t ret; 409148369Smarius 410150285Smarius REQUIRE(funcp != NULL); 411150285Smarius 412150285Smarius /* check if the gost engine works properly */ 413150285Smarius e = ENGINE_by_id("gost"); 414147256Sbrooks if (e == NULL) 415108964Stmm return (dst__openssl_toresult2("ENGINE_by_id", 416108964Stmm DST_R_OPENSSLFAILURE)); 417174987Smarius if (ENGINE_init(e) <= 0) { 418108964Stmm ENGINE_free(e); 419108964Stmm e = NULL; 420108964Stmm return (dst__openssl_toresult2("ENGINE_init", 421174987Smarius DST_R_OPENSSLFAILURE)); 422108964Stmm } 423108964Stmm /* better than to rely on digest_gost symbol */ 424108964Stmm opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94); 425179925Smarius if (opensslgost_digest == NULL) 426108964Stmm DST_RET(dst__openssl_toresult2("ENGINE_get_digest", 427108964Stmm DST_R_OPENSSLFAILURE)); 428108964Stmm /* from openssl.cnf */ 429108964Stmm if (ENGINE_register_pkey_asn1_meths(e) <= 0) 430108964Stmm DST_RET(dst__openssl_toresult2( 431108964Stmm "ENGINE_register_pkey_asn1_meths", 432108964Stmm DST_R_OPENSSLFAILURE)); 433108964Stmm if (ENGINE_ctrl_cmd_string(e, 434108964Stmm "CRYPT_PARAMS", 435108964Stmm "id-Gost28147-89-CryptoPro-A-ParamSet", 436174987Smarius 0) <= 0) 437108964Stmm DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string", 438147256Sbrooks DST_R_OPENSSLFAILURE)); 439108964Stmm 440148369Smarius if (*funcp == NULL) 441108964Stmm *funcp = &opensslgost_functions; 442148369Smarius return (ISC_R_SUCCESS); 443108964Stmm 444108964Stmm err: 445108964Stmm ENGINE_finish(e); 446174987Smarius ENGINE_free(e); 447108964Stmm e = NULL; 448147256Sbrooks return (ret); 449108964Stmm} 450148369Smarius 451149552Smarius#else /* HAVE_OPENSSL_GOST */ 452149552Smarius 453149552Smarius#include <isc/util.h> 454149552Smarius 455172334SmariusEMPTY_TRANSLATION_UNIT 456108964Stmm 457148369Smarius#endif /* HAVE_OPENSSL_GOST */ 458148369Smarius/*! \file */ 459108964Stmm