6daba0
From 316cb2a41f154e4663d7e7fead60cfc0bfa86af9 Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Mon, 12 Apr 2021 13:55:10 +0900
6daba0
Subject: [PATCH 1/2] pkey: do not check NULL argument in ossl_pkey_new()
6daba0
6daba0
Passing NULL to ossl_pkey_new() makes no sense in the first place, and
6daba0
in fact it is ensured not to be NULL in all cases.
6daba0
---
6daba0
 ext/openssl/ossl_pkey.c | 6 +-----
6daba0
 ext/openssl/ossl_pkey.h | 1 +
6daba0
 2 files changed, 2 insertions(+), 5 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
6daba0
index f9f5162e..820e4a2c 100644
6daba0
--- a/ext/openssl/ossl_pkey.c
6daba0
+++ b/ext/openssl/ossl_pkey.c
6daba0
@@ -38,12 +38,8 @@ static VALUE
6daba0
 pkey_new0(EVP_PKEY *pkey)
6daba0
 {
6daba0
     VALUE klass, obj;
6daba0
-    int type;
6daba0
 
6daba0
-    if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
6daba0
-	ossl_raise(rb_eRuntimeError, "pkey is empty");
6daba0
-
6daba0
-    switch (type) {
6daba0
+    switch (EVP_PKEY_base_id(pkey)) {
6daba0
 #if !defined(OPENSSL_NO_RSA)
6daba0
       case EVP_PKEY_RSA: klass = cRSA; break;
6daba0
 #endif
6daba0
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
6daba0
index 4beede22..f0476780 100644
6daba0
--- a/ext/openssl/ossl_pkey.h
6daba0
+++ b/ext/openssl/ossl_pkey.h
6daba0
@@ -35,6 +35,7 @@ extern const rb_data_type_t ossl_evp_pkey_type;
6daba0
     } \
6daba0
 } while (0)
6daba0
 
6daba0
+/* Takes ownership of the EVP_PKEY */
6daba0
 VALUE ossl_pkey_new(EVP_PKEY *);
6daba0
 void ossl_pkey_check_public_key(const EVP_PKEY *);
6daba0
 EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE);
6daba0
6daba0
From 74f6c6175688502a5bf27ae35367616858630c0f Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Mon, 12 Apr 2021 18:32:40 +0900
6daba0
Subject: [PATCH 2/2] pkey: allocate EVP_PKEY on #initialize
6daba0
6daba0
Allocate an EVP_PKEY when the content is ready: when #initialize
6daba0
or #initialize_copy is called, rather than when a T_DATA is allocated.
6daba0
This is more natural because the lower level API has been deprecated
6daba0
and an EVP_PKEY is becoming the minimum unit of handling keys.
6daba0
---
6daba0
 ext/openssl/ossl_pkey.c     | 15 ++----
6daba0
 ext/openssl/ossl_pkey.h     | 15 ++----
6daba0
 ext/openssl/ossl_pkey_dh.c  | 71 +++++++++++++++++++--------
6daba0
 ext/openssl/ossl_pkey_dsa.c | 93 ++++++++++++++++++++---------------
6daba0
 ext/openssl/ossl_pkey_ec.c  | 91 +++++++++++++++++++----------------
6daba0
 ext/openssl/ossl_pkey_rsa.c | 96 ++++++++++++++++++++++---------------
6daba0
 6 files changed, 218 insertions(+), 163 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
6daba0
index 820e4a2c..ea75d63f 100644
6daba0
--- a/ext/openssl/ossl_pkey.c
6daba0
+++ b/ext/openssl/ossl_pkey.c
6daba0
@@ -54,8 +54,8 @@ pkey_new0(EVP_PKEY *pkey)
6daba0
 #endif
6daba0
       default:           klass = cPKey; break;
6daba0
     }
6daba0
-    obj = NewPKey(klass);
6daba0
-    SetPKey(obj, pkey);
6daba0
+    obj = rb_obj_alloc(klass);
6daba0
+    RTYPEDDATA_DATA(obj) = pkey;
6daba0
     return obj;
6daba0
 }
6daba0
 
6daba0
@@ -511,16 +511,7 @@ DupPKeyPtr(VALUE obj)
6daba0
 static VALUE
6daba0
 ossl_pkey_alloc(VALUE klass)
6daba0
 {
6daba0
-    EVP_PKEY *pkey;
6daba0
-    VALUE obj;
6daba0
-
6daba0
-    obj = NewPKey(klass);
6daba0
-    if (!(pkey = EVP_PKEY_new())) {
6daba0
-	ossl_raise(ePKeyError, NULL);
6daba0
-    }
6daba0
-    SetPKey(obj, pkey);
6daba0
-
6daba0
-    return obj;
6daba0
+    return TypedData_Wrap_Struct(klass, &ossl_evp_pkey_type, NULL);
6daba0
 }
6daba0
 
6daba0
 /*
6daba0
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
6daba0
index f0476780..ed18bc69 100644
6daba0
--- a/ext/openssl/ossl_pkey.h
6daba0
+++ b/ext/openssl/ossl_pkey.h
6daba0
@@ -15,19 +15,10 @@ extern VALUE cPKey;
6daba0
 extern VALUE ePKeyError;
6daba0
 extern const rb_data_type_t ossl_evp_pkey_type;
6daba0
 
6daba0
-#define OSSL_PKEY_SET_PRIVATE(obj) rb_iv_set((obj), "private", Qtrue)
6daba0
-#define OSSL_PKEY_SET_PUBLIC(obj)  rb_iv_set((obj), "private", Qfalse)
6daba0
-#define OSSL_PKEY_IS_PRIVATE(obj)  (rb_iv_get((obj), "private") == Qtrue)
6daba0
+/* For ENGINE */
6daba0
+#define OSSL_PKEY_SET_PRIVATE(obj) rb_ivar_set((obj), rb_intern("private"), Qtrue)
6daba0
+#define OSSL_PKEY_IS_PRIVATE(obj)  (rb_attr_get((obj), rb_intern("private")) == Qtrue)
6daba0
 
6daba0
-#define NewPKey(klass) \
6daba0
-    TypedData_Wrap_Struct((klass), &ossl_evp_pkey_type, 0)
6daba0
-#define SetPKey(obj, pkey) do { \
6daba0
-    if (!(pkey)) { \
6daba0
-	rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!"); \
6daba0
-    } \
6daba0
-    RTYPEDDATA_DATA(obj) = (pkey); \
6daba0
-    OSSL_PKEY_SET_PUBLIC(obj); \
6daba0
-} while (0)
6daba0
 #define GetPKey(obj, pkey) do {\
6daba0
     TypedData_Get_Struct((obj), EVP_PKEY, &ossl_evp_pkey_type, (pkey)); \
6daba0
     if (!(pkey)) { \
6daba0
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
6daba0
index ca782bbe..04c11b21 100644
6daba0
--- a/ext/openssl/ossl_pkey_dh.c
6daba0
+++ b/ext/openssl/ossl_pkey_dh.c
6daba0
@@ -72,34 +72,57 @@ static VALUE
6daba0
 ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
6daba0
 {
6daba0
     EVP_PKEY *pkey;
6daba0
+    int type;
6daba0
     DH *dh;
6daba0
-    BIO *in;
6daba0
+    BIO *in = NULL;
6daba0
     VALUE arg;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
+
6daba0
     /* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */
6daba0
     if (rb_scan_args(argc, argv, "01", &arg) == 0) {
6daba0
         dh = DH_new();
6daba0
         if (!dh)
6daba0
             ossl_raise(eDHError, "DH_new");
6daba0
+        goto legacy;
6daba0
     }
6daba0
-    else {
6daba0
-	arg = ossl_to_der_if_possible(arg);
6daba0
-	in = ossl_obj2bio(&arg;;
6daba0
-	dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
6daba0
-	if (!dh){
6daba0
-	    OSSL_BIO_reset(in);
6daba0
-	    dh = d2i_DHparams_bio(in, NULL);
6daba0
-	}
6daba0
-	BIO_free(in);
6daba0
-	if (!dh) {
6daba0
-	    ossl_raise(eDHError, NULL);
6daba0
-	}
6daba0
+
6daba0
+    arg = ossl_to_der_if_possible(arg);
6daba0
+    in = ossl_obj2bio(&arg;;
6daba0
+
6daba0
+    /*
6daba0
+     * On OpenSSL <= 1.1.1 and current versions of LibreSSL, the generic
6daba0
+     * routine does not support DER-encoded parameters
6daba0
+     */
6daba0
+    dh = d2i_DHparams_bio(in, NULL);
6daba0
+    if (dh)
6daba0
+        goto legacy;
6daba0
+    OSSL_BIO_reset(in);
6daba0
+
6daba0
+    pkey = ossl_pkey_read_generic(in, Qnil);
6daba0
+    BIO_free(in);
6daba0
+    if (!pkey)
6daba0
+        ossl_raise(eDHError, "could not parse pkey");
6daba0
+
6daba0
+    type = EVP_PKEY_base_id(pkey);
6daba0
+    if (type != EVP_PKEY_DH) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        rb_raise(eDHError, "incorrect pkey type: %s", OBJ_nid2sn(type));
6daba0
     }
6daba0
-    if (!EVP_PKEY_assign_DH(pkey, dh)) {
6daba0
-	DH_free(dh);
6daba0
-	ossl_raise(eDHError, NULL);
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
+    return self;
6daba0
+
6daba0
+  legacy:
6daba0
+    BIO_free(in);
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        DH_free(dh);
6daba0
+        ossl_raise(eDHError, "EVP_PKEY_assign_DH");
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
     return self;
6daba0
 }
6daba0
 
6daba0
@@ -110,15 +133,14 @@ ossl_dh_initialize_copy(VALUE self, VALUE other)
6daba0
     DH *dh, *dh_other;
6daba0
     const BIGNUM *pub, *priv;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
6daba0
-	ossl_raise(eDHError, "DH already initialized");
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
     GetDH(other, dh_other);
6daba0
 
6daba0
     dh = DHparams_dup(dh_other);
6daba0
     if (!dh)
6daba0
 	ossl_raise(eDHError, "DHparams_dup");
6daba0
-    EVP_PKEY_assign_DH(pkey, dh);
6daba0
 
6daba0
     DH_get0_key(dh_other, &pub, &priv;;
6daba0
     if (pub) {
6daba0
@@ -133,6 +155,13 @@ ossl_dh_initialize_copy(VALUE self, VALUE other)
6daba0
 	DH_set0_key(dh, pub2, priv2);
6daba0
     }
6daba0
 
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        DH_free(dh);
6daba0
+        ossl_raise(eDHError, "EVP_PKEY_assign_DH");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
     return self;
6daba0
 }
6daba0
 
6daba0
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
6daba0
index 7af00eeb..15724548 100644
6daba0
--- a/ext/openssl/ossl_pkey_dsa.c
6daba0
+++ b/ext/openssl/ossl_pkey_dsa.c
6daba0
@@ -83,50 +83,59 @@ VALUE eDSAError;
6daba0
 static VALUE
6daba0
 ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
6daba0
 {
6daba0
-    EVP_PKEY *pkey, *tmp;
6daba0
-    DSA *dsa = NULL;
6daba0
-    BIO *in;
6daba0
+    EVP_PKEY *pkey;
6daba0
+    DSA *dsa;
6daba0
+    BIO *in = NULL;
6daba0
     VALUE arg, pass;
6daba0
+    int type;
6daba0
+
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
     /* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
6daba0
     rb_scan_args(argc, argv, "02", &arg, &pass);
6daba0
     if (argc == 0) {
6daba0
         dsa = DSA_new();
6daba0
         if (!dsa)
6daba0
             ossl_raise(eDSAError, "DSA_new");
6daba0
+        goto legacy;
6daba0
     }
6daba0
-    else {
6daba0
-	pass = ossl_pem_passwd_value(pass);
6daba0
-	arg = ossl_to_der_if_possible(arg);
6daba0
-	in = ossl_obj2bio(&arg;;
6daba0
-
6daba0
-        tmp = ossl_pkey_read_generic(in, pass);
6daba0
-        if (tmp) {
6daba0
-            if (EVP_PKEY_base_id(tmp) != EVP_PKEY_DSA)
6daba0
-                rb_raise(eDSAError, "incorrect pkey type: %s",
6daba0
-                         OBJ_nid2sn(EVP_PKEY_base_id(tmp)));
6daba0
-            dsa = EVP_PKEY_get1_DSA(tmp);
6daba0
-            EVP_PKEY_free(tmp);
6daba0
-        }
6daba0
-	if (!dsa) {
6daba0
-	    OSSL_BIO_reset(in);
6daba0
-#define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \
6daba0
-	(d2i_of_void *)d2i_DSAPublicKey, PEM_STRING_DSA_PUBLIC, (bp), (void **)(x), (cb), (u))
6daba0
-	    dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL);
6daba0
-#undef PEM_read_bio_DSAPublicKey
6daba0
-	}
6daba0
-	BIO_free(in);
6daba0
-	if (!dsa) {
6daba0
-	    ossl_clear_error();
6daba0
-	    ossl_raise(eDSAError, "Neither PUB key nor PRIV key");
6daba0
-	}
6daba0
-    }
6daba0
-    if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
6daba0
-	DSA_free(dsa);
6daba0
-	ossl_raise(eDSAError, NULL);
6daba0
+
6daba0
+    pass = ossl_pem_passwd_value(pass);
6daba0
+    arg = ossl_to_der_if_possible(arg);
6daba0
+    in = ossl_obj2bio(&arg;;
6daba0
+
6daba0
+    /* DER-encoded DSAPublicKey format isn't supported by the generic routine */
6daba0
+    dsa = (DSA *)PEM_ASN1_read_bio((d2i_of_void *)d2i_DSAPublicKey,
6daba0
+                                   PEM_STRING_DSA_PUBLIC,
6daba0
+                                   in, NULL, NULL, NULL);
6daba0
+    if (dsa)
6daba0
+        goto legacy;
6daba0
+    OSSL_BIO_reset(in);
6daba0
+
6daba0
+    pkey = ossl_pkey_read_generic(in, pass);
6daba0
+    BIO_free(in);
6daba0
+    if (!pkey)
6daba0
+        ossl_raise(eDSAError, "Neither PUB key nor PRIV key");
6daba0
+
6daba0
+    type = EVP_PKEY_base_id(pkey);
6daba0
+    if (type != EVP_PKEY_DSA) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        rb_raise(eDSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
+    return self;
6daba0
 
6daba0
+  legacy:
6daba0
+    BIO_free(in);
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        DSA_free(dsa);
6daba0
+        ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
     return self;
6daba0
 }
6daba0
 
6daba0
@@ -136,16 +145,24 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other)
6daba0
     EVP_PKEY *pkey;
6daba0
     DSA *dsa, *dsa_new;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
6daba0
-	ossl_raise(eDSAError, "DSA already initialized");
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
     GetDSA(other, dsa);
6daba0
 
6daba0
-    dsa_new = ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey, (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa);
6daba0
+    dsa_new = (DSA *)ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey,
6daba0
+                              (d2i_of_void *)d2i_DSAPrivateKey,
6daba0
+                              (char *)dsa);
6daba0
     if (!dsa_new)
6daba0
 	ossl_raise(eDSAError, "ASN1_dup");
6daba0
 
6daba0
-    EVP_PKEY_assign_DSA(pkey, dsa_new);
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa_new) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        DSA_free(dsa_new);
6daba0
+        ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
 
6daba0
     return self;
6daba0
 }
6daba0
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
6daba0
index db80d112..71e63969 100644
6daba0
--- a/ext/openssl/ossl_pkey_ec.c
6daba0
+++ b/ext/openssl/ossl_pkey_ec.c
6daba0
@@ -114,13 +114,16 @@ ossl_ec_key_s_generate(VALUE klass, VALUE arg)
6daba0
     VALUE obj;
6daba0
 
6daba0
     obj = rb_obj_alloc(klass);
6daba0
-    GetPKey(obj, pkey);
6daba0
 
6daba0
     ec = ec_key_new_from_group(arg);
6daba0
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) {
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
         EC_KEY_free(ec);
6daba0
         ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(obj) = pkey;
6daba0
+
6daba0
     if (!EC_KEY_generate_key(ec))
6daba0
 	ossl_raise(eECError, "EC_KEY_generate_key");
6daba0
 
6daba0
@@ -141,51 +144,54 @@ ossl_ec_key_s_generate(VALUE klass, VALUE arg)
6daba0
 static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self)
6daba0
 {
6daba0
     EVP_PKEY *pkey;
6daba0
-    EC_KEY *ec = NULL;
6daba0
+    EC_KEY *ec;
6daba0
+    BIO *in;
6daba0
     VALUE arg, pass;
6daba0
+    int type;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
6daba0
-        ossl_raise(eECError, "EC_KEY already initialized");
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
 
6daba0
     rb_scan_args(argc, argv, "02", &arg, &pass);
6daba0
-
6daba0
     if (NIL_P(arg)) {
6daba0
         if (!(ec = EC_KEY_new()))
6daba0
-	    ossl_raise(eECError, NULL);
6daba0
-    } else if (rb_obj_is_kind_of(arg, cEC)) {
6daba0
-	EC_KEY *other_ec = NULL;
6daba0
+            ossl_raise(eECError, "EC_KEY_new");
6daba0
+        goto legacy;
6daba0
+    }
6daba0
+    else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
6daba0
+        ec = ec_key_new_from_group(arg);
6daba0
+        goto legacy;
6daba0
+    }
6daba0
 
6daba0
-	GetEC(arg, other_ec);
6daba0
-	if (!(ec = EC_KEY_dup(other_ec)))
6daba0
-	    ossl_raise(eECError, NULL);
6daba0
-    } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
6daba0
-	ec = ec_key_new_from_group(arg);
6daba0
-    } else {
6daba0
-        BIO *in = ossl_obj2bio(&arg;;
6daba0
-        EVP_PKEY *tmp;
6daba0
-        pass = ossl_pem_passwd_value(pass);
6daba0
-        tmp = ossl_pkey_read_generic(in, pass);
6daba0
-        if (tmp) {
6daba0
-            if (EVP_PKEY_base_id(tmp) != EVP_PKEY_EC)
6daba0
-                rb_raise(eECError, "incorrect pkey type: %s",
6daba0
-                         OBJ_nid2sn(EVP_PKEY_base_id(tmp)));
6daba0
-            ec = EVP_PKEY_get1_EC_KEY(tmp);
6daba0
-            EVP_PKEY_free(tmp);
6daba0
-        }
6daba0
-	BIO_free(in);
6daba0
+    pass = ossl_pem_passwd_value(pass);
6daba0
+    arg = ossl_to_der_if_possible(arg);
6daba0
+    in = ossl_obj2bio(&arg;;
6daba0
 
6daba0
-	if (!ec) {
6daba0
-	    ossl_clear_error();
6daba0
-	    ec = ec_key_new_from_group(arg);
6daba0
-	}
6daba0
+    pkey = ossl_pkey_read_generic(in, pass);
6daba0
+    BIO_free(in);
6daba0
+    if (!pkey) {
6daba0
+        ossl_clear_error();
6daba0
+        ec = ec_key_new_from_group(arg);
6daba0
+        goto legacy;
6daba0
     }
6daba0
 
6daba0
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) {
6daba0
-	EC_KEY_free(ec);
6daba0
-	ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
6daba0
+    type = EVP_PKEY_base_id(pkey);
6daba0
+    if (type != EVP_PKEY_EC) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        rb_raise(eDSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
+    return self;
6daba0
 
6daba0
+  legacy:
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        EC_KEY_free(ec);
6daba0
+        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
     return self;
6daba0
 }
6daba0
 
6daba0
@@ -195,18 +201,21 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other)
6daba0
     EVP_PKEY *pkey;
6daba0
     EC_KEY *ec, *ec_new;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
6daba0
-	ossl_raise(eECError, "EC already initialized");
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
     GetEC(other, ec);
6daba0
 
6daba0
     ec_new = EC_KEY_dup(ec);
6daba0
     if (!ec_new)
6daba0
 	ossl_raise(eECError, "EC_KEY_dup");
6daba0
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec_new)) {
6daba0
-	EC_KEY_free(ec_new);
6daba0
-	ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
6daba0
+
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec_new) != 1) {
6daba0
+        EC_KEY_free(ec_new);
6daba0
+        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
 
6daba0
     return self;
6daba0
 }
6daba0
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
6daba0
index 8ebd3ec5..b8dbc0e1 100644
6daba0
--- a/ext/openssl/ossl_pkey_rsa.c
6daba0
+++ b/ext/openssl/ossl_pkey_rsa.c
6daba0
@@ -76,51 +76,62 @@ VALUE eRSAError;
6daba0
 static VALUE
6daba0
 ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
6daba0
 {
6daba0
-    EVP_PKEY *pkey, *tmp;
6daba0
-    RSA *rsa = NULL;
6daba0
-    BIO *in;
6daba0
+    EVP_PKEY *pkey;
6daba0
+    RSA *rsa;
6daba0
+    BIO *in = NULL;
6daba0
     VALUE arg, pass;
6daba0
+    int type;
6daba0
+
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
     /* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
6daba0
     rb_scan_args(argc, argv, "02", &arg, &pass);
6daba0
     if (argc == 0) {
6daba0
 	rsa = RSA_new();
6daba0
         if (!rsa)
6daba0
             ossl_raise(eRSAError, "RSA_new");
6daba0
+        goto legacy;
6daba0
     }
6daba0
-    else {
6daba0
-	pass = ossl_pem_passwd_value(pass);
6daba0
-	arg = ossl_to_der_if_possible(arg);
6daba0
-	in = ossl_obj2bio(&arg;;
6daba0
-
6daba0
-        tmp = ossl_pkey_read_generic(in, pass);
6daba0
-        if (tmp) {
6daba0
-            if (EVP_PKEY_base_id(tmp) != EVP_PKEY_RSA)
6daba0
-                rb_raise(eRSAError, "incorrect pkey type: %s",
6daba0
-                         OBJ_nid2sn(EVP_PKEY_base_id(tmp)));
6daba0
-            rsa = EVP_PKEY_get1_RSA(tmp);
6daba0
-            EVP_PKEY_free(tmp);
6daba0
-        }
6daba0
-	if (!rsa) {
6daba0
-	    OSSL_BIO_reset(in);
6daba0
-	    rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
6daba0
-	}
6daba0
-	if (!rsa) {
6daba0
-	    OSSL_BIO_reset(in);
6daba0
-	    rsa = d2i_RSAPublicKey_bio(in, NULL);
6daba0
-	}
6daba0
-	BIO_free(in);
6daba0
-	if (!rsa) {
6daba0
-            ossl_clear_error();
6daba0
-	    ossl_raise(eRSAError, "Neither PUB key nor PRIV key");
6daba0
-	}
6daba0
-    }
6daba0
-    if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
6daba0
-	RSA_free(rsa);
6daba0
-	ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
6daba0
+
6daba0
+    pass = ossl_pem_passwd_value(pass);
6daba0
+    arg = ossl_to_der_if_possible(arg);
6daba0
+    in = ossl_obj2bio(&arg;;
6daba0
+
6daba0
+    /* First try RSAPublicKey format */
6daba0
+    rsa = d2i_RSAPublicKey_bio(in, NULL);
6daba0
+    if (rsa)
6daba0
+        goto legacy;
6daba0
+    OSSL_BIO_reset(in);
6daba0
+    rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
6daba0
+    if (rsa)
6daba0
+        goto legacy;
6daba0
+    OSSL_BIO_reset(in);
6daba0
+
6daba0
+    /* Use the generic routine */
6daba0
+    pkey = ossl_pkey_read_generic(in, pass);
6daba0
+    BIO_free(in);
6daba0
+    if (!pkey)
6daba0
+        ossl_raise(eRSAError, "Neither PUB key nor PRIV key");
6daba0
+
6daba0
+    type = EVP_PKEY_base_id(pkey);
6daba0
+    if (type != EVP_PKEY_RSA) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        rb_raise(eRSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
6daba0
     }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
+    return self;
6daba0
 
6daba0
+  legacy:
6daba0
+    BIO_free(in);
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
6daba0
+        EVP_PKEY_free(pkey);
6daba0
+        RSA_free(rsa);
6daba0
+        ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
     return self;
6daba0
 }
6daba0
 
6daba0
@@ -130,16 +141,23 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other)
6daba0
     EVP_PKEY *pkey;
6daba0
     RSA *rsa, *rsa_new;
6daba0
 
6daba0
-    GetPKey(self, pkey);
6daba0
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
6daba0
-	ossl_raise(eRSAError, "RSA already initialized");
6daba0
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
6daba0
+    if (pkey)
6daba0
+        rb_raise(rb_eTypeError, "pkey already initialized");
6daba0
     GetRSA(other, rsa);
6daba0
 
6daba0
-    rsa_new = ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa);
6daba0
+    rsa_new = (RSA *)ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey,
6daba0
+                              (d2i_of_void *)d2i_RSAPrivateKey,
6daba0
+                              (char *)rsa);
6daba0
     if (!rsa_new)
6daba0
 	ossl_raise(eRSAError, "ASN1_dup");
6daba0
 
6daba0
-    EVP_PKEY_assign_RSA(pkey, rsa_new);
6daba0
+    pkey = EVP_PKEY_new();
6daba0
+    if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) {
6daba0
+        RSA_free(rsa_new);
6daba0
+        ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
6daba0
+    }
6daba0
+    RTYPEDDATA_DATA(self) = pkey;
6daba0
 
6daba0
     return self;
6daba0
 }
6daba0