Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkey: deprecate PKey::*#set_* and PKey::{DH,EC}#generate_key! #480

Merged
merged 5 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions ext/openssl/ossl_pkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ static VALUE ossl_##_keytype##_get_##_name(VALUE self) \
OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \
_type##_get0_##_group(obj, NULL, &bn))

#if !OSSL_OPENSSL_PREREQ(3, 0, 0)
#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \
/* \
* call-seq: \
Expand Down Expand Up @@ -181,6 +182,21 @@ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \
} \
return self; \
}
#else
#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \
static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \
{ \
rb_raise(ePKeyError, \
#_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \
}

#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \
static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \
{ \
rb_raise(ePKeyError, \
#_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \
}
#endif

#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \
OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \
Expand Down
9 changes: 5 additions & 4 deletions ext/openssl/ossl_pkey_dh.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@ VALUE eDHError;
*
* Examples:
* # Creating an instance from scratch
* dh = DH.new
* # Note that this is deprecated and will not work on OpenSSL 3.0 or later.
* dh = OpenSSL::PKey::DH.new
* dh.set_pqg(bn_p, nil, bn_g)
*
* # Generating a parameters and a key pair
* dh = DH.new(2048) # An alias of DH.generate(2048)
* dh = OpenSSL::PKey::DH.new(2048) # An alias of OpenSSL::PKey::DH.generate(2048)
*
* # 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
* dh_params = OpenSSL::PKey::DH.new(File.read('parameters.pem')) # loads parameters only
* dh = OpenSSL::PKey.generate_key(dh_params) # generates a key pair
*/
static VALUE
ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
Expand Down
16 changes: 16 additions & 0 deletions ext/openssl/ossl_pkey_ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ ossl_ec_key_get_group(VALUE self)
static VALUE
ossl_ec_key_set_group(VALUE self, VALUE group_v)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
EC_KEY *ec;
EC_GROUP *group;

Expand All @@ -247,6 +250,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v)
ossl_raise(eECError, "EC_KEY_set_group");

return group_v;
#endif
}

/*
Expand Down Expand Up @@ -275,6 +279,9 @@ static VALUE ossl_ec_key_get_private_key(VALUE self)
*/
static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
EC_KEY *ec;
BIGNUM *bn = NULL;

Expand All @@ -294,6 +301,7 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
}

return private_key;
#endif
}

/*
Expand Down Expand Up @@ -322,6 +330,9 @@ static VALUE ossl_ec_key_get_public_key(VALUE self)
*/
static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
EC_KEY *ec;
EC_POINT *point = NULL;

Expand All @@ -341,6 +352,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
}

return public_key;
#endif
}

/*
Expand Down Expand Up @@ -430,13 +442,17 @@ ossl_ec_key_to_der(VALUE self)
*/
static VALUE ossl_ec_key_generate_key(VALUE self)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
EC_KEY *ec;

GetEC(self, ec);
if (EC_KEY_generate_key(ec) != 1)
ossl_raise(eECError, "EC_KEY_generate_key");

return self;
#endif
}

/*
Expand Down
50 changes: 40 additions & 10 deletions lib/openssl/pkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,19 @@ def public_key
# * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by
# DH#public_key as that contains the DH parameters only.
def compute_key(pub_bn)
peer = dup
peer.set_key(pub_bn, nil)
derive(peer)
# FIXME: This is constructing an X.509 SubjectPublicKeyInfo and is very
# inefficient
obj = OpenSSL::ASN1.Sequence([
OpenSSL::ASN1.Sequence([
OpenSSL::ASN1.ObjectId("dhKeyAgreement"),
OpenSSL::ASN1.Sequence([
OpenSSL::ASN1.Integer(p),
OpenSSL::ASN1.Integer(g),
]),
]),
OpenSSL::ASN1.BitString(OpenSSL::ASN1.Integer(pub_bn).to_der),
])
derive(OpenSSL::PKey.read(obj.to_der))
end

# :call-seq:
Expand All @@ -61,14 +71,29 @@ def compute_key(pub_bn)
# called first in order to generate the per-session keys before performing
# the actual key exchange.
#
# <b>Deprecated in version 3.0</b>. This method is incompatible with
# OpenSSL 3.0.0 or later.
#
# See also OpenSSL::PKey.generate_key.
#
# 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
# # DEPRECATED USAGE: This will not work on OpenSSL 3.0 or later
# dh0 = OpenSSL::PKey::DH.new(2048)
# dh = dh0.public_key # #public_key only copies the DH parameters (contrary to the name)
# dh.generate_key!
# puts dh.private? # => true
# puts dh0.pub_key == dh.pub_key #=> false
#
# # With OpenSSL::PKey.generate_key
# dh0 = OpenSSL::PKey::DH.new(2048)
# dh = OpenSSL::PKey.generate_key(dh0)
# puts dh0.pub_key == dh.pub_key #=> false
def generate_key!
if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000
raise DHError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \
"use OpenSSL::PKey.generate_key instead"
end

unless priv_key
tmp = OpenSSL::PKey.generate_key(self)
set_key(tmp.pub_key, tmp.priv_key)
Expand Down Expand Up @@ -249,9 +274,14 @@ def dsa_verify_asn1(data, sig)
# This method is provided for backwards compatibility, and calls #derive
# internally.
def dh_compute_key(pubkey)
peer = OpenSSL::PKey::EC.new(group)
peer.public_key = pubkey
derive(peer)
obj = OpenSSL::ASN1.Sequence([
OpenSSL::ASN1.Sequence([
OpenSSL::ASN1.ObjectId("id-ecPublicKey"),
group.to_der,
]),
OpenSSL::ASN1.BitString(pubkey.to_octet_string(:uncompressed)),
])
derive(OpenSSL::PKey.read(obj.to_der))
end
end

Expand Down
56 changes: 36 additions & 20 deletions test/openssl/test_pkey_dh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ def test_new_break
end

def test_derive_key
dh1 = Fixtures.pkey("dh1024").generate_key!
dh2 = Fixtures.pkey("dh1024").generate_key!
params = Fixtures.pkey("dh1024")
dh1 = OpenSSL::PKey.generate_key(params)
dh2 = OpenSSL::PKey.generate_key(params)
dh1_pub = OpenSSL::PKey.read(dh1.public_to_der)
dh2_pub = OpenSSL::PKey.read(dh2.public_to_der)

z = dh1.g.mod_exp(dh1.priv_key, dh1.p).mod_exp(dh2.priv_key, dh1.p).to_s(2)
assert_equal z, dh1.derive(dh2_pub)
assert_equal z, dh2.derive(dh1_pub)

assert_raise(OpenSSL::PKey::PKeyError) { params.derive(dh1_pub) }
assert_raise(OpenSSL::PKey::PKeyError) { dh1_pub.derive(params) }

assert_equal z, dh1.compute_key(dh2.pub_key)
assert_equal z, dh2.compute_key(dh1.pub_key)
end
Expand Down Expand Up @@ -74,19 +79,16 @@ def test_public_key
end

def test_generate_key
dh = Fixtures.pkey("dh1024").public_key # creates a copy
# Deprecated in v3.0.0; incompatible with OpenSSL 3.0
dh = Fixtures.pkey("dh1024").public_key # creates a copy with params only
assert_no_key(dh)
dh.generate_key!
assert_key(dh)
end

def test_key_exchange
dh = Fixtures.pkey("dh1024")
dh2 = dh.public_key
dh.generate_key!
dh2.generate_key!
assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key))
end
end if !openssl?(3, 0, 0)

def test_params_ok?
dh0 = Fixtures.pkey("dh1024")
Expand All @@ -105,13 +107,32 @@ def test_params_ok?
end

def test_dup
dh = Fixtures.pkey("dh1024")
dh2 = dh.dup
assert_equal dh.to_der, dh2.to_der # params
assert_equal_params dh, dh2 # keys
dh2.set_pqg(dh2.p + 1, nil, dh2.g)
assert_not_equal dh2.p, dh.p
assert_equal dh2.g, dh.g
# Parameters only
dh1 = Fixtures.pkey("dh1024")
dh2 = dh1.dup
assert_equal dh1.to_der, dh2.to_der
assert_not_equal nil, dh1.p
assert_not_equal nil, dh1.g
assert_equal [dh1.p, dh1.g], [dh2.p, dh2.g]
assert_equal nil, dh1.pub_key
assert_equal nil, dh1.priv_key
assert_equal [dh1.pub_key, dh1.priv_key], [dh2.pub_key, dh2.priv_key]

# PKey is immutable in OpenSSL >= 3.0
if !openssl?(3, 0, 0)
dh2.set_pqg(dh2.p + 1, nil, dh2.g)
assert_not_equal dh2.p, dh1.p
end

# With a key pair
dh3 = OpenSSL::PKey.generate_key(Fixtures.pkey("dh1024"))
dh4 = dh3.dup
assert_equal dh3.to_der, dh4.to_der
assert_equal dh1.to_der, dh4.to_der # encodes parameters only
assert_equal [dh1.p, dh1.g], [dh4.p, dh4.g]
assert_not_equal nil, dh3.pub_key
assert_not_equal nil, dh3.priv_key
assert_equal [dh3.pub_key, dh3.priv_key], [dh4.pub_key, dh4.priv_key]
end

def test_marshal
Expand All @@ -123,11 +144,6 @@ def test_marshal

private

def assert_equal_params(dh1, dh2)
assert_equal(dh1.g, dh2.g)
assert_equal(dh1.p, dh2.p)
end

def assert_no_key(dh)
assert_equal(false, dh.public?)
assert_equal(false, dh.private?)
Expand Down
8 changes: 6 additions & 2 deletions test/openssl/test_pkey_dsa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,12 @@ def test_dup
key = Fixtures.pkey("dsa1024")
key2 = key.dup
assert_equal key.params, key2.params
key2.set_pqg(key2.p + 1, key2.q, key2.g)
assert_not_equal key.params, key2.params

# PKey is immutable in OpenSSL >= 3.0
if !openssl?(3, 0, 0)
key2.set_pqg(key2.p + 1, key2.q, key2.g)
assert_not_equal key.params, key2.params
end
end

def test_marshal
Expand Down
Loading