An implementation of the Diffie-Hellman key exchange protocol based on discrete logarithms in finite fields, the same basis that DSA
is built on.
The prime (an OpenSSL::BN
) of the Diffie-Hellman parameters.
The generator (an OpenSSL::BN
) g of the Diffie-Hellman parameters.
The per-session public key (an OpenSSL::BN
) matching the private key. This needs to be passed to DH#compute_key
.
The per-session private key, an OpenSSL::BN
.
dh1 = OpenSSL::PKey::DH.new(2048) der = 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
Creates a new DH
instance from scratch by generating the private and public components alike.
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); if (obj == Qfalse) { DH_free(dh); ossl_raise(eDHError, NULL); } return obj; }
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.
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.
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; DH *dh; int g = 2; BIO *in; VALUE arg, gen; GetPKey(self, pkey); if(rb_scan_args(argc, argv, "02", &arg, &gen) == 0) { dh = DH_new(); } else if (RB_INTEGER_TYPE_P(arg)) { if (!NIL_P(gen)) { g = NUM2INT(gen); } if (!(dh = dh_generate(NUM2INT(arg), g))) { ossl_raise(eDHError, NULL); } } else { arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL); if (!dh){ OSSL_BIO_reset(in); dh = d2i_DHparams_bio(in, NULL); } BIO_free(in); if (!dh) { ossl_raise(eDHError, NULL); } } if (!EVP_PKEY_assign_DH(pkey, dh)) { DH_free(dh); ossl_raise(eDHError, NULL); } return self; }
Returns a String containing a shared secret computed from the other party's public value. See DH_compute_key() for further information.
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; const BIGNUM *pub_key, *dh_p; VALUE str; int len; GetDH(self, dh); DH_get0_pqg(dh, &dh_p, NULL, NULL); if (!dh_p) ossl_raise(eDHError, "incomplete DH"); pub_key = GetBNPtr(pub); len = DH_size(dh); str = rb_str_new(0, len); 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; }
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) { DH *dh; BIO *out; VALUE str; GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDHError, NULL); } if (!PEM_write_bio_DHparams(out, dh)) { BIO_free(out); ossl_raise(eDHError, NULL); } str = ossl_membio2str(out); return str; }
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.
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; GetDH(self, dh); if (!DH_generate_key(dh)) ossl_raise(eDHError, "Failed to generate key"); return self; }
static VALUE ossl_dh_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; DH *dh, *dh_other; const BIGNUM *pub, *priv; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eDHError, "DH already initialized"); GetDH(other, dh_other); dh = DHparams_dup(dh_other); if (!dh) ossl_raise(eDHError, "DHparams_dup"); EVP_PKEY_assign_DH(pkey, dh); DH_get0_key(dh_other, &pub, &priv); if (pub) { BIGNUM *pub2 = BN_dup(pub); BIGNUM *priv2 = BN_dup(priv); if (!pub2 || priv && !priv2) { BN_clear_free(pub2); BN_clear_free(priv2); ossl_raise(eDHError, "BN_dup"); } DH_set0_key(dh, pub2, priv2); } return self; }
Stores all parameters of key to the hash INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! Don't use :-)) (I's up to you)
static VALUE ossl_dh_get_params(VALUE self) { DH *dh; VALUE hash; const BIGNUM *p, *q, *g, *pub_key, *priv_key; GetDH(self, dh); DH_get0_pqg(dh, &p, &q, &g); DH_get0_key(dh, &pub_key, &priv_key); hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g)); rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key)); rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key)); return hash; }
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; int codes; GetDH(self, dh); if (!DH_check(dh, &codes)) { return Qfalse; } return codes == 0 ? Qtrue : Qfalse; }
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) { DH *dh; const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, NULL, &bn); #if !defined(OPENSSL_NO_ENGINE) return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse; #else return bn ? Qtrue : Qfalse; #endif }
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) { DH *dh; const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, &bn, NULL); return bn ? Qtrue : Qfalse; }
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.
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) { DH *orig_dh, *dh; VALUE obj; GetDH(self, orig_dh); dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */ obj = dh_instance(rb_obj_class(self), dh); if (obj == Qfalse) { DH_free(dh); ossl_raise(eDHError, NULL); } return obj; }
Sets pub_key and priv_key for the DH
instance. priv_key may be nil
.
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) { DH *dh; unsigned char *p; long len; VALUE str; GetDH(self, dh); if((len = i2d_DHparams(dh, NULL)) <= 0) ossl_raise(eDHError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_DHparams(dh, &p) < 0) ossl_raise(eDHError, NULL); ossl_str_adjust(str, p); return str; }
Prints all parameters of key to buffer INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! Don't use :-)) (I's up to you)
static VALUE ossl_dh_to_text(VALUE self) { DH *dh; BIO *out; VALUE str; GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDHError, NULL); } if (!DHparams_print(out, dh)) { BIO_free(out); ossl_raise(eDHError, NULL); } str = ossl_membio2str(out); return str; }