Skip to content

Commit cf5ad9a

Browse files
committed
pkey: disallow {DH,DSA,EC,RSA}.new without arguments on OpenSSL 3.0
When OpenSSL::PKey::{DH,DSA,EC,RSA}.new is called without any arguments, it sets up an empty corresponding low-level struct and wraps it in an EVP_PKEY. This is useful when the user later fills the missing fields using low-level setter methods such as OpenSSL::PKey::RSA#set_key. Such setter methods are not compatible with OpenSSL 3.0 or later, where EVP_PKEY is immutable once set up. This means that the ability to create an empty instance is useless. Let's raise ArgumentError if this is attempted.
1 parent 032ed63 commit cf5ad9a

File tree

8 files changed

+71
-20
lines changed

8 files changed

+71
-20
lines changed

ext/openssl/ossl_pkey_dh.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static VALUE eDHError;
4343
* If called without arguments, an empty instance without any parameter or key
4444
* components is created. Use #set_pqg to manually set the parameters afterwards
4545
* (and optionally #set_key to set private and public key components).
46+
* This form is not compatible with OpenSSL 3.0 or later.
4647
*
4748
* If a String is given, tries to parse it as a DER- or PEM- encoded parameters.
4849
* See also OpenSSL::PKey.read which can parse keys of any kinds.
@@ -58,14 +59,15 @@ static VALUE eDHError;
5859
*
5960
* Examples:
6061
* # Creating an instance from scratch
61-
* # Note that this is deprecated and will not work on OpenSSL 3.0 or later.
62+
* # Note that this is deprecated and will result in ArgumentError when
63+
* # using OpenSSL 3.0 or later.
6264
* dh = OpenSSL::PKey::DH.new
6365
* dh.set_pqg(bn_p, nil, bn_g)
6466
*
6567
* # Generating a parameters and a key pair
6668
* dh = OpenSSL::PKey::DH.new(2048) # An alias of OpenSSL::PKey::DH.generate(2048)
6769
*
68-
* # Reading DH parameters
70+
* # Reading DH parameters from a PEM-encoded string
6971
* dh_params = OpenSSL::PKey::DH.new(File.read('parameters.pem')) # loads parameters only
7072
* dh = OpenSSL::PKey.generate_key(dh_params) # generates a key pair
7173
*/
@@ -84,10 +86,15 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
8486

8587
/* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */
8688
if (rb_scan_args(argc, argv, "01", &arg) == 0) {
89+
#ifdef OSSL_HAVE_IMMUTABLE_PKEY
90+
rb_raise(rb_eArgError, "OpenSSL::PKey::DH.new cannot be called " \
91+
"without arguments; pkeys are immutable on OpenSSL 3.0");
92+
#else
8793
dh = DH_new();
8894
if (!dh)
8995
ossl_raise(eDHError, "DH_new");
9096
goto legacy;
97+
#endif
9198
}
9299

93100
arg = ossl_to_der_if_possible(arg);

ext/openssl/ossl_pkey_dsa.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ static VALUE eDSAError;
5656
*
5757
* If called without arguments, creates a new instance with no key components
5858
* set. They can be set individually by #set_pqg and #set_key.
59+
* This form is not compatible with OpenSSL 3.0 or later.
5960
*
6061
* If called with a String, tries to parse as DER or PEM encoding of a \DSA key.
6162
* See also OpenSSL::PKey.read which can parse keys of any kinds.
@@ -96,10 +97,15 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
9697
/* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
9798
rb_scan_args(argc, argv, "02", &arg, &pass);
9899
if (argc == 0) {
100+
#ifdef OSSL_HAVE_IMMUTABLE_PKEY
101+
rb_raise(rb_eArgError, "OpenSSL::PKey::DSA.new cannot be called " \
102+
"without arguments; pkeys are immutable on OpenSSL 3.0");
103+
#else
99104
dsa = DSA_new();
100105
if (!dsa)
101106
ossl_raise(eDSAError, "DSA_new");
102107
goto legacy;
108+
#endif
103109
}
104110

105111
pass = ossl_pem_passwd_value(pass);

ext/openssl/ossl_pkey_ec.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,14 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self)
147147

148148
rb_scan_args(argc, argv, "02", &arg, &pass);
149149
if (NIL_P(arg)) {
150+
#ifdef OSSL_HAVE_IMMUTABLE_PKEY
151+
rb_raise(rb_eArgError, "OpenSSL::PKey::EC.new cannot be called " \
152+
"without arguments; pkeys are immutable on OpenSSL 3.0");
153+
#else
150154
if (!(ec = EC_KEY_new()))
151155
ossl_raise(eECError, "EC_KEY_new");
152156
goto legacy;
157+
#endif
153158
}
154159
else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
155160
ec = ec_key_new_from_group(arg);

ext/openssl/ossl_pkey_rsa.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ static VALUE eRSAError;
5959
* If called without arguments, creates a new instance with no key components
6060
* set. They can be set individually by #set_key, #set_factors, and
6161
* #set_crt_params.
62+
* This form is not compatible with OpenSSL 3.0 or later.
6263
*
6364
* If called with a String, tries to parse as DER or PEM encoding of an \RSA key.
6465
* Note that if _password_ is not specified, but the key is encrypted with a
@@ -89,10 +90,15 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
8990
/* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
9091
rb_scan_args(argc, argv, "02", &arg, &pass);
9192
if (argc == 0) {
93+
#ifdef OSSL_HAVE_IMMUTABLE_PKEY
94+
rb_raise(rb_eArgError, "OpenSSL::PKey::RSA.new cannot be called " \
95+
"without arguments; pkeys are immutable on OpenSSL 3.0");
96+
#else
9297
rsa = RSA_new();
9398
if (!rsa)
9499
ossl_raise(eRSAError, "RSA_new");
95100
goto legacy;
101+
#endif
96102
}
97103

98104
pass = ossl_pem_passwd_value(pass);

test/openssl/test_pkey_dh.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
77
NEW_KEYLEN = 2048
88

99
def test_new_empty
10-
dh = OpenSSL::PKey::DH.new
11-
assert_equal nil, dh.p
12-
assert_equal nil, dh.priv_key
10+
# pkeys are immutable in OpenSSL >= 3.0
11+
if openssl?(3, 0, 0)
12+
assert_raise(ArgumentError) { OpenSSL::PKey::DH.new }
13+
else
14+
dh = OpenSSL::PKey::DH.new
15+
assert_nil(dh.p)
16+
assert_nil(dh.priv_key)
17+
end
1318
end
1419

1520
def test_new_generate

test/openssl/test_pkey_dsa.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ def test_new_break
3434
end
3535

3636
def test_new_empty
37-
key = OpenSSL::PKey::DSA.new
38-
assert_nil(key.p)
39-
assert_raise(OpenSSL::PKey::PKeyError) { key.to_der }
37+
# pkeys are immutable in OpenSSL >= 3.0
38+
if openssl?(3, 0, 0)
39+
assert_raise(ArgumentError) { OpenSSL::PKey::DSA.new }
40+
else
41+
key = OpenSSL::PKey::DSA.new
42+
assert_nil(key.p)
43+
assert_raise(OpenSSL::PKey::PKeyError) { key.to_der }
44+
end
4045
end
4146

4247
def test_generate

test/openssl/test_pkey_ec.rb

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,9 @@
44
if defined?(OpenSSL)
55

66
class OpenSSL::TestEC < OpenSSL::PKeyTestCase
7-
def test_ec_key
7+
def test_ec_key_new
88
key1 = OpenSSL::PKey::EC.generate("prime256v1")
99

10-
# PKey is immutable in OpenSSL >= 3.0; constructing an empty EC object is
11-
# deprecated
12-
if !openssl?(3, 0, 0)
13-
key2 = OpenSSL::PKey::EC.new
14-
key2.group = key1.group
15-
key2.private_key = key1.private_key
16-
key2.public_key = key1.public_key
17-
assert_equal key1.to_der, key2.to_der
18-
end
19-
2010
key3 = OpenSSL::PKey::EC.new(key1)
2111
assert_equal key1.to_der, key3.to_der
2212

@@ -35,6 +25,23 @@ def test_ec_key
3525
end
3626
end
3727

28+
def test_ec_key_new_empty
29+
# pkeys are immutable in OpenSSL >= 3.0; constructing an empty EC object is
30+
# disallowed
31+
if openssl?(3, 0, 0)
32+
assert_raise(ArgumentError) { OpenSSL::PKey::EC.new }
33+
else
34+
key = OpenSSL::PKey::EC.new
35+
assert_nil(key.group)
36+
37+
p256 = Fixtures.pkey("p256")
38+
key.group = p256.group
39+
key.private_key = p256.private_key
40+
key.public_key = p256.public_key
41+
assert_equal(p256.to_der, key.to_der)
42+
end
43+
end
44+
3845
def test_builtin_curves
3946
builtin_curves = OpenSSL::PKey::EC.builtin_curves
4047
assert_not_empty builtin_curves

test/openssl/test_pkey_rsa.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ def test_new_public_exponent
6161
assert_equal 3, key.e
6262
end
6363

64+
def test_new_empty
65+
# pkeys are immutable in OpenSSL >= 3.0
66+
if openssl?(3, 0, 0)
67+
assert_raise(ArgumentError) { OpenSSL::PKey::RSA.new }
68+
else
69+
key = OpenSSL::PKey::RSA.new
70+
assert_nil(key.n)
71+
end
72+
end
73+
6474
def test_s_generate
6575
key1 = OpenSSL::PKey::RSA.generate(2048)
6676
assert_equal 2048, key1.n.num_bits
@@ -181,7 +191,7 @@ def test_verify_empty_rsa
181191
assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") {
182192
rsa.verify("SHA1", "a", "b")
183193
}
184-
end
194+
end unless openssl?(3, 0, 0) # Empty RSA key is not allowed in OpenSSL 3.0
185195

186196
def test_sign_verify_pss
187197
key = Fixtures.pkey("rsa2048")

0 commit comments

Comments
 (0)