1/* 2 * Copyright (C) 2010-2014 Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* $Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp $ */ 18 19#include <config.h> 20 21#ifdef HAVE_OPENSSL_GOST 22 23#include <isc/entropy.h> 24#include <isc/mem.h> 25#include <isc/string.h> 26#include <isc/util.h> 27 28#include <dst/result.h> 29 30#include "dst_internal.h" 31#include "dst_openssl.h" 32#include "dst_parse.h" 33 34#include <openssl/err.h> 35#include <openssl/objects.h> 36#include <openssl/rsa.h> 37#include <openssl/engine.h> 38 39static ENGINE *e = NULL; 40static const EVP_MD *opensslgost_digest; 41extern const EVP_MD *EVP_gost(void); 42 43const EVP_MD *EVP_gost(void) { 44 return (opensslgost_digest); 45} 46 47#define DST_RET(a) {ret = a; goto err;} 48 49static isc_result_t opensslgost_todns(const dst_key_t *key, 50 isc_buffer_t *data); 51 52static isc_result_t 53opensslgost_createctx(dst_key_t *key, dst_context_t *dctx) { 54 EVP_MD_CTX *evp_md_ctx; 55 const EVP_MD *md = EVP_gost(); 56 57 UNUSED(key); 58 59 if (md == NULL) 60 return (DST_R_OPENSSLFAILURE); 61 62 evp_md_ctx = EVP_MD_CTX_create(); 63 if (evp_md_ctx == NULL) 64 return (ISC_R_NOMEMORY); 65 66 if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) { 67 EVP_MD_CTX_destroy(evp_md_ctx); 68 return (ISC_R_FAILURE); 69 } 70 dctx->ctxdata.evp_md_ctx = evp_md_ctx; 71 72 return (ISC_R_SUCCESS); 73} 74 75static void 76opensslgost_destroyctx(dst_context_t *dctx) { 77 EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 78 79 if (evp_md_ctx != NULL) { 80 EVP_MD_CTX_destroy(evp_md_ctx); 81 dctx->ctxdata.evp_md_ctx = NULL; 82 } 83} 84 85static isc_result_t 86opensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) { 87 EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 88 89 if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) 90 return (ISC_R_FAILURE); 91 92 return (ISC_R_SUCCESS); 93} 94 95static isc_result_t 96opensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) { 97 dst_key_t *key = dctx->key; 98 isc_region_t r; 99 unsigned int siglen = 0; 100 EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 101 EVP_PKEY *pkey = key->keydata.pkey; 102 103 isc_buffer_availableregion(sig, &r); 104 105 if (r.length < (unsigned int) EVP_PKEY_size(pkey)) 106 return (ISC_R_NOSPACE); 107 108 if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey)) 109 return (ISC_R_FAILURE); 110 111 isc_buffer_add(sig, siglen); 112 113 return (ISC_R_SUCCESS); 114} 115 116static isc_result_t 117opensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) { 118 dst_key_t *key = dctx->key; 119 int status = 0; 120 EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; 121 EVP_PKEY *pkey = key->keydata.pkey; 122 123 status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey); 124 switch (status) { 125 case 1: 126 return (ISC_R_SUCCESS); 127 case 0: 128 return (dst__openssl_toresult(DST_R_VERIFYFAILURE)); 129 default: 130 return (dst__openssl_toresult3(dctx->category, 131 "EVP_VerifyFinal", 132 DST_R_VERIFYFAILURE)); 133 } 134} 135 136static isc_boolean_t 137opensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) { 138 EVP_PKEY *pkey1, *pkey2; 139 140 pkey1 = key1->keydata.pkey; 141 pkey2 = key2->keydata.pkey; 142 143 if (pkey1 == NULL && pkey2 == NULL) 144 return (ISC_TRUE); 145 else if (pkey1 == NULL || pkey2 == NULL) 146 return (ISC_FALSE); 147 148 if (EVP_PKEY_cmp(pkey1, pkey2) != 1) 149 return (ISC_FALSE); 150 return (ISC_TRUE); 151} 152 153static int 154progress_cb(EVP_PKEY_CTX *ctx) 155{ 156 union { 157 void *dptr; 158 void (*fptr)(int); 159 } u; 160 int p; 161 162 u.dptr = EVP_PKEY_CTX_get_app_data(ctx); 163 p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); 164 if (u.fptr != NULL) 165 u.fptr(p); 166 return (1); 167} 168 169static isc_result_t 170opensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) { 171 EVP_PKEY_CTX *ctx; 172 union { 173 void *dptr; 174 void (*fptr)(int); 175 } u; 176 EVP_PKEY *pkey = NULL; 177 isc_result_t ret; 178 179 UNUSED(unused); 180 ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL); 181 if (ctx == NULL) 182 DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id", 183 DST_R_OPENSSLFAILURE)); 184 if (callback != NULL) { 185 u.fptr = callback; 186 EVP_PKEY_CTX_set_app_data(ctx, u.dptr); 187 EVP_PKEY_CTX_set_cb(ctx, &progress_cb); 188 } 189 if (EVP_PKEY_keygen_init(ctx) <= 0) 190 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init", 191 DST_R_OPENSSLFAILURE)); 192 if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0) 193 DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str", 194 DST_R_OPENSSLFAILURE)); 195 if (EVP_PKEY_keygen(ctx, &pkey) <= 0) 196 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen", 197 DST_R_OPENSSLFAILURE)); 198 key->keydata.pkey = pkey; 199 EVP_PKEY_CTX_free(ctx); 200 return (ISC_R_SUCCESS); 201 202err: 203 if (pkey != NULL) 204 EVP_PKEY_free(pkey); 205 if (ctx != NULL) 206 EVP_PKEY_CTX_free(ctx); 207 return (ret); 208} 209 210static isc_boolean_t 211opensslgost_isprivate(const dst_key_t *key) { 212 EVP_PKEY *pkey = key->keydata.pkey; 213 EC_KEY *ec; 214 215 INSIST(pkey != NULL); 216 217 ec = EVP_PKEY_get0(pkey); 218 return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL)); 219} 220 221static void 222opensslgost_destroy(dst_key_t *key) { 223 EVP_PKEY *pkey = key->keydata.pkey; 224 225 EVP_PKEY_free(pkey); 226 key->keydata.pkey = NULL; 227} 228 229unsigned char gost_prefix[37] = { 230 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 231 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 232 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 233 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 234 0x03, 0x43, 0x00, 0x04, 0x40 235}; 236 237static isc_result_t 238opensslgost_todns(const dst_key_t *key, isc_buffer_t *data) { 239 EVP_PKEY *pkey; 240 isc_region_t r; 241 unsigned char der[37 + 64], *p; 242 int len; 243 244 REQUIRE(key->keydata.pkey != NULL); 245 246 pkey = key->keydata.pkey; 247 248 isc_buffer_availableregion(data, &r); 249 if (r.length < 64) 250 return (ISC_R_NOSPACE); 251 252 p = der; 253 len = i2d_PUBKEY(pkey, &p); 254 INSIST(len == sizeof(der)); 255 INSIST(memcmp(gost_prefix, der, 37) == 0); 256 memmove(r.base, der + 37, 64); 257 isc_buffer_add(data, 64); 258 259 return (ISC_R_SUCCESS); 260} 261 262static isc_result_t 263opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) { 264 isc_region_t r; 265 EVP_PKEY *pkey = NULL; 266 unsigned char der[37 + 64]; 267 const unsigned char *p; 268 269 isc_buffer_remainingregion(data, &r); 270 if (r.length == 0) 271 return (ISC_R_SUCCESS); 272 273 if (r.length != 64) 274 return (DST_R_INVALIDPUBLICKEY); 275 memmove(der, gost_prefix, 37); 276 memmove(der + 37, r.base, 64); 277 isc_buffer_forward(data, 64); 278 279 p = der; 280 if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL) 281 return (dst__openssl_toresult2("d2i_PUBKEY", 282 DST_R_OPENSSLFAILURE)); 283 key->keydata.pkey = pkey; 284 285 return (ISC_R_SUCCESS); 286} 287 288static isc_result_t 289opensslgost_tofile(const dst_key_t *key, const char *directory) { 290 EVP_PKEY *pkey; 291 dst_private_t priv; 292 isc_result_t result; 293 unsigned char *der, *p; 294 int len; 295 296 if (key->keydata.pkey == NULL) 297 return (DST_R_NULLKEY); 298 299 if (key->external) { 300 priv.nelements = 0; 301 return (dst__privstruct_writefile(key, &priv, directory)); 302 } 303 304 pkey = key->keydata.pkey; 305 306 len = i2d_PrivateKey(pkey, NULL); 307 der = isc_mem_get(key->mctx, (size_t) len); 308 if (der == NULL) 309 return (ISC_R_NOMEMORY); 310 311 p = der; 312 if (i2d_PrivateKey(pkey, &p) != len) { 313 result = dst__openssl_toresult2("i2d_PrivateKey", 314 DST_R_OPENSSLFAILURE); 315 goto fail; 316 } 317 318 priv.elements[0].tag = TAG_GOST_PRIVASN1; 319 priv.elements[0].length = len; 320 priv.elements[0].data = der; 321 priv.nelements = GOST_NTAGS; 322 323 result = dst__privstruct_writefile(key, &priv, directory); 324 fail: 325 if (der != NULL) 326 isc_mem_put(key->mctx, der, (size_t) len); 327 return (result); 328} 329 330static isc_result_t 331opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { 332 dst_private_t priv; 333 isc_result_t ret; 334 isc_mem_t *mctx = key->mctx; 335 EVP_PKEY *pkey = NULL; 336 const unsigned char *p; 337 338 UNUSED(pub); 339 340 /* read private key file */ 341 ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv); 342 if (ret != ISC_R_SUCCESS) 343 return (ret); 344 345 if (key->external) { 346 INSIST(priv.nelements == 0); 347 if (pub == NULL) 348 DST_RET(DST_R_INVALIDPRIVATEKEY); 349 key->keydata.pkey = pub->keydata.pkey; 350 pub->keydata.pkey = NULL; 351 } else { 352 INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1); 353 p = priv.elements[0].data; 354 if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p, 355 (long) priv.elements[0].length) == NULL) 356 DST_RET(dst__openssl_toresult2("d2i_PrivateKey", 357 DST_R_INVALIDPRIVATEKEY)); 358 key->keydata.pkey = pkey; 359 } 360 key->key_size = EVP_PKEY_bits(pkey); 361 dst__privstruct_free(&priv, mctx); 362 memset(&priv, 0, sizeof(priv)); 363 return (ISC_R_SUCCESS); 364 365 err: 366 if (pkey != NULL) 367 EVP_PKEY_free(pkey); 368 opensslgost_destroy(key); 369 dst__privstruct_free(&priv, mctx); 370 memset(&priv, 0, sizeof(priv)); 371 return (ret); 372} 373 374static void 375opensslgost_cleanup(void) { 376 if (e != NULL) { 377 ENGINE_finish(e); 378 ENGINE_free(e); 379 e = NULL; 380 } 381} 382 383static dst_func_t opensslgost_functions = { 384 opensslgost_createctx, 385 opensslgost_destroyctx, 386 opensslgost_adddata, 387 opensslgost_sign, 388 opensslgost_verify, 389 NULL, /*%< verify2 */ 390 NULL, /*%< computesecret */ 391 opensslgost_compare, 392 NULL, /*%< paramcompare */ 393 opensslgost_generate, 394 opensslgost_isprivate, 395 opensslgost_destroy, 396 opensslgost_todns, 397 opensslgost_fromdns, 398 opensslgost_tofile, 399 opensslgost_parse, 400 opensslgost_cleanup, 401 NULL, /*%< fromlabel */ 402 NULL, /*%< dump */ 403 NULL /*%< restore */ 404}; 405 406isc_result_t 407dst__opensslgost_init(dst_func_t **funcp) { 408 isc_result_t ret; 409 410 REQUIRE(funcp != NULL); 411 412 /* check if the gost engine works properly */ 413 e = ENGINE_by_id("gost"); 414 if (e == NULL) 415 return (dst__openssl_toresult2("ENGINE_by_id", 416 DST_R_OPENSSLFAILURE)); 417 if (ENGINE_init(e) <= 0) { 418 ENGINE_free(e); 419 e = NULL; 420 return (dst__openssl_toresult2("ENGINE_init", 421 DST_R_OPENSSLFAILURE)); 422 } 423 /* better than to rely on digest_gost symbol */ 424 opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94); 425 if (opensslgost_digest == NULL) 426 DST_RET(dst__openssl_toresult2("ENGINE_get_digest", 427 DST_R_OPENSSLFAILURE)); 428 /* from openssl.cnf */ 429 if (ENGINE_register_pkey_asn1_meths(e) <= 0) 430 DST_RET(dst__openssl_toresult2( 431 "ENGINE_register_pkey_asn1_meths", 432 DST_R_OPENSSLFAILURE)); 433 if (ENGINE_ctrl_cmd_string(e, 434 "CRYPT_PARAMS", 435 "id-Gost28147-89-CryptoPro-A-ParamSet", 436 0) <= 0) 437 DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string", 438 DST_R_OPENSSLFAILURE)); 439 440 if (*funcp == NULL) 441 *funcp = &opensslgost_functions; 442 return (ISC_R_SUCCESS); 443 444 err: 445 ENGINE_finish(e); 446 ENGINE_free(e); 447 e = NULL; 448 return (ret); 449} 450 451#else /* HAVE_OPENSSL_GOST */ 452 453#include <isc/util.h> 454 455EMPTY_TRANSLATION_UNIT 456 457#endif /* HAVE_OPENSSL_GOST */ 458/*! \file */ 459