1/* 2 * $Id: ossl_pkey_dsa.c 36355 2012-07-10 13:57:11Z nobu $ 3 * 'OpenSSL for Ruby' project 4 * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> 5 * All rights reserved. 6 */ 7/* 8 * This program is licenced under the same licence as Ruby. 9 * (See the file 'LICENCE'.) 10 */ 11#if !defined(OPENSSL_NO_DSA) 12 13#include "ossl.h" 14 15#define GetPKeyDSA(obj, pkey) do { \ 16 GetPKey((obj), (pkey)); \ 17 if (EVP_PKEY_type((pkey)->type) != EVP_PKEY_DSA) { /* PARANOIA? */ \ 18 ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ 19 } \ 20} while (0) 21 22#define DSA_HAS_PRIVATE(dsa) ((dsa)->priv_key) 23#define DSA_PRIVATE(obj,dsa) (DSA_HAS_PRIVATE(dsa)||OSSL_PKEY_IS_PRIVATE(obj)) 24 25/* 26 * Classes 27 */ 28VALUE cDSA; 29VALUE eDSAError; 30 31/* 32 * Public 33 */ 34static VALUE 35dsa_instance(VALUE klass, DSA *dsa) 36{ 37 EVP_PKEY *pkey; 38 VALUE obj; 39 40 if (!dsa) { 41 return Qfalse; 42 } 43 if (!(pkey = EVP_PKEY_new())) { 44 return Qfalse; 45 } 46 if (!EVP_PKEY_assign_DSA(pkey, dsa)) { 47 EVP_PKEY_free(pkey); 48 return Qfalse; 49 } 50 WrapPKey(klass, obj, pkey); 51 52 return obj; 53} 54 55VALUE 56ossl_dsa_new(EVP_PKEY *pkey) 57{ 58 VALUE obj; 59 60 if (!pkey) { 61 obj = dsa_instance(cDSA, DSA_new()); 62 } else { 63 if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) { 64 ossl_raise(rb_eTypeError, "Not a DSA key!"); 65 } 66 WrapPKey(cDSA, obj, pkey); 67 } 68 if (obj == Qfalse) { 69 ossl_raise(eDSAError, NULL); 70 } 71 72 return obj; 73} 74 75/* 76 * Private 77 */ 78#if defined(HAVE_DSA_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB 79struct dsa_blocking_gen_arg { 80 DSA *dsa; 81 int size; 82 unsigned char* seed; 83 int seed_len; 84 int *counter; 85 unsigned long *h; 86 BN_GENCB *cb; 87 int result; 88}; 89 90static void * 91dsa_blocking_gen(void *arg) 92{ 93 struct dsa_blocking_gen_arg *gen = (struct dsa_blocking_gen_arg *)arg; 94 gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, gen->seed, gen->seed_len, gen->counter, gen->h, gen->cb); 95 return 0; 96} 97#endif 98 99static DSA * 100dsa_generate(int size) 101{ 102#if defined(HAVE_DSA_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB 103 BN_GENCB cb; 104 struct ossl_generate_cb_arg cb_arg; 105 struct dsa_blocking_gen_arg gen_arg; 106 DSA *dsa = DSA_new(); 107 unsigned char seed[20]; 108 int seed_len = 20, counter; 109 unsigned long h; 110 111 if (!dsa) return 0; 112 if (!RAND_bytes(seed, seed_len)) { 113 DSA_free(dsa); 114 return 0; 115 } 116 117 memset(&cb_arg, 0, sizeof(struct ossl_generate_cb_arg)); 118 if (rb_block_given_p()) 119 cb_arg.yield = 1; 120 BN_GENCB_set(&cb, ossl_generate_cb_2, &cb_arg); 121 gen_arg.dsa = dsa; 122 gen_arg.size = size; 123 gen_arg.seed = seed; 124 gen_arg.seed_len = seed_len; 125 gen_arg.counter = &counter; 126 gen_arg.h = &h; 127 gen_arg.cb = &cb; 128 if (cb_arg.yield == 1) { 129 /* we cannot release GVL when callback proc is supplied */ 130 dsa_blocking_gen(&gen_arg); 131 } else { 132 /* there's a chance to unblock */ 133 rb_thread_call_without_gvl(dsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg); 134 } 135 if (!gen_arg.result) { 136 DSA_free(dsa); 137 if (cb_arg.state) rb_jump_tag(cb_arg.state); 138 return 0; 139 } 140#else 141 DSA *dsa; 142 unsigned char seed[20]; 143 int seed_len = 20, counter; 144 unsigned long h; 145 146 if (!RAND_bytes(seed, seed_len)) { 147 return 0; 148 } 149 dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h, 150 rb_block_given_p() ? ossl_generate_cb : NULL, NULL); 151 if(!dsa) return 0; 152#endif 153 154 if (!DSA_generate_key(dsa)) { 155 DSA_free(dsa); 156 return 0; 157 } 158 159 return dsa; 160} 161 162/* 163 * call-seq: 164 * DSA.generate(size) -> dsa 165 * 166 * Creates a new DSA instance by generating a private/public key pair 167 * from scratch. 168 * 169 * === Parameters 170 * * +size+ is an integer representing the desired key size. 171 * 172 */ 173static VALUE 174ossl_dsa_s_generate(VALUE klass, VALUE size) 175{ 176 DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */ 177 VALUE obj = dsa_instance(klass, dsa); 178 179 if (obj == Qfalse) { 180 DSA_free(dsa); 181 ossl_raise(eDSAError, NULL); 182 } 183 184 return obj; 185} 186 187/* 188 * call-seq: 189 * DSA.new([size | string [, pass]) -> dsa 190 * 191 * Creates a new DSA instance by reading an existing key from +string+. 192 * 193 * === Parameters 194 * * +size+ is an integer representing the desired key size. 195 * * +string+ contains a DER or PEM encoded key. 196 * * +pass+ is a string that contains an optional password. 197 * 198 * === Examples 199 * DSA.new -> dsa 200 * DSA.new(1024) -> dsa 201 * DSA.new(File.read('dsa.pem')) -> dsa 202 * DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa 203 * 204 */ 205static VALUE 206ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) 207{ 208 EVP_PKEY *pkey; 209 DSA *dsa; 210 BIO *in; 211 char *passwd = NULL; 212 VALUE arg, pass; 213 214 GetPKey(self, pkey); 215 if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) { 216 dsa = DSA_new(); 217 } 218 else if (FIXNUM_P(arg)) { 219 if (!(dsa = dsa_generate(FIX2INT(arg)))) { 220 ossl_raise(eDSAError, NULL); 221 } 222 } 223 else { 224 if (!NIL_P(pass)) passwd = StringValuePtr(pass); 225 arg = ossl_to_der_if_possible(arg); 226 in = ossl_obj2bio(arg); 227 dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); 228 if (!dsa) { 229 OSSL_BIO_reset(in); 230 dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL); 231 } 232 if (!dsa) { 233 OSSL_BIO_reset(in); 234 dsa = d2i_DSAPrivateKey_bio(in, NULL); 235 } 236 if (!dsa) { 237 OSSL_BIO_reset(in); 238 dsa = d2i_DSA_PUBKEY_bio(in, NULL); 239 } 240 if (!dsa) { 241 OSSL_BIO_reset(in); 242 dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL); 243 } 244 BIO_free(in); 245 if (!dsa) { 246 ERR_clear_error(); 247 ossl_raise(eDSAError, "Neither PUB key nor PRIV key"); 248 } 249 } 250 if (!EVP_PKEY_assign_DSA(pkey, dsa)) { 251 DSA_free(dsa); 252 ossl_raise(eDSAError, NULL); 253 } 254 255 return self; 256} 257 258/* 259 * call-seq: 260 * dsa.public? -> true | false 261 * 262 * Indicates whether this DSA instance has a public key associated with it or 263 * not. The public key may be retrieved with DSA#public_key. 264 */ 265static VALUE 266ossl_dsa_is_public(VALUE self) 267{ 268 EVP_PKEY *pkey; 269 270 GetPKeyDSA(self, pkey); 271 272 return (pkey->pkey.dsa->pub_key) ? Qtrue : Qfalse; 273} 274 275/* 276 * call-seq: 277 * dsa.private? -> true | false 278 * 279 * Indicates whether this DSA instance has a private key associated with it or 280 * not. The private key may be retrieved with DSA#private_key. 281 */ 282static VALUE 283ossl_dsa_is_private(VALUE self) 284{ 285 EVP_PKEY *pkey; 286 287 GetPKeyDSA(self, pkey); 288 289 return (DSA_PRIVATE(self, pkey->pkey.dsa)) ? Qtrue : Qfalse; 290} 291 292/* 293 * call-seq: 294 * dsa.to_pem([cipher, password]) -> aString 295 * 296 * Encodes this DSA to its PEM encoding. 297 * 298 * === Parameters 299 * * +cipher+ is an OpenSSL::Cipher. 300 * * +password+ is a string containing your password. 301 * 302 * === Examples 303 * DSA.to_pem -> aString 304 * DSA.to_pem(cipher, 'mypassword') -> aString 305 * 306 */ 307static VALUE 308ossl_dsa_export(int argc, VALUE *argv, VALUE self) 309{ 310 EVP_PKEY *pkey; 311 BIO *out; 312 const EVP_CIPHER *ciph = NULL; 313 char *passwd = NULL; 314 VALUE cipher, pass, str; 315 316 GetPKeyDSA(self, pkey); 317 rb_scan_args(argc, argv, "02", &cipher, &pass); 318 if (!NIL_P(cipher)) { 319 ciph = GetCipherPtr(cipher); 320 if (!NIL_P(pass)) { 321 StringValue(pass); 322 if (RSTRING_LENINT(pass) < OSSL_MIN_PWD_LEN) 323 ossl_raise(eOSSLError, "OpenSSL requires passwords to be at least four characters long"); 324 passwd = RSTRING_PTR(pass); 325 } 326 } 327 if (!(out = BIO_new(BIO_s_mem()))) { 328 ossl_raise(eDSAError, NULL); 329 } 330 if (DSA_HAS_PRIVATE(pkey->pkey.dsa)) { 331 if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph, 332 NULL, 0, ossl_pem_passwd_cb, passwd)){ 333 BIO_free(out); 334 ossl_raise(eDSAError, NULL); 335 } 336 } else { 337 if (!PEM_write_bio_DSA_PUBKEY(out, pkey->pkey.dsa)) { 338 BIO_free(out); 339 ossl_raise(eDSAError, NULL); 340 } 341 } 342 str = ossl_membio2str(out); 343 344 return str; 345} 346 347/* 348 * call-seq: 349 * dsa.to_der -> aString 350 * 351 * Encodes this DSA to its DER encoding. 352 * 353 */ 354static VALUE 355ossl_dsa_to_der(VALUE self) 356{ 357 EVP_PKEY *pkey; 358 int (*i2d_func)_((DSA*, unsigned char**)); 359 unsigned char *p; 360 long len; 361 VALUE str; 362 363 GetPKeyDSA(self, pkey); 364 if(DSA_HAS_PRIVATE(pkey->pkey.dsa)) 365 i2d_func = (int(*)_((DSA*,unsigned char**)))i2d_DSAPrivateKey; 366 else 367 i2d_func = i2d_DSA_PUBKEY; 368 if((len = i2d_func(pkey->pkey.dsa, NULL)) <= 0) 369 ossl_raise(eDSAError, NULL); 370 str = rb_str_new(0, len); 371 p = (unsigned char *)RSTRING_PTR(str); 372 if(i2d_func(pkey->pkey.dsa, &p) < 0) 373 ossl_raise(eDSAError, NULL); 374 ossl_str_adjust(str, p); 375 376 return str; 377} 378 379/* 380 * call-seq: 381 * dsa.params -> hash 382 * 383 * Stores all parameters of key to the hash 384 * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! 385 * Don't use :-)) (I's up to you) 386 */ 387static VALUE 388ossl_dsa_get_params(VALUE self) 389{ 390 EVP_PKEY *pkey; 391 VALUE hash; 392 393 GetPKeyDSA(self, pkey); 394 395 hash = rb_hash_new(); 396 397 rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dsa->p)); 398 rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.dsa->q)); 399 rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dsa->g)); 400 rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dsa->pub_key)); 401 rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dsa->priv_key)); 402 403 return hash; 404} 405 406/* 407 * call-seq: 408 * dsa.to_text -> aString 409 * 410 * Prints all parameters of key to buffer 411 * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! 412 * Don't use :-)) (I's up to you) 413 */ 414static VALUE 415ossl_dsa_to_text(VALUE self) 416{ 417 EVP_PKEY *pkey; 418 BIO *out; 419 VALUE str; 420 421 GetPKeyDSA(self, pkey); 422 if (!(out = BIO_new(BIO_s_mem()))) { 423 ossl_raise(eDSAError, NULL); 424 } 425 if (!DSA_print(out, pkey->pkey.dsa, 0)) { /* offset = 0 */ 426 BIO_free(out); 427 ossl_raise(eDSAError, NULL); 428 } 429 str = ossl_membio2str(out); 430 431 return str; 432} 433 434/* 435 * call-seq: 436 * dsa.public_key -> aDSA 437 * 438 * Returns a new DSA instance that carries just the public key information. 439 * If the current instance has also private key information, this will no 440 * longer be present in the new instance. This feature is helpful for 441 * publishing the public key information without leaking any of the private 442 * information. 443 * 444 * === Example 445 * dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information 446 * pub_key = dsa.public_key # has only the public part available 447 * pub_key_der = pub_key.to_der # it's safe to publish this 448 * 449 * 450 */ 451static VALUE 452ossl_dsa_to_public_key(VALUE self) 453{ 454 EVP_PKEY *pkey; 455 DSA *dsa; 456 VALUE obj; 457 458 GetPKeyDSA(self, pkey); 459 /* err check performed by dsa_instance */ 460 dsa = DSAPublicKey_dup(pkey->pkey.dsa); 461 obj = dsa_instance(CLASS_OF(self), dsa); 462 if (obj == Qfalse) { 463 DSA_free(dsa); 464 ossl_raise(eDSAError, NULL); 465 } 466 return obj; 467} 468 469#define ossl_dsa_buf_size(pkey) (DSA_size((pkey)->pkey.dsa)+16) 470 471/* 472 * call-seq: 473 * dsa.syssign(string) -> aString 474 * 475 * Computes and returns the DSA signature of +string+, where +string+ is 476 * expected to be an already-computed message digest of the original input 477 * data. The signature is issued using the private key of this DSA instance. 478 * 479 * === Parameters 480 * * +string+ is a message digest of the original input data to be signed 481 * 482 * === Example 483 * dsa = OpenSSL::PKey::DSA.new(2048) 484 * doc = "Sign me" 485 * digest = OpenSSL::Digest::SHA1.digest(doc) 486 * sig = dsa.syssign(digest) 487 * 488 * 489 */ 490static VALUE 491ossl_dsa_sign(VALUE self, VALUE data) 492{ 493 EVP_PKEY *pkey; 494 unsigned int buf_len; 495 VALUE str; 496 497 GetPKeyDSA(self, pkey); 498 StringValue(data); 499 if (!DSA_PRIVATE(self, pkey->pkey.dsa)) { 500 ossl_raise(eDSAError, "Private DSA key needed!"); 501 } 502 str = rb_str_new(0, ossl_dsa_buf_size(pkey)); 503 if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data), 504 (unsigned char *)RSTRING_PTR(str), 505 &buf_len, pkey->pkey.dsa)) { /* type is ignored (0) */ 506 ossl_raise(eDSAError, NULL); 507 } 508 rb_str_set_len(str, buf_len); 509 510 return str; 511} 512 513/* 514 * call-seq: 515 * dsa.sysverify(digest, sig) -> true | false 516 * 517 * Verifies whether the signature is valid given the message digest input. It 518 * does so by validating +sig+ using the public key of this DSA instance. 519 * 520 * === Parameters 521 * * +digest+ is a message digest of the original input data to be signed 522 * * +sig+ is a DSA signature value 523 * 524 * === Example 525 * dsa = OpenSSL::PKey::DSA.new(2048) 526 * doc = "Sign me" 527 * digest = OpenSSL::Digest::SHA1.digest(doc) 528 * sig = dsa.syssign(digest) 529 * puts dsa.sysverify(digest, sig) # => true 530 * 531 */ 532static VALUE 533ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig) 534{ 535 EVP_PKEY *pkey; 536 int ret; 537 538 GetPKeyDSA(self, pkey); 539 StringValue(digest); 540 StringValue(sig); 541 /* type is ignored (0) */ 542 ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest), 543 (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey->pkey.dsa); 544 if (ret < 0) { 545 ossl_raise(eDSAError, NULL); 546 } 547 else if (ret == 1) { 548 return Qtrue; 549 } 550 551 return Qfalse; 552} 553 554OSSL_PKEY_BN(dsa, p) 555OSSL_PKEY_BN(dsa, q) 556OSSL_PKEY_BN(dsa, g) 557OSSL_PKEY_BN(dsa, pub_key) 558OSSL_PKEY_BN(dsa, priv_key) 559 560/* 561 * INIT 562 */ 563void 564Init_ossl_dsa() 565{ 566#if 0 567 mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */ 568 mPKey = rb_define_module_under(mOSSL, "PKey"); 569#endif 570 571 /* Document-class: OpenSSL::PKey::DSAError 572 * 573 * Generic exception that is raised if an operation on a DSA PKey 574 * fails unexpectedly or in case an instantiation of an instance of DSA 575 * fails due to non-conformant input data. 576 */ 577 eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError); 578 579 /* Document-class: OpenSSL::PKey::DSA 580 * 581 * DSA, the Digital Signature Algorithm, is specified in NIST's 582 * FIPS 186-3. It is an asymmetric public key algorithm that may be used 583 * similar to e.g. RSA. 584 * Please note that for OpenSSL versions prior to 1.0.0 the digest 585 * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or 586 * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing 587 * signatures with a DSA key using OpenSSL::PKey#sign. 588 * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted, 589 * any Digest may be used for signing. 590 */ 591 cDSA = rb_define_class_under(mPKey, "DSA", cPKey); 592 593 rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); 594 rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1); 595 596 rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); 597 rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); 598 rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0); 599 rb_define_method(cDSA, "export", ossl_dsa_export, -1); 600 rb_define_alias(cDSA, "to_pem", "export"); 601 rb_define_alias(cDSA, "to_s", "export"); 602 rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0); 603 rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0); 604 rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1); 605 rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2); 606 607 DEF_OSSL_PKEY_BN(cDSA, dsa, p); 608 DEF_OSSL_PKEY_BN(cDSA, dsa, q); 609 DEF_OSSL_PKEY_BN(cDSA, dsa, g); 610 DEF_OSSL_PKEY_BN(cDSA, dsa, pub_key); 611 DEF_OSSL_PKEY_BN(cDSA, dsa, priv_key); 612 613 rb_define_method(cDSA, "params", ossl_dsa_get_params, 0); 614} 615 616#else /* defined NO_DSA */ 617void 618Init_ossl_dsa() 619{ 620} 621#endif /* NO_DSA */ 622