From e0b4c56956ef4ef3ab47cf5d2eed55fbcc5152e8 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 17 May 2020 18:25:38 +0900 Subject: [PATCH 1/3] pkey: implement #to_text using EVP API Use EVP_PKEY_print_private() instead of the low-level API *_print() functions, such as RSA_print(). EVP_PKEY_print_*() family was added in OpenSSL 1.0.0. Note that it falls back to EVP_PKEY_print_public() and EVP_PKEY_print_params() as necessary. This is required for EVP_PKEY_DH type for which _private() fails if the private component is not set in the pkey object. Since the new API works in the same way for all key types, we now implement #to_text in the base class OpenSSL::PKey::PKey rather than in each subclass. --- ext/openssl/ossl_pkey.c | 38 +++++++++++++++++++++++++++++++++++++ ext/openssl/ossl_pkey_dh.c | 29 ---------------------------- ext/openssl/ossl_pkey_dsa.c | 29 ---------------------------- ext/openssl/ossl_pkey_ec.c | 27 -------------------------- ext/openssl/ossl_pkey_rsa.c | 31 ------------------------------ test/openssl/test_pkey.rb | 5 +++++ 6 files changed, 43 insertions(+), 116 deletions(-) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 593788e1e..b92c8a663 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -539,6 +539,43 @@ ossl_pkey_inspect(VALUE self) OBJ_nid2sn(nid)); } +/* + * call-seq: + * pkey.to_text -> string + * + * Dumps key parameters, public key, and private key components contained in + * the key into a human-readable text. + * + * This is intended for debugging purpose. + * + * See also the man page EVP_PKEY_print_private(3). + */ +static VALUE +ossl_pkey_to_text(VALUE self) +{ + EVP_PKEY *pkey; + BIO *bio; + + GetPKey(self, pkey); + if (!(bio = BIO_new(BIO_s_mem()))) + ossl_raise(ePKeyError, "BIO_new"); + + if (EVP_PKEY_print_private(bio, pkey, 0, NULL) == 1) + goto out; + OSSL_BIO_reset(bio); + if (EVP_PKEY_print_public(bio, pkey, 0, NULL) == 1) + goto out; + OSSL_BIO_reset(bio); + if (EVP_PKEY_print_params(bio, pkey, 0, NULL) == 1) + goto out; + + BIO_free(bio); + ossl_raise(ePKeyError, "EVP_PKEY_print_params"); + + out: + return ossl_membio2str(bio); +} + VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der) { @@ -1077,6 +1114,7 @@ Init_ossl_pkey(void) rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); rb_define_method(cPKey, "oid", ossl_pkey_oid, 0); rb_define_method(cPKey, "inspect", ossl_pkey_inspect, 0); + rb_define_method(cPKey, "to_text", ossl_pkey_to_text, 0); rb_define_method(cPKey, "private_to_der", ossl_pkey_private_to_der, -1); rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1); rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0); diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 6b477b077..acd3bf474 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -266,34 +266,6 @@ ossl_dh_get_params(VALUE self) return hash; } -/* - * call-seq: - * dh.to_text -> aString - * - * 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; -} - /* * call-seq: * dh.public_key -> aDH @@ -426,7 +398,6 @@ Init_ossl_dh(void) rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1); rb_define_method(cDH, "public?", ossl_dh_is_public, 0); rb_define_method(cDH, "private?", ossl_dh_is_private, 0); - rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); rb_define_method(cDH, "export", ossl_dh_export, 0); rb_define_alias(cDH, "to_pem", "export"); rb_define_alias(cDH, "to_s", "export"); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 1c5a8a737..f017cceb4 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -264,34 +264,6 @@ ossl_dsa_get_params(VALUE self) return hash; } -/* - * call-seq: - * dsa.to_text -> aString - * - * Prints all parameters of key to buffer - * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! - * Don't use :-)) (I's up to you) - */ -static VALUE -ossl_dsa_to_text(VALUE self) -{ - DSA *dsa; - BIO *out; - VALUE str; - - GetDSA(self, dsa); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eDSAError, NULL); - } - if (!DSA_print(out, dsa, 0)) { /* offset = 0 */ - BIO_free(out); - ossl_raise(eDSAError, NULL); - } - str = ossl_membio2str(out); - - return str; -} - /* * call-seq: * dsa.public_key -> aDSA @@ -469,7 +441,6 @@ Init_ossl_dsa(void) rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); - rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0); rb_define_method(cDSA, "export", ossl_dsa_export, -1); rb_define_alias(cDSA, "to_pem", "export"); rb_define_alias(cDSA, "to_s", "export"); diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index deca4f43b..22afbdc2e 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -412,32 +412,6 @@ ossl_ec_key_to_der(VALUE self) else return ossl_pkey_export_spki(self, 1); } - -/* - * call-seq: - * key.to_text => String - * - * See the OpenSSL documentation for EC_KEY_print() - */ -static VALUE ossl_ec_key_to_text(VALUE self) -{ - EC_KEY *ec; - BIO *out; - VALUE str; - - GetEC(self, ec); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eECError, "BIO_new(BIO_s_mem())"); - } - if (!EC_KEY_print(out, ec, 0)) { - BIO_free(out); - ossl_raise(eECError, "EC_KEY_print"); - } - str = ossl_membio2str(out); - - return str; -} - /* * call-seq: * key.generate_key! => self @@ -1601,7 +1575,6 @@ void Init_ossl_ec(void) rb_define_method(cEC, "export", ossl_ec_key_export, -1); rb_define_alias(cEC, "to_pem", "export"); rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0); - rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0); rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 43f82cb29..7a7e66dbd 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -587,36 +587,6 @@ ossl_rsa_get_params(VALUE self) return hash; } -/* - * call-seq: - * rsa.to_text => String - * - * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! - * - * Dumps all parameters of a keypair to a String - * - * Don't use :-)) (It's up to you) - */ -static VALUE -ossl_rsa_to_text(VALUE self) -{ - RSA *rsa; - BIO *out; - VALUE str; - - GetRSA(self, rsa); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eRSAError, NULL); - } - if (!RSA_print(out, rsa, 0)) { /* offset = 0 */ - BIO_free(out); - ossl_raise(eRSAError, NULL); - } - str = ossl_membio2str(out); - - return str; -} - /* * call-seq: * rsa.public_key -> RSA @@ -738,7 +708,6 @@ Init_ossl_rsa(void) rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); - rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0); rb_define_method(cRSA, "export", ossl_rsa_export, -1); rb_define_alias(cRSA, "to_pem", "export"); rb_define_alias(cRSA, "to_s", "export"); diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index 0a516f98e..4a539d8c4 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -169,4 +169,9 @@ def test_compare? key1.compare?(key4) end end + + def test_to_text + rsa = Fixtures.pkey("rsa1024") + assert_include rsa.to_text, "publicExponent" + end end From 48a6c391ef47c9a12c3d2c96a5a2db4f44295182 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 15 Apr 2021 19:11:32 +0900 Subject: [PATCH 2/3] pkey: implement {DH,DSA,RSA}#public_key in Ruby The low-level API that is used to implement #public_key is deprecated in OpenSSL 3.0. It is actually very simple to implement in another way, using existing methods only, in much shorter code. Let's do it. While we are at it, the documentation is updated to recommend against using #public_key. Now that OpenSSL::PKey::PKey implements public_to_der method, there is no real use case for #public_key in newly written Ruby programs. --- ext/openssl/ossl_pkey_dh.c | 63 ++++++++--------------------------- ext/openssl/ossl_pkey_dsa.c | 42 ----------------------- ext/openssl/ossl_pkey_rsa.c | 58 +------------------------------- lib/openssl/pkey.rb | 55 ++++++++++++++++++++++++++++++ test/openssl/test_pkey_rsa.rb | 37 ++++++++++---------- 5 files changed, 87 insertions(+), 168 deletions(-) diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index acd3bf474..a512b209d 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -266,48 +266,6 @@ ossl_dh_get_params(VALUE self) return hash; } -/* - * call-seq: - * dh.public_key -> aDH - * - * 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 *orig_dh, *dh; - VALUE obj; - - obj = rb_obj_alloc(rb_obj_class(self)); - GetPKey(obj, pkey); - - GetDH(self, orig_dh); - dh = DHparams_dup(orig_dh); - if (!dh) - ossl_raise(eDHError, "DHparams_dup"); - if (!EVP_PKEY_assign_DH(pkey, dh)) { - DH_free(dh); - ossl_raise(eDHError, "EVP_PKEY_assign_DH"); - } - return obj; -} - /* * call-seq: * dh.params_ok? -> true | false @@ -384,14 +342,20 @@ Init_ossl_dh(void) * The per-session private key, an OpenSSL::BN. * * === Example of a key exchange - * 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) + * # you may send the parameters (der) and own public key (pub1) publicly + * # to the participating party + * dh1 = OpenSSL::PKey::DH.new(2048) + * der = dh1.to_der + * pub1 = dh1.pub_key + * + * # the other party generates its per-session key pair + * dhparams = OpenSSL::PKey::DH.new(der) + * dh2 = OpenSSL::PKey.generate_key(dhparams) + * pub2 = dh2.pub_key * - * puts symm_key1 == symm_key2 # => true + * symm_key1 = dh1.compute_key(pub2) + * symm_key2 = dh2.compute_key(pub1) + * puts symm_key1 == symm_key2 # => true */ cDH = rb_define_class_under(mPKey, "DH", cPKey); rb_define_method(cDH, "initialize", ossl_dh_initialize, -1); @@ -402,7 +366,6 @@ Init_ossl_dh(void) rb_define_alias(cDH, "to_pem", "export"); rb_define_alias(cDH, "to_s", "export"); 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); DEF_OSSL_PKEY_BN(cDH, dh, p); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index f017cceb4..ab9ac781e 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -264,47 +264,6 @@ ossl_dsa_get_params(VALUE self) return hash; } -/* - * call-seq: - * dsa.public_key -> aDSA - * - * Returns a new DSA instance that carries just the public key information. - * If the current instance has also private key information, this will no - * longer be present in the new instance. This feature is helpful for - * publishing the public key information without leaking any of the private - * information. - * - * === Example - * dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information - * pub_key = dsa.public_key # has only the public part available - * pub_key_der = pub_key.to_der # it's safe to publish this - * - * - */ -static VALUE -ossl_dsa_to_public_key(VALUE self) -{ - EVP_PKEY *pkey, *pkey_new; - DSA *dsa; - VALUE obj; - - GetPKeyDSA(self, pkey); - obj = rb_obj_alloc(rb_obj_class(self)); - GetPKey(obj, pkey_new); - -#define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \ - (i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa)) - dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey)); -#undef DSAPublicKey_dup - if (!dsa) - ossl_raise(eDSAError, "DSAPublicKey_dup"); - if (!EVP_PKEY_assign_DSA(pkey_new, dsa)) { - DSA_free(dsa); - ossl_raise(eDSAError, "EVP_PKEY_assign_DSA"); - } - return obj; -} - /* * call-seq: * dsa.syssign(string) -> aString @@ -445,7 +404,6 @@ Init_ossl_dsa(void) rb_define_alias(cDSA, "to_pem", "export"); rb_define_alias(cDSA, "to_s", "export"); rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0); - rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0); rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1); rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 7a7e66dbd..1c5476cdc 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -390,7 +390,7 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) * data = "Sign me!" * pkey = OpenSSL::PKey::RSA.new(2048) * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") - * pub_key = pkey.public_key + * pub_key = OpenSSL::PKey.read(pkey.public_to_der) * puts pub_key.verify_pss("SHA256", signature, data, * salt_length: :auto, mgf1_hash: "SHA256") # => true */ @@ -587,61 +587,6 @@ ossl_rsa_get_params(VALUE self) return hash; } -/* - * call-seq: - * rsa.public_key -> RSA - * - * Makes new RSA instance containing the public key from the private key. - */ -static VALUE -ossl_rsa_to_public_key(VALUE self) -{ - EVP_PKEY *pkey, *pkey_new; - RSA *rsa; - VALUE obj; - - GetPKeyRSA(self, pkey); - obj = rb_obj_alloc(rb_obj_class(self)); - GetPKey(obj, pkey_new); - - rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); - if (!rsa) - ossl_raise(eRSAError, "RSAPublicKey_dup"); - if (!EVP_PKEY_assign_RSA(pkey_new, rsa)) { - RSA_free(rsa); - ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); - } - return obj; -} - -/* - * TODO: Test me - -static VALUE -ossl_rsa_blinding_on(VALUE self) -{ - RSA *rsa; - - GetRSA(self, rsa); - - if (RSA_blinding_on(rsa, ossl_bn_ctx) != 1) { - ossl_raise(eRSAError, NULL); - } - return self; -} - -static VALUE -ossl_rsa_blinding_off(VALUE self) -{ - RSA *rsa; - - GetRSA(self, rsa); - RSA_blinding_off(rsa); - - return self; -} - */ - /* * Document-method: OpenSSL::PKey::RSA#set_key * call-seq: @@ -712,7 +657,6 @@ Init_ossl_rsa(void) rb_define_alias(cRSA, "to_pem", "export"); rb_define_alias(cRSA, "to_s", "export"); rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0); - rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0); rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, -1); rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1); rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1); diff --git a/lib/openssl/pkey.rb b/lib/openssl/pkey.rb index 53ee52f98..569559e1c 100644 --- a/lib/openssl/pkey.rb +++ b/lib/openssl/pkey.rb @@ -10,6 +10,30 @@ module OpenSSL::PKey class DH include OpenSSL::Marshal + # :call-seq: + # dh.public_key -> dhnew + # + # Returns a new DH instance that carries just the \DH parameters. + # + # Contrary to the method name, the returned DH object contains only + # parameters and not the public key. + # + # This method is provided for backwards compatibility. In most cases, there + # is no need to call this method. + # + # For the purpose of re-generating the key pair while keeping the + # parameters, check OpenSSL::PKey.generate_key. + # + # Example: + # # OpenSSL::PKey::DH.generate by default generates a random key pair + # dh1 = OpenSSL::PKey::DH.generate(2048) + # p dh1.priv_key #=> # + # dhcopy = dh1.public_key + # p dhcopy.priv_key #=> nil + def public_key + DH.new(to_der) + end + # :call-seq: # dh.compute_key(pub_bn) -> string # @@ -89,6 +113,22 @@ def new(*args, &blk) # :nodoc: class DSA include OpenSSL::Marshal + # :call-seq: + # dsa.public_key -> dsanew + # + # Returns a new DSA instance that carries just the \DSA parameters and the + # public key. + # + # This method is provided for backwards compatibility. In most cases, there + # is no need to call this method. + # + # For the purpose of serializing the public key, to PEM or DER encoding of + # X.509 SubjectPublicKeyInfo format, check PKey#public_to_pem and + # PKey#public_to_der. + def public_key + OpenSSL::PKey.read(public_to_der) + end + class << self # :call-seq: # DSA.generate(size) -> dsa @@ -159,6 +199,21 @@ def to_bn(conversion_form = group.point_conversion_form) class RSA include OpenSSL::Marshal + # :call-seq: + # rsa.public_key -> rsanew + # + # Returns a new RSA instance that carries just the public key components. + # + # This method is provided for backwards compatibility. In most cases, there + # is no need to call this method. + # + # For the purpose of serializing the public key, to PEM or DER encoding of + # X.509 SubjectPublicKeyInfo format, check PKey#public_to_pem and + # PKey#public_to_der. + def public_key + OpenSSL::PKey.read(public_to_der) + end + class << self # :call-seq: # RSA.generate(size, exponent = 65537) -> RSA diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb index d1e68dbc9..5f8d04e75 100644 --- a/test/openssl/test_pkey_rsa.rb +++ b/test/openssl/test_pkey_rsa.rb @@ -69,29 +69,28 @@ def test_private end def test_new - key = OpenSSL::PKey::RSA.new 512 - pem = key.public_key.to_pem - OpenSSL::PKey::RSA.new pem - assert_equal([], OpenSSL.errors) - end + key = OpenSSL::PKey::RSA.new(512) + assert_equal 512, key.n.num_bits + assert_equal 65537, key.e + assert_not_nil key.d - def test_new_exponent_default - assert_equal(65537, OpenSSL::PKey::RSA.new(512).e) + # Specify public exponent + key2 = OpenSSL::PKey::RSA.new(512, 3) + assert_equal 512, key2.n.num_bits + assert_equal 3, key2.e + assert_not_nil key2.d end - def test_new_with_exponent - 1.upto(30) do |idx| - e = (2 ** idx) + 1 - key = OpenSSL::PKey::RSA.new(512, e) - assert_equal(e, key.e) - end - end + def test_s_generate + key1 = OpenSSL::PKey::RSA.generate(512) + assert_equal 512, key1.n.num_bits + assert_equal 65537, key1.e - def test_generate - key = OpenSSL::PKey::RSA.generate(512, 17) - assert_equal 512, key.n.num_bits - assert_equal 17, key.e - assert_not_nil key.d + # Specify public exponent + key2 = OpenSSL::PKey::RSA.generate(512, 3) + assert_equal 512, key2.n.num_bits + assert_equal 3, key2.e + assert_not_nil key2.d end def test_new_break From 797e9f8e0865785df5a95c7344fa62a0a5c70e0b Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 10 Jul 2020 14:34:51 +0900 Subject: [PATCH 3/3] pkey/dh, pkey/ec: use EVP_PKEY_check() family Use EVP_PKEY_param_check() instead of DH_check() if available. Also, use EVP_PKEY_public_check() instead of EC_KEY_check_key(). EVP_PKEY_*check() is part of the EVP API and is meant to replace those low-level functions. They were added by OpenSSL 1.1.1. It is currently not provided by LibreSSL. --- ext/openssl/extconf.rb | 3 +++ ext/openssl/ossl_pkey_dh.c | 27 +++++++++++++++++++++++---- ext/openssl/ossl_pkey_ec.c | 23 +++++++++++++++++++---- test/openssl/test_pkey_dh.rb | 16 ++++++++++++++++ 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 5cb28f309..8812b7de8 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -165,6 +165,9 @@ def find_openssl_library have_func("EVP_PBE_scrypt") have_func("SSL_CTX_set_post_handshake_auth") +# added in 1.1.1 +have_func("EVP_PKEY_check") + Logging::message "=== Checking done. ===\n" create_header diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index a512b209d..ca782bbe5 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -273,19 +273,38 @@ ossl_dh_get_params(VALUE self) * 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. + * + * See also the man page EVP_PKEY_param_check(3). */ static VALUE ossl_dh_check_params(VALUE self) { + int ret; +#ifdef HAVE_EVP_PKEY_CHECK + EVP_PKEY *pkey; + EVP_PKEY_CTX *pctx; + + GetPKey(self, pkey); + pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); + if (!pctx) + ossl_raise(eDHError, "EVP_PKEY_CTX_new"); + ret = EVP_PKEY_param_check(pctx); + EVP_PKEY_CTX_free(pctx); +#else DH *dh; int codes; GetDH(self, dh); - if (!DH_check(dh, &codes)) { - return Qfalse; - } + ret = DH_check(dh, &codes) == 1 && codes == 0; +#endif - return codes == 0 ? Qtrue : Qfalse; + if (ret == 1) + return Qtrue; + else { + /* DH_check_ex() will put error entry on failure */ + ossl_clear_error(); + return Qfalse; + } } /* diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 22afbdc2e..af59cfabd 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -438,20 +438,35 @@ static VALUE ossl_ec_key_generate_key(VALUE self) } /* - * call-seq: - * key.check_key => true + * call-seq: + * key.check_key => true * - * Raises an exception if the key is invalid. + * Raises an exception if the key is invalid. * - * See the OpenSSL documentation for EC_KEY_check_key() + * See also the man page EVP_PKEY_public_check(3). */ static VALUE ossl_ec_key_check_key(VALUE self) { +#ifdef HAVE_EVP_PKEY_CHECK + EVP_PKEY *pkey; + EVP_PKEY_CTX *pctx; + int ret; + + GetPKey(self, pkey); + pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); + if (!pctx) + ossl_raise(eDHError, "EVP_PKEY_CTX_new"); + ret = EVP_PKEY_public_check(pctx); + EVP_PKEY_CTX_free(pctx); + if (ret != 1) + ossl_raise(eECError, "EVP_PKEY_public_check"); +#else EC_KEY *ec; GetEC(self, ec); if (EC_KEY_check_key(ec) != 1) ossl_raise(eECError, "EC_KEY_check_key"); +#endif return Qtrue; } diff --git a/test/openssl/test_pkey_dh.rb b/test/openssl/test_pkey_dh.rb index 279ce1984..f80af8f84 100644 --- a/test/openssl/test_pkey_dh.rb +++ b/test/openssl/test_pkey_dh.rb @@ -86,6 +86,22 @@ def test_key_exchange assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key)) end + def test_params_ok? + dh0 = Fixtures.pkey("dh1024") + + dh1 = OpenSSL::PKey::DH.new(OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(dh0.p), + OpenSSL::ASN1::Integer(dh0.g) + ])) + assert_equal(true, dh1.params_ok?) + + dh2 = OpenSSL::PKey::DH.new(OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(dh0.p + 1), + OpenSSL::ASN1::Integer(dh0.g) + ])) + assert_equal(false, dh2.params_ok?) + end + def test_dup dh = Fixtures.pkey("dh1024") dh2 = dh.dup