Ruby  2.0.0p247(2013-06-27revision41674)
ext/openssl/ossl_pkey.c
Go to the documentation of this file.
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