Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /* 00002 * $Id: ossl_pkey.c 35322 2012-04-14 00:36:26Z drbrain $ 00003 * 'OpenSSL for Ruby' project 00004 * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> 00005 * All rights reserved. 00006 */ 00007 /* 00008 * This program is licenced under the same licence as Ruby. 00009 * (See the file 'LICENCE'.) 00010 */ 00011 #include "ossl.h" 00012 00013 /* 00014 * Classes 00015 */ 00016 VALUE mPKey; 00017 VALUE cPKey; 00018 VALUE ePKeyError; 00019 ID id_private_q; 00020 00021 /* 00022 * callback for generating keys 00023 */ 00024 void 00025 ossl_generate_cb(int p, int n, void *arg) 00026 { 00027 VALUE ary; 00028 00029 ary = rb_ary_new2(2); 00030 rb_ary_store(ary, 0, INT2NUM(p)); 00031 rb_ary_store(ary, 1, INT2NUM(n)); 00032 00033 rb_yield(ary); 00034 } 00035 00036 #if HAVE_BN_GENCB 00037 /* OpenSSL 2nd version of GN generation callback */ 00038 int 00039 ossl_generate_cb_2(int p, int n, BN_GENCB *cb) 00040 { 00041 VALUE ary; 00042 struct ossl_generate_cb_arg *arg; 00043 int state; 00044 00045 arg = (struct ossl_generate_cb_arg *)cb->arg; 00046 if (arg->yield) { 00047 ary = rb_ary_new2(2); 00048 rb_ary_store(ary, 0, INT2NUM(p)); 00049 rb_ary_store(ary, 1, INT2NUM(n)); 00050 00051 /* 00052 * can be break by raising exception or 'break' 00053 */ 00054 rb_protect(rb_yield, ary, &state); 00055 if (state) { 00056 arg->stop = 1; 00057 arg->state = state; 00058 } 00059 } 00060 if (arg->stop) return 0; 00061 return 1; 00062 } 00063 00064 void 00065 ossl_generate_cb_stop(void *ptr) 00066 { 00067 struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr; 00068 arg->stop = 1; 00069 } 00070 #endif 00071 00072 /* 00073 * Public 00074 */ 00075 VALUE 00076 ossl_pkey_new(EVP_PKEY *pkey) 00077 { 00078 if (!pkey) { 00079 ossl_raise(ePKeyError, "Cannot make new key from NULL."); 00080 } 00081 switch (EVP_PKEY_type(pkey->type)) { 00082 #if !defined(OPENSSL_NO_RSA) 00083 case EVP_PKEY_RSA: 00084 return ossl_rsa_new(pkey); 00085 #endif 00086 #if !defined(OPENSSL_NO_DSA) 00087 case EVP_PKEY_DSA: 00088 return ossl_dsa_new(pkey); 00089 #endif 00090 #if !defined(OPENSSL_NO_DH) 00091 case EVP_PKEY_DH: 00092 return ossl_dh_new(pkey); 00093 #endif 00094 #if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) 00095 case EVP_PKEY_EC: 00096 return ossl_ec_new(pkey); 00097 #endif 00098 default: 00099 ossl_raise(ePKeyError, "unsupported key type"); 00100 } 00101 00102 UNREACHABLE; 00103 } 00104 00105 VALUE 00106 ossl_pkey_new_from_file(VALUE filename) 00107 { 00108 FILE *fp; 00109 EVP_PKEY *pkey; 00110 00111 SafeStringValue(filename); 00112 if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { 00113 ossl_raise(ePKeyError, "%s", strerror(errno)); 00114 } 00115 rb_fd_fix_cloexec(fileno(fp)); 00116 00117 pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); 00118 fclose(fp); 00119 if (!pkey) { 00120 ossl_raise(ePKeyError, NULL); 00121 } 00122 00123 return ossl_pkey_new(pkey); 00124 } 00125 00126 /* 00127 * call-seq: 00128 * OpenSSL::PKey.read(string [, pwd ] ) -> PKey 00129 * OpenSSL::PKey.read(file [, pwd ]) -> PKey 00130 * 00131 * === Parameters 00132 * * +string+ is a DER- or PEM-encoded string containing an arbitrary private 00133 * or public key. 00134 * * +file+ is an instance of +File+ containing a DER- or PEM-encoded 00135 * arbitrary private or public key. 00136 * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted 00137 * PEM resource. 00138 */ 00139 static VALUE 00140 ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) 00141 { 00142 EVP_PKEY *pkey; 00143 BIO *bio; 00144 VALUE data, pass; 00145 char *passwd = NULL; 00146 00147 rb_scan_args(argc, argv, "11", &data, &pass); 00148 00149 bio = ossl_obj2bio(data); 00150 if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { 00151 OSSL_BIO_reset(bio); 00152 if (!NIL_P(pass)) { 00153 passwd = StringValuePtr(pass); 00154 } 00155 if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, passwd))) { 00156 OSSL_BIO_reset(bio); 00157 if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) { 00158 OSSL_BIO_reset(bio); 00159 if (!NIL_P(pass)) { 00160 passwd = StringValuePtr(pass); 00161 } 00162 pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, passwd); 00163 } 00164 } 00165 } 00166 00167 BIO_free(bio); 00168 if (!pkey) 00169 ossl_raise(rb_eArgError, "Could not parse PKey"); 00170 return ossl_pkey_new(pkey); 00171 } 00172 00173 EVP_PKEY * 00174 GetPKeyPtr(VALUE obj) 00175 { 00176 EVP_PKEY *pkey; 00177 00178 SafeGetPKey(obj, pkey); 00179 00180 return pkey; 00181 } 00182 00183 EVP_PKEY * 00184 GetPrivPKeyPtr(VALUE obj) 00185 { 00186 EVP_PKEY *pkey; 00187 00188 if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { 00189 ossl_raise(rb_eArgError, "Private key is needed."); 00190 } 00191 SafeGetPKey(obj, pkey); 00192 00193 return pkey; 00194 } 00195 00196 EVP_PKEY * 00197 DupPKeyPtr(VALUE obj) 00198 { 00199 EVP_PKEY *pkey; 00200 00201 SafeGetPKey(obj, pkey); 00202 CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); 00203 00204 return pkey; 00205 } 00206 00207 EVP_PKEY * 00208 DupPrivPKeyPtr(VALUE obj) 00209 { 00210 EVP_PKEY *pkey; 00211 00212 if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { 00213 ossl_raise(rb_eArgError, "Private key is needed."); 00214 } 00215 SafeGetPKey(obj, pkey); 00216 CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); 00217 00218 return pkey; 00219 } 00220 00221 /* 00222 * Private 00223 */ 00224 static VALUE 00225 ossl_pkey_alloc(VALUE klass) 00226 { 00227 EVP_PKEY *pkey; 00228 VALUE obj; 00229 00230 if (!(pkey = EVP_PKEY_new())) { 00231 ossl_raise(ePKeyError, NULL); 00232 } 00233 WrapPKey(klass, obj, pkey); 00234 00235 return obj; 00236 } 00237 00238 /* 00239 * call-seq: 00240 * PKeyClass.new -> self 00241 * 00242 * Because PKey is an abstract class, actually calling this method explicitly 00243 * will raise a +NotImplementedError+. 00244 */ 00245 static VALUE 00246 ossl_pkey_initialize(VALUE self) 00247 { 00248 if (rb_obj_is_instance_of(self, cPKey)) { 00249 ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class."); 00250 } 00251 return self; 00252 } 00253 00254 /* 00255 * call-seq: 00256 * pkey.sign(digest, data) -> String 00257 * 00258 * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must 00259 * be provided. The return value is again a +String+ containing the signature. 00260 * A PKeyError is raised should errors occur. 00261 * Any previous state of the +Digest+ instance is irrelevant to the signature 00262 * outcome, the digest instance is reset to its initial state during the 00263 * operation. 00264 * 00265 * == Example 00266 * data = 'Sign me!' 00267 * digest = OpenSSL::Digest::SHA256.new 00268 * pkey = OpenSSL::PKey::RSA.new(2048) 00269 * signature = pkey.sign(digest, data) 00270 */ 00271 static VALUE 00272 ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) 00273 { 00274 EVP_PKEY *pkey; 00275 EVP_MD_CTX ctx; 00276 unsigned int buf_len; 00277 VALUE str; 00278 00279 if (rb_funcall(self, id_private_q, 0, NULL) != Qtrue) { 00280 ossl_raise(rb_eArgError, "Private key is needed."); 00281 } 00282 GetPKey(self, pkey); 00283 EVP_SignInit(&ctx, GetDigestPtr(digest)); 00284 StringValue(data); 00285 EVP_SignUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); 00286 str = rb_str_new(0, EVP_PKEY_size(pkey)+16); 00287 if (!EVP_SignFinal(&ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey)) 00288 ossl_raise(ePKeyError, NULL); 00289 assert((long)buf_len <= RSTRING_LEN(str)); 00290 rb_str_set_len(str, buf_len); 00291 00292 return str; 00293 } 00294 00295 /* 00296 * call-seq: 00297 * pkey.verify(digest, signature, data) -> String 00298 * 00299 * To verify the +String+ +signature+, +digest+, an instance of 00300 * OpenSSL::Digest, must be provided to re-compute the message digest of the 00301 * original +data+, also a +String+. The return value is +true+ if the 00302 * signature is valid, +false+ otherwise. A PKeyError is raised should errors 00303 * occur. 00304 * Any previous state of the +Digest+ instance is irrelevant to the validation 00305 * outcome, the digest instance is reset to its initial state during the 00306 * operation. 00307 * 00308 * == Example 00309 * data = 'Sign me!' 00310 * digest = OpenSSL::Digest::SHA256.new 00311 * pkey = OpenSSL::PKey::RSA.new(2048) 00312 * signature = pkey.sign(digest, data) 00313 * pub_key = pkey.public_key 00314 * puts pub_key.verify(digest, signature, data) # => true 00315 */ 00316 static VALUE 00317 ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) 00318 { 00319 EVP_PKEY *pkey; 00320 EVP_MD_CTX ctx; 00321 00322 GetPKey(self, pkey); 00323 EVP_VerifyInit(&ctx, GetDigestPtr(digest)); 00324 StringValue(sig); 00325 StringValue(data); 00326 EVP_VerifyUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); 00327 switch (EVP_VerifyFinal(&ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey)) { 00328 case 0: 00329 return Qfalse; 00330 case 1: 00331 return Qtrue; 00332 default: 00333 ossl_raise(ePKeyError, NULL); 00334 } 00335 return Qnil; /* dummy */ 00336 } 00337 00338 /* 00339 * INIT 00340 */ 00341 void 00342 Init_ossl_pkey() 00343 { 00344 #if 0 00345 mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ 00346 #endif 00347 00348 /* Document-module: OpenSSL::PKey 00349 * 00350 * == Asymmetric Public Key Algorithms 00351 * 00352 * Asymmetric public key algorithms solve the problem of establishing and 00353 * sharing secret keys to en-/decrypt messages. The key in such an 00354 * algorithm consists of two parts: a public key that may be distributed 00355 * to others and a private key that needs to remain secret. 00356 * 00357 * Messages encrypted with a public key can only be encrypted by 00358 * recipients that are in possession of the associated private key. 00359 * Since public key algorithms are considerably slower than symmetric 00360 * key algorithms (cf. OpenSSL::Cipher) they are often used to establish 00361 * a symmetric key shared between two parties that are in possession of 00362 * each other's public key. 00363 * 00364 * Asymmetric algorithms offer a lot of nice features that are used in a 00365 * lot of different areas. A very common application is the creation and 00366 * validation of digital signatures. To sign a document, the signatory 00367 * generally uses a message digest algorithm (cf. OpenSSL::Digest) to 00368 * compute a digest of the document that is then encrypted (i.e. signed) 00369 * using the private key. Anyone in possession of the public key may then 00370 * verify the signature by computing the message digest of the original 00371 * document on their own, decrypting the signature using the signatory's 00372 * public key and comparing the result to the message digest they 00373 * previously computed. The signature is valid if and only if the 00374 * decrypted signature is equal to this message digest. 00375 * 00376 * The PKey module offers support for three popular public/private key 00377 * algorithms: 00378 * * RSA (OpenSSL::PKey::RSA) 00379 * * DSA (OpenSSL::PKey::DSA) 00380 * * Elliptic Curve Cryptography (OpenSSL::PKey::EC) 00381 * Each of these implementations is in fact a sub-class of the abstract 00382 * PKey class which offers the interface for supporting digital signatures 00383 * in the form of PKey#sign and PKey#verify. 00384 * 00385 * == Diffie-Hellman Key Exchange 00386 * 00387 * Finally PKey also features OpenSSL::PKey::DH, an implementation of 00388 * the Diffie-Hellman key exchange protocol based on discrete logarithms 00389 * in finite fields, the same basis that DSA is built on. 00390 * The Diffie-Hellman protocol can be used to exchange (symmetric) keys 00391 * over insecure channels without needing any prior joint knowledge 00392 * between the participating parties. As the security of DH demands 00393 * relatively long "public keys" (i.e. the part that is overtly 00394 * transmitted between participants) DH tends to be quite slow. If 00395 * security or speed is your primary concern, OpenSSL::PKey::EC offers 00396 * another implementation of the Diffie-Hellman protocol. 00397 * 00398 */ 00399 mPKey = rb_define_module_under(mOSSL, "PKey"); 00400 00401 /* Document-class: OpenSSL::PKey::PKeyError 00402 * 00403 *Raised when errors occur during PKey#sign or PKey#verify. 00404 */ 00405 ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); 00406 00407 /* Document-class: OpenSSL::PKey::PKey 00408 * 00409 * An abstract class that bundles signature creation (PKey#sign) and 00410 * validation (PKey#verify) that is common to all implementations except 00411 * OpenSSL::PKey::DH 00412 * * OpenSSL::PKey::RSA 00413 * * OpenSSL::PKey::DSA 00414 * * OpenSSL::PKey::EC 00415 */ 00416 cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); 00417 00418 rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); 00419 00420 rb_define_alloc_func(cPKey, ossl_pkey_alloc); 00421 rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); 00422 00423 rb_define_method(cPKey, "sign", ossl_pkey_sign, 2); 00424 rb_define_method(cPKey, "verify", ossl_pkey_verify, 3); 00425 00426 id_private_q = rb_intern("private?"); 00427 00428 /* 00429 * INIT rsa, dsa, dh, ec 00430 */ 00431 Init_ossl_rsa(); 00432 Init_ossl_dsa(); 00433 Init_ossl_dh(); 00434 Init_ossl_ec(); 00435 } 00436 00437