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