ext/rubysl/openssl/ossl_pkey_dh.c in rubysl-openssl-1.0.2 vs ext/rubysl/openssl/ossl_pkey_dh.c in rubysl-openssl-2.0.0
- old
+ new
@@ -1,7 +1,7 @@
/*
- * $Id: ossl_pkey_dh.c 28004 2010-05-24 23:58:49Z shyouhei $
+ * $Id$
* 'OpenSSL for Ruby' project
* Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
* All rights reserved.
*/
/*
@@ -11,12 +11,12 @@
#if !defined(OPENSSL_NO_DH)
#include "ossl.h"
#define GetPKeyDH(obj, pkey) do { \
- GetPKey(obj, pkey); \
- if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH) { /* PARANOIA? */ \
+ GetPKey((obj), (pkey)); \
+ if (EVP_PKEY_type((pkey)->type) != EVP_PKEY_DH) { /* PARANOIA? */ \
ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \
} \
} while (0)
#define DH_HAS_PRIVATE(dh) ((dh)->priv_key)
@@ -40,11 +40,11 @@
static VALUE
dh_instance(VALUE klass, DH *dh)
{
EVP_PKEY *pkey;
VALUE obj;
-
+
if (!dh) {
return Qfalse;
}
if (!(pkey = EVP_PKEY_new())) {
return Qfalse;
@@ -79,44 +79,94 @@
}
/*
* Private
*/
+#if defined(HAVE_DH_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB
+struct dh_blocking_gen_arg {
+ DH *dh;
+ int size;
+ int gen;
+ BN_GENCB *cb;
+ int result;
+};
+
+static void *
+dh_blocking_gen(void *arg)
+{
+ struct dh_blocking_gen_arg *gen = (struct dh_blocking_gen_arg *)arg;
+ gen->result = DH_generate_parameters_ex(gen->dh, gen->size, gen->gen, gen->cb);
+ return 0;
+}
+#endif
+
static DH *
dh_generate(int size, int gen)
{
- DH *dh;
-
- dh = DH_generate_parameters(size, gen,
- rb_block_given_p() ? ossl_generate_cb : NULL,
- NULL);
+#if defined(HAVE_DH_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB
+ BN_GENCB cb;
+ struct ossl_generate_cb_arg cb_arg;
+ struct dh_blocking_gen_arg gen_arg;
+ DH *dh = DH_new();
+
if (!dh) return 0;
- if (!DH_generate_key(dh)) {
+ memset(&cb_arg, 0, sizeof(struct ossl_generate_cb_arg));
+ if (rb_block_given_p())
+ cb_arg.yield = 1;
+ BN_GENCB_set(&cb, ossl_generate_cb_2, &cb_arg);
+ gen_arg.dh = dh;
+ gen_arg.size = size;
+ gen_arg.gen = gen;
+ gen_arg.cb = &cb;
+ if (cb_arg.yield == 1) {
+ /* we cannot release GVL when callback proc is supplied */
+ dh_blocking_gen(&gen_arg);
+ } else {
+ /* there's a chance to unblock */
+ rb_thread_call_without_gvl(dh_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
+ }
+
+ if (!gen_arg.result) {
DH_free(dh);
+ if (cb_arg.state) rb_jump_tag(cb_arg.state);
return 0;
}
+#else
+ DH *dh;
+ dh = DH_generate_parameters(size, gen, rb_block_given_p() ? ossl_generate_cb : NULL, NULL);
+ if (!dh) return 0;
+#endif
+
+ if (!DH_generate_key(dh)) {
+ DH_free(dh);
+ return 0;
+ }
+
return dh;
}
/*
* call-seq:
* DH.generate(size [, generator]) -> dh
*
- * === Parameters
- * * +size+ is an integer representing the desired key size. Keys smaller than 1024 should be considered insecure.
- * * +generator+ is a small number > 1, typically 2 or 5.
+ * Creates a new DH instance from scratch by generating the private and public
+ * components alike.
*
+ * === Parameters
+ * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
+ * * +generator+ is a small number > 1, typically 2 or 5.
+ *
*/
static VALUE
ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass)
{
DH *dh ;
int g = 2;
VALUE size, gen, obj;
-
+
if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) {
g = NUM2INT(gen);
}
dh = dh_generate(NUM2INT(size), g);
obj = dh_instance(klass, dh);
@@ -130,20 +180,28 @@
/*
* call-seq:
* DH.new([size [, generator] | string]) -> dh
*
- * === Parameters
- * * +size+ is an integer representing the desired key size. Keys smaller than 1024 should be considered insecure.
- * * +generator+ is a small number > 1, typically 2 or 5.
- * * +string+ contains the DER or PEM encoded key.
+ * Either generates a DH instance from scratch or by reading already existing
+ * DH parameters from +string+. Note that when reading a DH instance from
+ * data that was encoded from a DH instance by using DH#to_pem or DH#to_der
+ * the result will *not* contain a public/private key pair yet. This needs to
+ * be generated using DH#generate_key! first.
*
- * === Examples
- * * DH.new -> dh
- * * DH.new(1024) -> dh
- * * DH.new(1024, 5) -> dh
- * * DH.new(File.read('key.pem')) -> dh
+ * === Parameters
+ * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
+ * * +generator+ is a small number > 1, typically 2 or 5.
+ * * +string+ contains the DER or PEM encoded key.
+ *
+ * === Examples
+ * DH.new # -> dh
+ * DH.new(1024) # -> dh
+ * DH.new(1024, 5) # -> dh
+ * #Reading DH parameters
+ * dh = DH.new(File.read('parameters.pem')) # -> dh, but no public/private key yet
+ * dh.generate_key! # -> dh with public and private key
*/
static VALUE
ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
@@ -167,15 +225,17 @@
else {
arg = ossl_to_der_if_possible(arg);
in = ossl_obj2bio(arg);
dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
if (!dh){
- BIO_reset(in);
+ OSSL_BIO_reset(in);
dh = d2i_DHparams_bio(in, NULL);
}
BIO_free(in);
- if (!dh) ossl_raise(eDHError, NULL);
+ if (!dh) {
+ ossl_raise(eDHError, NULL);
+ }
}
if (!EVP_PKEY_assign_DH(pkey, dh)) {
DH_free(dh);
ossl_raise(eDHError, NULL);
}
@@ -184,10 +244,12 @@
/*
* call-seq:
* dh.public? -> true | false
*
+ * Indicates whether this DH instance has a public key associated with it or
+ * not. The public key may be retrieved with DH#pub_key.
*/
static VALUE
ossl_dh_is_public(VALUE self)
{
EVP_PKEY *pkey;
@@ -199,25 +261,30 @@
/*
* call-seq:
* dh.private? -> true | false
*
+ * Indicates whether this DH instance has a private key associated with it or
+ * not. The private key may be retrieved with DH#priv_key.
*/
static VALUE
ossl_dh_is_private(VALUE self)
{
EVP_PKEY *pkey;
GetPKeyDH(self, pkey);
-
+
return (DH_PRIVATE(pkey->pkey.dh)) ? Qtrue : Qfalse;
}
/*
* call-seq:
* dh.to_pem -> aString
*
+ * Encodes this DH to its PEM encoding. Note that any existing per-session
+ * public/private keys will *not* get encoded, just the Diffie-Hellman
+ * parameters will be encoded.
*/
static VALUE
ossl_dh_export(VALUE self)
{
EVP_PKEY *pkey;
@@ -239,24 +306,28 @@
/*
* call-seq:
* dh.to_der -> aString
*
+ * Encodes this DH to its DER encoding. Note that any existing per-session
+ * public/private keys will *not* get encoded, just the Diffie-Hellman
+ * parameters will be encoded.
+
*/
static VALUE
ossl_dh_to_der(VALUE self)
-{
+{
EVP_PKEY *pkey;
unsigned char *p;
long len;
VALUE str;
GetPKeyDH(self, pkey);
if((len = i2d_DHparams(pkey->pkey.dh, NULL)) <= 0)
ossl_raise(eDHError, NULL);
str = rb_str_new(0, len);
- p = RSTRING_PTR(str);
+ p = (unsigned char *)RSTRING_PTR(str);
if(i2d_DHparams(pkey->pkey.dh, &p) < 0)
ossl_raise(eDHError, NULL);
ossl_str_adjust(str, p);
return str;
@@ -282,11 +353,11 @@
rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dh->p));
rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dh->g));
rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dh->pub_key));
rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dh->priv_key));
-
+
return hash;
}
/*
* call-seq:
@@ -318,19 +389,34 @@
/*
* call-seq:
* dh.public_key -> aDH
*
- * Makes new instance DH PUBLIC_KEY from PRIVATE_KEY
+ * Returns a new DH instance that carries just the public information, i.e.
+ * the prime +p+ and the generator +g+, but no public/private key yet. Such
+ * a pair may be generated using DH#generate_key!. The "public key" needed
+ * for a key exchange with DH#compute_key is considered as per-session
+ * information and may be retrieved with DH#pub_key once a key pair has
+ * been generated.
+ * If the current instance already contains private information (and thus a
+ * valid public/private key pair), this information will no longer be present
+ * in the new instance generated by DH#public_key. This feature is helpful for
+ * publishing the Diffie-Hellman parameters without leaking any of the private
+ * per-session information.
+ *
+ * === Example
+ * dh = OpenSSL::PKey::DH.new(2048) # has public and private key set
+ * public_key = dh.public_key # contains only prime and generator
+ * parameters = public_key.to_der # it's safe to publish this
*/
static VALUE
ossl_dh_to_public_key(VALUE self)
{
EVP_PKEY *pkey;
DH *dh;
VALUE obj;
-
+
GetPKeyDH(self, pkey);
dh = DHparams_dup(pkey->pkey.dh); /* err check perfomed by dh_instance */
obj = dh_instance(CLASS_OF(self), dh);
if (obj == Qfalse) {
DH_free(dh);
@@ -342,18 +428,21 @@
/*
* call-seq:
* dh.check_params -> true | false
*
+ * Validates the Diffie-Hellman parameters associated with this instance.
+ * It checks whether a safe prime and a suitable generator are used. If this
+ * is not the case, +false+ is returned.
*/
static VALUE
ossl_dh_check_params(VALUE self)
{
DH *dh;
EVP_PKEY *pkey;
int codes;
-
+
GetPKeyDH(self, pkey);
dh = pkey->pkey.dh;
if (!DH_check(dh, &codes)) {
return Qfalse;
@@ -362,12 +451,23 @@
return codes == 0 ? Qtrue : Qfalse;
}
/*
* call-seq:
- * dh.generate_key -> self
+ * dh.generate_key! -> self
*
+ * Generates a private and public key unless a private key already exists.
+ * If this DH instance was generated from public DH parameters (e.g. by
+ * encoding the result of DH#public_key), then this method needs to be
+ * called first in order to generate the per-session keys before performing
+ * the actual key exchange.
+ *
+ * === Example
+ * dh = OpenSSL::PKey::DH.new(2048)
+ * public_key = dh.public_key #contains no private/public key yet
+ * public_key.generate_key!
+ * puts public_key.private? # => true
*/
static VALUE
ossl_dh_generate_key(VALUE self)
{
DH *dh;
@@ -383,17 +483,16 @@
/*
* call-seq:
* dh.compute_key(pub_bn) -> aString
*
- * === Parameters
- * * +pub_bn+ is a OpenSSL::BN.
+ * Returns a String containing a shared secret computed from the other party's public value.
+ * See DH_compute_key() for further information.
*
- * Returns aString containing a shared secret computed from the other parties public value.
- *
- * See DH_compute_key() for further information.
- *
+ * === Parameters
+ * * +pub_bn+ is a OpenSSL::BN, *not* the DH instance returned by
+ * DH#public_key as that contains the DH parameters only.
*/
static VALUE
ossl_dh_compute_key(VALUE self, VALUE pub)
{
DH *dh;
@@ -405,11 +504,11 @@
GetPKeyDH(self, pkey);
dh = pkey->pkey.dh;
pub_key = GetBNPtr(pub);
len = DH_size(dh);
str = rb_str_new(0, len);
- if ((len = DH_compute_key(RSTRING_PTR(str), pub_key, dh)) < 0) {
+ if ((len = DH_compute_key((unsigned char *)RSTRING_PTR(str), pub_key, dh)) < 0) {
ossl_raise(eDHError, NULL);
}
rb_str_set_len(str, len);
return str;
@@ -434,14 +533,14 @@
0x48, 0x4c, 0xca, 0x6d, 0x7a, 0xae, 0x18, 0x4a,
0x81, 0x91, 0xb6, 0xce, 0x4d, 0x8e, 0xf6, 0xe5,
0x08, 0x04, 0x8c, 0x52, 0x8f, 0xe3, 0x4a, 0x31,
0x44, 0x47, 0x19, 0xa1, 0x4a, 0xc8, 0x8b, 0xcb,
};
-static unsigned char DEFAULT_DH_512_GEN[] = { 0x02 };
+static unsigned char DEFAULT_DH_512_GEN[] = { 0x02 };
DH *OSSL_DEFAULT_DH_512 = NULL;
-
-/*
+
+/*
* -----BEGIN DH PARAMETERS-----
* MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ
* AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR
* T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC
* -----END DH PARAMETERS-----
@@ -471,12 +570,12 @@
ossl_create_dh(unsigned char *p, size_t plen, unsigned char *g, size_t glen)
{
DH *dh;
if ((dh = DH_new()) == NULL) ossl_raise(eDHError, NULL);
- dh->p = BN_bin2bn(p, plen, NULL);
- dh->g = BN_bin2bn(g, glen, NULL);
+ dh->p = BN_bin2bn(p, rb_long2int(plen), NULL);
+ dh->g = BN_bin2bn(g, rb_long2int(glen), NULL);
if (dh->p == NULL || dh->g == NULL){
DH_free(dh);
ossl_raise(eDHError, NULL);
}
@@ -487,16 +586,49 @@
* INIT
*/
void
Init_ossl_dh()
{
-#if 0 /* let rdoc know about mOSSL and mPKey */
- mOSSL = rb_define_module("OpenSSL");
+#if 0
+ mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */
mPKey = rb_define_module_under(mOSSL, "PKey");
#endif
+ /* Document-class: OpenSSL::PKey::DHError
+ *
+ * Generic exception that is raised if an operation on a DH PKey
+ * fails unexpectedly or in case an instantiation of an instance of DH
+ * fails due to non-conformant input data.
+ */
eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError);
+ /* Document-class: OpenSSL::PKey::DH
+ *
+ * An implementation of the Diffie-Hellman key exchange protocol based on
+ * discrete logarithms in finite fields, the same basis that DSA is built
+ * on.
+ *
+ * === Accessor methods for the Diffie-Hellman parameters
+ * * DH#p
+ * The prime (an OpenSSL::BN) of the Diffie-Hellman parameters.
+ * * DH#g
+ * The generator (an OpenSSL::BN) g of the Diffie-Hellman parameters.
+ * * DH#pub_key
+ * The per-session public key (an OpenSSL::BN) matching the private key.
+ * This needs to be passed to DH#compute_key.
+ * * DH#priv_key
+ * The per-session private key, an OpenSSL::BN.
+ *
+ * === Example of a key exchange
+ * dh1 = OpenSSL::PKey::DH.new(2048)
+ * params = dh1.public_key.to_der #you may send this publicly to the participating party
+ * dh2 = OpenSSL::PKey::DH.new(der)
+ * dh2.generate_key! #generate the per-session key pair
+ * symm_key1 = dh1.compute_key(dh2.pub_key)
+ * symm_key2 = dh2.compute_key(dh1.pub_key)
+ *
+ * puts symm_key1 == symm_key2 # => true
+ */
cDH = rb_define_class_under(mPKey, "DH", cPKey);
rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1);
rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
@@ -507,9 +639,10 @@
rb_define_method(cDH, "to_der", ossl_dh_to_der, 0);
rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0);
rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0);
rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0);
rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1);
+
DEF_OSSL_PKEY_BN(cDH, dh, p);
DEF_OSSL_PKEY_BN(cDH, dh, g);
DEF_OSSL_PKEY_BN(cDH, dh, pub_key);
DEF_OSSL_PKEY_BN(cDH, dh, priv_key);
rb_define_method(cDH, "params", ossl_dh_get_params, 0);