1/* 2 * $Id: ossl_pkey.c 45868 2014-05-07 16:59:18Z usa $ 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#include "ossl.h" 12 13/* 14 * Classes 15 */ 16VALUE mPKey; 17VALUE cPKey; 18VALUE ePKeyError; 19ID id_private_q; 20 21/* 22 * callback for generating keys 23 */ 24void 25ossl_generate_cb(int p, int n, void *arg) 26{ 27 VALUE ary; 28 29 ary = rb_ary_new2(2); 30 rb_ary_store(ary, 0, INT2NUM(p)); 31 rb_ary_store(ary, 1, INT2NUM(n)); 32 33 rb_yield(ary); 34} 35 36#if HAVE_BN_GENCB 37/* OpenSSL 2nd version of GN generation callback */ 38int 39ossl_generate_cb_2(int p, int n, BN_GENCB *cb) 40{ 41 VALUE ary; 42 struct ossl_generate_cb_arg *arg; 43 int state; 44 45 arg = (struct ossl_generate_cb_arg *)cb->arg; 46 if (arg->yield) { 47 ary = rb_ary_new2(2); 48 rb_ary_store(ary, 0, INT2NUM(p)); 49 rb_ary_store(ary, 1, INT2NUM(n)); 50 51 /* 52 * can be break by raising exception or 'break' 53 */ 54 rb_protect(rb_yield, ary, &state); 55 if (state) { 56 arg->stop = 1; 57 arg->state = state; 58 } 59 } 60 if (arg->stop) return 0; 61 return 1; 62} 63 64void 65ossl_generate_cb_stop(void *ptr) 66{ 67 struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr; 68 arg->stop = 1; 69} 70#endif 71 72/* 73 * Public 74 */ 75VALUE 76ossl_pkey_new(EVP_PKEY *pkey) 77{ 78 if (!pkey) { 79 ossl_raise(ePKeyError, "Cannot make new key from NULL."); 80 } 81 switch (EVP_PKEY_type(pkey->type)) { 82#if !defined(OPENSSL_NO_RSA) 83 case EVP_PKEY_RSA: 84 return ossl_rsa_new(pkey); 85#endif 86#if !defined(OPENSSL_NO_DSA) 87 case EVP_PKEY_DSA: 88 return ossl_dsa_new(pkey); 89#endif 90#if !defined(OPENSSL_NO_DH) 91 case EVP_PKEY_DH: 92 return ossl_dh_new(pkey); 93#endif 94#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) 95 case EVP_PKEY_EC: 96 return ossl_ec_new(pkey); 97#endif 98 default: 99 ossl_raise(ePKeyError, "unsupported key type"); 100 } 101 102 UNREACHABLE; 103} 104 105VALUE 106ossl_pkey_new_from_file(VALUE filename) 107{ 108 FILE *fp; 109 EVP_PKEY *pkey; 110 111 SafeStringValue(filename); 112 if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { 113 ossl_raise(ePKeyError, "%s", strerror(errno)); 114 } 115 rb_fd_fix_cloexec(fileno(fp)); 116 117 pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); 118 fclose(fp); 119 if (!pkey) { 120 ossl_raise(ePKeyError, NULL); 121 } 122 123 return ossl_pkey_new(pkey); 124} 125 126/* 127 * call-seq: 128 * OpenSSL::PKey.read(string [, pwd ] ) -> PKey 129 * OpenSSL::PKey.read(file [, pwd ]) -> PKey 130 * 131 * === Parameters 132 * * +string+ is a DER- or PEM-encoded string containing an arbitrary private 133 * or public key. 134 * * +file+ is an instance of +File+ containing a DER- or PEM-encoded 135 * arbitrary private or public key. 136 * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted 137 * PEM resource. 138 */ 139static VALUE 140ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) 141{ 142 EVP_PKEY *pkey; 143 BIO *bio; 144 VALUE data, pass; 145 char *passwd = NULL; 146 147 rb_scan_args(argc, argv, "11", &data, &pass); 148 149 bio = ossl_obj2bio(data); 150 if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { 151 OSSL_BIO_reset(bio); 152 if (!NIL_P(pass)) { 153 passwd = StringValuePtr(pass); 154 } 155 if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, passwd))) { 156 OSSL_BIO_reset(bio); 157 if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) { 158 OSSL_BIO_reset(bio); 159 if (!NIL_P(pass)) { 160 passwd = StringValuePtr(pass); 161 } 162 pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, passwd); 163 } 164 } 165 } 166 167 BIO_free(bio); 168 if (!pkey) 169 ossl_raise(rb_eArgError, "Could not parse PKey"); 170 return ossl_pkey_new(pkey); 171} 172 173EVP_PKEY * 174GetPKeyPtr(VALUE obj) 175{ 176 EVP_PKEY *pkey; 177 178 SafeGetPKey(obj, pkey); 179 180 return pkey; 181} 182 183EVP_PKEY * 184GetPrivPKeyPtr(VALUE obj) 185{ 186 EVP_PKEY *pkey; 187 188 if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { 189 ossl_raise(rb_eArgError, "Private key is needed."); 190 } 191 SafeGetPKey(obj, pkey); 192 193 return pkey; 194} 195 196EVP_PKEY * 197DupPKeyPtr(VALUE obj) 198{ 199 EVP_PKEY *pkey; 200 201 SafeGetPKey(obj, pkey); 202 CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); 203 204 return pkey; 205} 206 207EVP_PKEY * 208DupPrivPKeyPtr(VALUE obj) 209{ 210 EVP_PKEY *pkey; 211 212 if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { 213 ossl_raise(rb_eArgError, "Private key is needed."); 214 } 215 SafeGetPKey(obj, pkey); 216 CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); 217 218 return pkey; 219} 220 221/* 222 * Private 223 */ 224static VALUE 225ossl_pkey_alloc(VALUE klass) 226{ 227 EVP_PKEY *pkey; 228 VALUE obj; 229 230 if (!(pkey = EVP_PKEY_new())) { 231 ossl_raise(ePKeyError, NULL); 232 } 233 WrapPKey(klass, obj, pkey); 234 235 return obj; 236} 237 238/* 239 * call-seq: 240 * PKeyClass.new -> self 241 * 242 * Because PKey is an abstract class, actually calling this method explicitly 243 * will raise a +NotImplementedError+. 244 */ 245static VALUE 246ossl_pkey_initialize(VALUE self) 247{ 248 if (rb_obj_is_instance_of(self, cPKey)) { 249 ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class."); 250 } 251 return self; 252} 253 254/* 255 * call-seq: 256 * pkey.sign(digest, data) -> String 257 * 258 * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must 259 * be provided. The return value is again a +String+ containing the signature. 260 * A PKeyError is raised should errors occur. 261 * Any previous state of the +Digest+ instance is irrelevant to the signature 262 * outcome, the digest instance is reset to its initial state during the 263 * operation. 264 * 265 * == Example 266 * data = 'Sign me!' 267 * digest = OpenSSL::Digest::SHA256.new 268 * pkey = OpenSSL::PKey::RSA.new(2048) 269 * signature = pkey.sign(digest, data) 270 */ 271static VALUE 272ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) 273{ 274 EVP_PKEY *pkey; 275 EVP_MD_CTX ctx; 276 unsigned int buf_len; 277 VALUE str; 278 279 if (rb_funcall(self, id_private_q, 0, NULL) != Qtrue) { 280 ossl_raise(rb_eArgError, "Private key is needed."); 281 } 282 GetPKey(self, pkey); 283 EVP_SignInit(&ctx, GetDigestPtr(digest)); 284 StringValue(data); 285 EVP_SignUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); 286 str = rb_str_new(0, EVP_PKEY_size(pkey)+16); 287 if (!EVP_SignFinal(&ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey)) 288 ossl_raise(ePKeyError, NULL); 289 assert((long)buf_len <= RSTRING_LEN(str)); 290 rb_str_set_len(str, buf_len); 291 292 return str; 293} 294 295/* 296 * call-seq: 297 * pkey.verify(digest, signature, data) -> String 298 * 299 * To verify the +String+ +signature+, +digest+, an instance of 300 * OpenSSL::Digest, must be provided to re-compute the message digest of the 301 * original +data+, also a +String+. The return value is +true+ if the 302 * signature is valid, +false+ otherwise. A PKeyError is raised should errors 303 * occur. 304 * Any previous state of the +Digest+ instance is irrelevant to the validation 305 * outcome, the digest instance is reset to its initial state during the 306 * operation. 307 * 308 * == Example 309 * data = 'Sign me!' 310 * digest = OpenSSL::Digest::SHA256.new 311 * pkey = OpenSSL::PKey::RSA.new(2048) 312 * signature = pkey.sign(digest, data) 313 * pub_key = pkey.public_key 314 * puts pub_key.verify(digest, signature, data) # => true 315 */ 316static VALUE 317ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) 318{ 319 EVP_PKEY *pkey; 320 EVP_MD_CTX ctx; 321 int result; 322 323 GetPKey(self, pkey); 324 StringValue(sig); 325 StringValue(data); 326 EVP_VerifyInit(&ctx, GetDigestPtr(digest)); 327 EVP_VerifyUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); 328 result = EVP_VerifyFinal(&ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey); 329 EVP_MD_CTX_cleanup(&ctx); 330 switch (result) { 331 case 0: 332 return Qfalse; 333 case 1: 334 return Qtrue; 335 default: 336 ossl_raise(ePKeyError, NULL); 337 } 338 return Qnil; /* dummy */ 339} 340 341/* 342 * INIT 343 */ 344void 345Init_ossl_pkey() 346{ 347#if 0 348 mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ 349#endif 350 351 /* Document-module: OpenSSL::PKey 352 * 353 * == Asymmetric Public Key Algorithms 354 * 355 * Asymmetric public key algorithms solve the problem of establishing and 356 * sharing secret keys to en-/decrypt messages. The key in such an 357 * algorithm consists of two parts: a public key that may be distributed 358 * to others and a private key that needs to remain secret. 359 * 360 * Messages encrypted with a public key can only be encrypted by 361 * recipients that are in possession of the associated private key. 362 * Since public key algorithms are considerably slower than symmetric 363 * key algorithms (cf. OpenSSL::Cipher) they are often used to establish 364 * a symmetric key shared between two parties that are in possession of 365 * each other's public key. 366 * 367 * Asymmetric algorithms offer a lot of nice features that are used in a 368 * lot of different areas. A very common application is the creation and 369 * validation of digital signatures. To sign a document, the signatory 370 * generally uses a message digest algorithm (cf. OpenSSL::Digest) to 371 * compute a digest of the document that is then encrypted (i.e. signed) 372 * using the private key. Anyone in possession of the public key may then 373 * verify the signature by computing the message digest of the original 374 * document on their own, decrypting the signature using the signatory's 375 * public key and comparing the result to the message digest they 376 * previously computed. The signature is valid if and only if the 377 * decrypted signature is equal to this message digest. 378 * 379 * The PKey module offers support for three popular public/private key 380 * algorithms: 381 * * RSA (OpenSSL::PKey::RSA) 382 * * DSA (OpenSSL::PKey::DSA) 383 * * Elliptic Curve Cryptography (OpenSSL::PKey::EC) 384 * Each of these implementations is in fact a sub-class of the abstract 385 * PKey class which offers the interface for supporting digital signatures 386 * in the form of PKey#sign and PKey#verify. 387 * 388 * == Diffie-Hellman Key Exchange 389 * 390 * Finally PKey also features OpenSSL::PKey::DH, an implementation of 391 * the Diffie-Hellman key exchange protocol based on discrete logarithms 392 * in finite fields, the same basis that DSA is built on. 393 * The Diffie-Hellman protocol can be used to exchange (symmetric) keys 394 * over insecure channels without needing any prior joint knowledge 395 * between the participating parties. As the security of DH demands 396 * relatively long "public keys" (i.e. the part that is overtly 397 * transmitted between participants) DH tends to be quite slow. If 398 * security or speed is your primary concern, OpenSSL::PKey::EC offers 399 * another implementation of the Diffie-Hellman protocol. 400 * 401 */ 402 mPKey = rb_define_module_under(mOSSL, "PKey"); 403 404 /* Document-class: OpenSSL::PKey::PKeyError 405 * 406 *Raised when errors occur during PKey#sign or PKey#verify. 407 */ 408 ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); 409 410 /* Document-class: OpenSSL::PKey::PKey 411 * 412 * An abstract class that bundles signature creation (PKey#sign) and 413 * validation (PKey#verify) that is common to all implementations except 414 * OpenSSL::PKey::DH 415 * * OpenSSL::PKey::RSA 416 * * OpenSSL::PKey::DSA 417 * * OpenSSL::PKey::EC 418 */ 419 cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); 420 421 rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); 422 423 rb_define_alloc_func(cPKey, ossl_pkey_alloc); 424 rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); 425 426 rb_define_method(cPKey, "sign", ossl_pkey_sign, 2); 427 rb_define_method(cPKey, "verify", ossl_pkey_verify, 3); 428 429 id_private_q = rb_intern("private?"); 430 431 /* 432 * INIT rsa, dsa, dh, ec 433 */ 434 Init_ossl_rsa(); 435 Init_ossl_dsa(); 436 Init_ossl_dh(); 437 Init_ossl_ec(); 438} 439 440