cd5466
From 8c185e0ae5e42bf5f3d76a1a0898946671116fa3 Mon Sep 17 00:00:00 2001
cd5466
From: Kazuki Yamaguchi <k@rhe.jp>
cd5466
Date: Wed, 3 Nov 2021 23:31:29 +0900
cd5466
Subject: [PATCH 1/2] pkey: test parsing concatenated PEM string
cd5466
cd5466
PEM-encoded private keys are sometimes stored together with irrelevant
cd5466
PEM blocks, such as the corresponding X.509 certificate.
cd5466
cd5466
PEM_read_bio_*() family automatically skips unknown PEM blocks, but on
cd5466
OpenSSL 3.0 we will be using the new OSSL_DECODER API instead due to
cd5466
some breaking changes around the password callback.
cd5466
cd5466
Let's add a test case so that we won't break the current behavior.
cd5466
---
cd5466
 test/openssl/test_pkey_rsa.rb | 6 ++++++
cd5466
 1 file changed, 6 insertions(+)
cd5466
cd5466
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
cd5466
index dbe87ba4..7510658d 100644
cd5466
--- a/test/openssl/test_pkey_rsa.rb
cd5466
+++ b/test/openssl/test_pkey_rsa.rb
cd5466
@@ -306,6 +306,12 @@ def test_RSAPrivateKey
cd5466
 
cd5466
     assert_equal asn1.to_der, rsa1024.to_der
cd5466
     assert_equal pem, rsa1024.export
cd5466
+
cd5466
+    # Unknown PEM prepended
cd5466
+    cert = issue_cert(OpenSSL::X509::Name.new([["CN", "nobody"]]), rsa1024, 1, [], nil, nil)
cd5466
+    str = cert.to_text + cert.to_pem + rsa1024.to_pem
cd5466
+    key = OpenSSL::PKey::RSA.new(str)
cd5466
+    assert_same_rsa rsa1024, key
cd5466
   end
cd5466
 
cd5466
   def test_RSAPrivateKey_encrypted
cd5466
cd5466
From a84ea531bbd080c3f58fe8d3dc9ffb1af2251f35 Mon Sep 17 00:00:00 2001
cd5466
From: Kazuki Yamaguchi <k@rhe.jp>
cd5466
Date: Sat, 20 Mar 2021 23:16:16 +0900
cd5466
Subject: [PATCH 2/2] pkey: use OSSL_DECODER to load encrypted PEM on OpenSSL
cd5466
 3.0
cd5466
cd5466
OpenSSL 3.0 has rewritten routines to load pkeys (PEM_read_bio_* and
cd5466
d2i_* functions) around the newly introduced OSSL_DECODER API.
cd5466
cd5466
This comes with a slight behavior change. They now decrypt and parse
cd5466
each encountered PEM block, then check the kind of the block. This used
cd5466
to be the reverse: they checked the PEM header to see the kind, and then
cd5466
decrypted the content. This means that the password callback may now be
cd5466
called repeatedly.
cd5466
cd5466
Let's use the OSSL_DECODER API directly on OpenSSL 3.0 so that the
cd5466
return value from the password callback will be reused automatically.
cd5466
---
cd5466
 ext/openssl/ossl_pkey.c | 40 ++++++++++++++++++++++++++++++++++++++++
cd5466
 1 file changed, 40 insertions(+)
cd5466
cd5466
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
cd5466
index f9f5162e..b08168a5 100644
cd5466
--- a/ext/openssl/ossl_pkey.c
cd5466
+++ b/ext/openssl/ossl_pkey.c
cd5466
@@ -78,6 +78,45 @@ ossl_pkey_new(EVP_PKEY *pkey)
cd5466
     return obj;
cd5466
 }
cd5466
 
cd5466
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
cd5466
+# include <openssl/decoder.h>
cd5466
+
cd5466
+EVP_PKEY *
cd5466
+ossl_pkey_read_generic(BIO *bio, VALUE pass)
cd5466
+{
cd5466
+    void *ppass = (void *)pass;
cd5466
+    OSSL_DECODER_CTX *dctx;
cd5466
+    EVP_PKEY *pkey = NULL;
cd5466
+    int pos = 0, pos2;
cd5466
+
cd5466
+    dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, NULL, 0, NULL, NULL);
cd5466
+    if (!dctx)
cd5466
+        goto out;
cd5466
+    if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
cd5466
+        goto out;
cd5466
+
cd5466
+    /* First check DER */
cd5466
+    if (OSSL_DECODER_from_bio(dctx, bio) == 1)
cd5466
+        goto out;
cd5466
+
cd5466
+    /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
cd5466
+    OSSL_BIO_reset(bio);
cd5466
+    if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
cd5466
+        goto out;
cd5466
+    while (OSSL_DECODER_from_bio(dctx, bio) != 1) {
cd5466
+        if (BIO_eof(bio))
cd5466
+            goto out;
cd5466
+        pos2 = BIO_tell(bio);
cd5466
+        if (pos2 < 0 || pos2 <= pos)
cd5466
+            goto out;
cd5466
+        pos = pos2;
cd5466
+    }
cd5466
+
cd5466
+  out:
cd5466
+    OSSL_DECODER_CTX_free(dctx);
cd5466
+    return pkey;
cd5466
+}
cd5466
+#else
cd5466
 EVP_PKEY *
cd5466
 ossl_pkey_read_generic(BIO *bio, VALUE pass)
cd5466
 {
cd5466
@@ -106,6 +145,7 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
cd5466
   out:
cd5466
     return pkey;
cd5466
 }
cd5466
+#endif
cd5466
 
cd5466
 /*
cd5466
  *  call-seq: