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