From 3fe8387950f83874372172a79233ffc0d5d335b0 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 15 Apr 2021 19:11:32 +0900 Subject: [ruby/openssl] 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. https://github.com/ruby/openssl/commit/48a6c391ef --- ext/openssl/lib/openssl/pkey.rb | 55 +++++++++++++++++++++++++++++++++++ ext/openssl/ossl_pkey_dh.c | 63 +++++++++-------------------------------- ext/openssl/ossl_pkey_dsa.c | 42 --------------------------- ext/openssl/ossl_pkey_rsa.c | 58 +------------------------------------ 4 files changed, 69 insertions(+), 149 deletions(-) (limited to 'ext/openssl') diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb index 53ee52f98b..569559e1ce 100644 --- a/ext/openssl/lib/openssl/pkey.rb +++ b/ext/openssl/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 @@ module OpenSSL::PKey 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 @@ module OpenSSL::PKey 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/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index acd3bf474e..a512b209d3 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 f017cceb4a..ab9ac781e8 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 7a7e66dbda..1c5476cdcd 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); -- cgit v1.2.3