Ruby 1.9.3p327(2012-11-10revision37606)
ext/openssl/ossl_pkey_dsa.c
Go to the documentation of this file.
00001 /*
00002  * $Id: ossl_pkey_dsa.c 32344 2011-06-30 20:20:32Z nobu $
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 #if !defined(OPENSSL_NO_DSA)
00012 
00013 #include "ossl.h"
00014 
00015 #define GetPKeyDSA(obj, pkey) do { \
00016     GetPKey((obj), (pkey)); \
00017     if (EVP_PKEY_type((pkey)->type) != EVP_PKEY_DSA) { /* PARANOIA? */ \
00018         ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \
00019     } \
00020 } while (0)
00021 
00022 #define DSA_HAS_PRIVATE(dsa) ((dsa)->priv_key)
00023 #define DSA_PRIVATE(obj,dsa) (DSA_HAS_PRIVATE(dsa)||OSSL_PKEY_IS_PRIVATE(obj))
00024 
00025 /*
00026  * Classes
00027  */
00028 VALUE cDSA;
00029 VALUE eDSAError;
00030 
00031 /*
00032  * Public
00033  */
00034 static VALUE
00035 dsa_instance(VALUE klass, DSA *dsa)
00036 {
00037     EVP_PKEY *pkey;
00038     VALUE obj;
00039 
00040     if (!dsa) {
00041         return Qfalse;
00042     }
00043     if (!(pkey = EVP_PKEY_new())) {
00044         return Qfalse;
00045     }
00046     if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
00047         EVP_PKEY_free(pkey);
00048         return Qfalse;
00049     }
00050     WrapPKey(klass, obj, pkey);
00051 
00052     return obj;
00053 }
00054 
00055 VALUE
00056 ossl_dsa_new(EVP_PKEY *pkey)
00057 {
00058     VALUE obj;
00059 
00060     if (!pkey) {
00061         obj = dsa_instance(cDSA, DSA_new());
00062     } else {
00063         if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) {
00064             ossl_raise(rb_eTypeError, "Not a DSA key!");
00065         }
00066         WrapPKey(cDSA, obj, pkey);
00067     }
00068     if (obj == Qfalse) {
00069         ossl_raise(eDSAError, NULL);
00070     }
00071 
00072     return obj;
00073 }
00074 
00075 /*
00076  * Private
00077  */
00078 static DSA *
00079 dsa_generate(int size)
00080 {
00081     DSA *dsa;
00082     unsigned char seed[20];
00083     int seed_len = 20, counter;
00084     unsigned long h;
00085 
00086     if (!RAND_bytes(seed, seed_len)) {
00087         return 0;
00088     }
00089     dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h,
00090             rb_block_given_p() ? ossl_generate_cb : NULL,
00091             NULL);
00092     if(!dsa) return 0;
00093 
00094     if (!DSA_generate_key(dsa)) {
00095         DSA_free(dsa);
00096         return 0;
00097     }
00098 
00099     return dsa;
00100 }
00101 
00102 /*
00103  *  call-seq:
00104  *    DSA.generate(size) -> dsa
00105  *
00106  * Creates a new DSA instance by generating a private/public key pair
00107  * from scratch.
00108  *
00109  * === Parameters
00110  * * +size+ is an integer representing the desired key size.
00111  *
00112  */
00113 static VALUE
00114 ossl_dsa_s_generate(VALUE klass, VALUE size)
00115 {
00116     DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */
00117     VALUE obj = dsa_instance(klass, dsa);
00118 
00119     if (obj == Qfalse) {
00120         DSA_free(dsa);
00121         ossl_raise(eDSAError, NULL);
00122     }
00123 
00124     return obj;
00125 }
00126 
00127 /*
00128  *  call-seq:
00129  *    DSA.new([size | string [, pass]) -> dsa
00130  *
00131  * Creates a new DSA instance by reading an existing key from +string+.
00132  *
00133  * === Parameters
00134  * * +size+ is an integer representing the desired key size.
00135  * * +string+ contains a DER or PEM encoded key.
00136  * * +pass+ is a string that contains an optional password.
00137  *
00138  * === Examples
00139  *  DSA.new -> dsa
00140  *  DSA.new(1024) -> dsa
00141  *  DSA.new(File.read('dsa.pem')) -> dsa
00142  *  DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa
00143  *
00144  */
00145 static VALUE
00146 ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
00147 {
00148     EVP_PKEY *pkey;
00149     DSA *dsa;
00150     BIO *in;
00151     char *passwd = NULL;
00152     VALUE arg, pass;
00153 
00154     GetPKey(self, pkey);
00155     if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
00156         dsa = DSA_new();
00157     }
00158     else if (FIXNUM_P(arg)) {
00159         if (!(dsa = dsa_generate(FIX2INT(arg)))) {
00160             ossl_raise(eDSAError, NULL);
00161         }
00162     }
00163     else {
00164         if (!NIL_P(pass)) passwd = StringValuePtr(pass);
00165         arg = ossl_to_der_if_possible(arg);
00166         in = ossl_obj2bio(arg);
00167         dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd);
00168         if (!dsa) {
00169             OSSL_BIO_reset(in);
00170             dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL);
00171         }
00172         if (!dsa) {
00173             OSSL_BIO_reset(in);
00174             dsa = d2i_DSAPrivateKey_bio(in, NULL);
00175         }
00176         if (!dsa) {
00177             OSSL_BIO_reset(in);
00178             dsa = d2i_DSA_PUBKEY_bio(in, NULL);
00179         }
00180         if (!dsa) {
00181             OSSL_BIO_reset(in);
00182             dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL);
00183         }
00184         BIO_free(in);
00185         if (!dsa) {
00186             ERR_clear_error();
00187             ossl_raise(eDSAError, "Neither PUB key nor PRIV key:");
00188         }
00189     }
00190     if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
00191         DSA_free(dsa);
00192         ossl_raise(eDSAError, NULL);
00193     }
00194 
00195     return self;
00196 }
00197 
00198 /*
00199  *  call-seq:
00200  *    dsa.public? -> true | false
00201  *
00202  * Indicates whether this DSA instance has a public key associated with it or
00203  * not. The public key may be retrieved with DSA#public_key.
00204  */
00205 static VALUE
00206 ossl_dsa_is_public(VALUE self)
00207 {
00208     EVP_PKEY *pkey;
00209 
00210     GetPKeyDSA(self, pkey);
00211 
00212     return (pkey->pkey.dsa->pub_key) ? Qtrue : Qfalse;
00213 }
00214 
00215 /*
00216  *  call-seq:
00217  *    dsa.private? -> true | false
00218  *
00219  * Indicates whether this DSA instance has a private key associated with it or
00220  * not. The private key may be retrieved with DSA#private_key.
00221  */
00222 static VALUE
00223 ossl_dsa_is_private(VALUE self)
00224 {
00225     EVP_PKEY *pkey;
00226 
00227     GetPKeyDSA(self, pkey);
00228 
00229     return (DSA_PRIVATE(self, pkey->pkey.dsa)) ? Qtrue : Qfalse;
00230 }
00231 
00232 /*
00233  *  call-seq:
00234  *    dsa.to_pem([cipher, password]) -> aString
00235  *
00236  * Encodes this DSA to its PEM encoding.
00237  *
00238  * === Parameters
00239  * * +cipher+ is an OpenSSL::Cipher.
00240  * * +password+ is a string containing your password.
00241  *
00242  * === Examples
00243  *  DSA.to_pem -> aString
00244  *  DSA.to_pem(cipher, 'mypassword') -> aString
00245  *
00246  */
00247 static VALUE
00248 ossl_dsa_export(int argc, VALUE *argv, VALUE self)
00249 {
00250     EVP_PKEY *pkey;
00251     BIO *out;
00252     const EVP_CIPHER *ciph = NULL;
00253     char *passwd = NULL;
00254     VALUE cipher, pass, str;
00255 
00256     GetPKeyDSA(self, pkey);
00257     rb_scan_args(argc, argv, "02", &cipher, &pass);
00258     if (!NIL_P(cipher)) {
00259         ciph = GetCipherPtr(cipher);
00260         if (!NIL_P(pass)) {
00261             passwd = StringValuePtr(pass);
00262         }
00263     }
00264     if (!(out = BIO_new(BIO_s_mem()))) {
00265         ossl_raise(eDSAError, NULL);
00266     }
00267     if (DSA_HAS_PRIVATE(pkey->pkey.dsa)) {
00268         if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph,
00269                                          NULL, 0, ossl_pem_passwd_cb, passwd)){
00270             BIO_free(out);
00271             ossl_raise(eDSAError, NULL);
00272         }
00273     } else {
00274         if (!PEM_write_bio_DSA_PUBKEY(out, pkey->pkey.dsa)) {
00275             BIO_free(out);
00276             ossl_raise(eDSAError, NULL);
00277         }
00278     }
00279     str = ossl_membio2str(out);
00280 
00281     return str;
00282 }
00283 
00284 /*
00285  *  call-seq:
00286  *    dsa.to_der -> aString
00287  *
00288  * Encodes this DSA to its DER encoding.
00289  *
00290  */
00291 static VALUE
00292 ossl_dsa_to_der(VALUE self)
00293 {
00294     EVP_PKEY *pkey;
00295     int (*i2d_func)_((DSA*, unsigned char**));
00296     unsigned char *p;
00297     long len;
00298     VALUE str;
00299 
00300     GetPKeyDSA(self, pkey);
00301     if(DSA_HAS_PRIVATE(pkey->pkey.dsa))
00302         i2d_func = (int(*)_((DSA*,unsigned char**)))i2d_DSAPrivateKey;
00303     else
00304         i2d_func = i2d_DSA_PUBKEY;
00305     if((len = i2d_func(pkey->pkey.dsa, NULL)) <= 0)
00306         ossl_raise(eDSAError, NULL);
00307     str = rb_str_new(0, len);
00308     p = (unsigned char *)RSTRING_PTR(str);
00309     if(i2d_func(pkey->pkey.dsa, &p) < 0)
00310         ossl_raise(eDSAError, NULL);
00311     ossl_str_adjust(str, p);
00312 
00313     return str;
00314 }
00315 
00316 /*
00317  *  call-seq:
00318  *    dsa.params -> hash
00319  *
00320  * Stores all parameters of key to the hash
00321  * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
00322  * Don't use :-)) (I's up to you)
00323  */
00324 static VALUE
00325 ossl_dsa_get_params(VALUE self)
00326 {
00327     EVP_PKEY *pkey;
00328     VALUE hash;
00329 
00330     GetPKeyDSA(self, pkey);
00331 
00332     hash = rb_hash_new();
00333 
00334     rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dsa->p));
00335     rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.dsa->q));
00336     rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dsa->g));
00337     rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dsa->pub_key));
00338     rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dsa->priv_key));
00339 
00340     return hash;
00341 }
00342 
00343 /*
00344  *  call-seq:
00345  *    dsa.to_text -> aString
00346  *
00347  * Prints all parameters of key to buffer
00348  * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
00349  * Don't use :-)) (I's up to you)
00350  */
00351 static VALUE
00352 ossl_dsa_to_text(VALUE self)
00353 {
00354     EVP_PKEY *pkey;
00355     BIO *out;
00356     VALUE str;
00357 
00358     GetPKeyDSA(self, pkey);
00359     if (!(out = BIO_new(BIO_s_mem()))) {
00360         ossl_raise(eDSAError, NULL);
00361     }
00362     if (!DSA_print(out, pkey->pkey.dsa, 0)) { /* offset = 0 */
00363         BIO_free(out);
00364         ossl_raise(eDSAError, NULL);
00365     }
00366     str = ossl_membio2str(out);
00367 
00368     return str;
00369 }
00370 
00371 /*
00372  *  call-seq:
00373  *    dsa.public_key -> aDSA
00374  *
00375  * Returns a new DSA instance that carries just the public key information.
00376  * If the current instance has also private key information, this will no
00377  * longer be present in the new instance. This feature is helpful for
00378  * publishing the public key information without leaking any of the private
00379  * information.
00380  *
00381  * === Example
00382  *  dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information
00383  *  pub_key = dsa.public_key # has only the public part available
00384  *  pub_key_der = pub_key.to_der # it's safe to publish this
00385  *
00386  *
00387  */
00388 static VALUE
00389 ossl_dsa_to_public_key(VALUE self)
00390 {
00391     EVP_PKEY *pkey;
00392     DSA *dsa;
00393     VALUE obj;
00394 
00395     GetPKeyDSA(self, pkey);
00396     /* err check performed by dsa_instance */
00397     dsa = DSAPublicKey_dup(pkey->pkey.dsa);
00398     obj = dsa_instance(CLASS_OF(self), dsa);
00399     if (obj == Qfalse) {
00400         DSA_free(dsa);
00401         ossl_raise(eDSAError, NULL);
00402     }
00403     return obj;
00404 }
00405 
00406 #define ossl_dsa_buf_size(pkey) (DSA_size((pkey)->pkey.dsa)+16)
00407 
00408 /*
00409  *  call-seq:
00410  *    dsa.syssign(string) -> aString
00411  *
00412  * Computes and returns the DSA signature of +string+, where +string+ is
00413  * expected to be an already-computed message digest of the original input
00414  * data. The signature is issued using the private key of this DSA instance.
00415  *
00416  * === Parameters
00417  * * +string+ is a message digest of the original input data to be signed
00418  *
00419  * === Example
00420  *  dsa = OpenSSL::PKey::DSA.new(2048)
00421  *  doc = "Sign me"
00422  *  digest = OpenSSL::Digest::SHA1.digest(doc)
00423  *  sig = dsa.syssign(digest)
00424  *
00425  *
00426  */
00427 static VALUE
00428 ossl_dsa_sign(VALUE self, VALUE data)
00429 {
00430     EVP_PKEY *pkey;
00431     unsigned int buf_len;
00432     VALUE str;
00433 
00434     GetPKeyDSA(self, pkey);
00435     StringValue(data);
00436     if (!DSA_PRIVATE(self, pkey->pkey.dsa)) {
00437         ossl_raise(eDSAError, "Private DSA key needed!");
00438     }
00439     str = rb_str_new(0, ossl_dsa_buf_size(pkey));
00440     if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
00441                   (unsigned char *)RSTRING_PTR(str),
00442                   &buf_len, pkey->pkey.dsa)) { /* type is ignored (0) */
00443         ossl_raise(eDSAError, NULL);
00444     }
00445     rb_str_set_len(str, buf_len);
00446 
00447     return str;
00448 }
00449 
00450 /*
00451  *  call-seq:
00452  *    dsa.sysverify(digest, sig) -> true | false
00453  *
00454  * Verifies whether the signature is valid given the message digest input. It
00455  * does so by validating +sig+ using the public key of this DSA instance.
00456  *
00457  * === Parameters
00458  * * +digest+ is a message digest of the original input data to be signed
00459  * * +sig+ is a DSA signature value
00460  *
00461  * === Example
00462  *  dsa = OpenSSL::PKey::DSA.new(2048)
00463  *  doc = "Sign me"
00464  *  digest = OpenSSL::Digest::SHA1.digest(doc)
00465  *  sig = dsa.syssign(digest)
00466  *  puts dsa.sysverify(digest, sig) # => true
00467  *
00468  */
00469 static VALUE
00470 ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
00471 {
00472     EVP_PKEY *pkey;
00473     int ret;
00474 
00475     GetPKeyDSA(self, pkey);
00476     StringValue(digest);
00477     StringValue(sig);
00478     /* type is ignored (0) */
00479     ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest),
00480                      (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey->pkey.dsa);
00481     if (ret < 0) {
00482         ossl_raise(eDSAError, NULL);
00483     }
00484     else if (ret == 1) {
00485         return Qtrue;
00486     }
00487 
00488     return Qfalse;
00489 }
00490 
00491 OSSL_PKEY_BN(dsa, p)
00492 OSSL_PKEY_BN(dsa, q)
00493 OSSL_PKEY_BN(dsa, g)
00494 OSSL_PKEY_BN(dsa, pub_key)
00495 OSSL_PKEY_BN(dsa, priv_key)
00496 
00497 /*
00498  * INIT
00499  */
00500 void
00501 Init_ossl_dsa()
00502 {
00503 #if 0
00504     mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */
00505     mPKey = rb_define_module_under(mOSSL, "PKey");
00506 #endif
00507 
00508     /* Document-class: OpenSSL::PKey::DSAError
00509      *
00510      * Generic exception that is raised if an operation on a DSA PKey
00511      * fails unexpectedly or in case an instantiation of an instance of DSA
00512      * fails due to non-conformant input data.
00513      */
00514     eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError);
00515 
00516     /* Document-class: OpenSSL::PKey::DSA
00517      *
00518      * DSA, the Digital Signature Algorithm, is specified in NIST's
00519      * FIPS 186-3. It is an asymmetric public key algorithm that may be used
00520      * similar to e.g. RSA.
00521      * Please note that for OpenSSL versions prior to 1.0.0 the digest
00522      * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or
00523      * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing
00524      * signatures with a DSA key using OpenSSL::PKey#sign.
00525      * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted,
00526      * any Digest may be used for signing.
00527      */
00528     cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
00529 
00530     rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
00531     rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
00532 
00533     rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
00534     rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
00535     rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0);
00536     rb_define_method(cDSA, "export", ossl_dsa_export, -1);
00537     rb_define_alias(cDSA, "to_pem", "export");
00538     rb_define_alias(cDSA, "to_s", "export");
00539     rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0);
00540     rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
00541     rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
00542     rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
00543 
00544     DEF_OSSL_PKEY_BN(cDSA, dsa, p);
00545     DEF_OSSL_PKEY_BN(cDSA, dsa, q);
00546     DEF_OSSL_PKEY_BN(cDSA, dsa, g);
00547     DEF_OSSL_PKEY_BN(cDSA, dsa, pub_key);
00548     DEF_OSSL_PKEY_BN(cDSA, dsa, priv_key);
00549 
00550     rb_define_method(cDSA, "params", ossl_dsa_get_params, 0);
00551 }
00552 
00553 #else /* defined NO_DSA */
00554 void
00555 Init_ossl_dsa()
00556 {
00557 }
00558 #endif /* NO_DSA */
00559