diff options
-rw-r--r-- | ext/openssl/ossl_pkey.c | 111 | ||||
-rw-r--r-- | test/openssl/test_pkey.rb | 6 | ||||
-rw-r--r-- | test/openssl/utils.rb | 16 |
3 files changed, 59 insertions, 74 deletions
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 6d73d259..013412c2 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -82,30 +82,62 @@ ossl_pkey_new(EVP_PKEY *pkey) #if OSSL_OPENSSL_PREREQ(3, 0, 0) # include <openssl/decoder.h> -EVP_PKEY * -ossl_pkey_read_generic(BIO *bio, VALUE pass) +static EVP_PKEY * +ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass) { void *ppass = (void *)pass; OSSL_DECODER_CTX *dctx; EVP_PKEY *pkey = NULL; int pos = 0, pos2; - dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, NULL, 0, NULL, NULL); + dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL, + selection, NULL, NULL); if (!dctx) goto out; - if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1) - goto out; - - /* First check DER */ - if (OSSL_DECODER_from_bio(dctx, bio) == 1) + if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, + ppass) != 1) goto out; + while (1) { + if (OSSL_DECODER_from_bio(dctx, bio) == 1) + goto out; + if (BIO_eof(bio)) + break; + pos2 = BIO_tell(bio); + if (pos2 < 0 || pos2 <= pos) + break; + ossl_clear_error(); + pos = pos2; + } + out: OSSL_BIO_reset(bio); + OSSL_DECODER_CTX_free(dctx); + return pkey; +} +EVP_PKEY * +ossl_pkey_read_generic(BIO *bio, VALUE pass) +{ + EVP_PKEY *pkey = NULL; + /* First check DER, then check PEM. */ + const char *input_types[] = {"DER", "PEM"}; + int input_type_num = (int)(sizeof(input_types) / sizeof(char *)); /* - * Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed. + * Non-zero selections to try to decode. + * + * See EVP_PKEY_fromdata(3) - Selections to see all the selections. * - * First check for private key formats. This is to keep compatibility with - * ruby/openssl < 3.0 which decoded the following as a private key. + * This is a workaround for the decoder failing to decode or returning + * bogus keys with selection 0, if a key management provider is different + * from a decoder provider. The workaround is to avoid using selection 0. + * + * Affected OpenSSL versions: >= 3.1.0, <= 3.1.2, or >= 3.0.0, <= 3.0.10 + * Fixed OpenSSL versions: 3.2, next release of the 3.1.z and 3.0.z + * + * See https://github.com/openssl/openssl/pull/21519 for details. + * + * First check for private key formats (EVP_PKEY_KEYPAIR). This is to keep + * compatibility with ruby/openssl < 3.0 which decoded the following as a + * private key. * * $ openssl ecparam -name prime256v1 -genkey -outform PEM * -----BEGIN EC PARAMETERS----- @@ -126,50 +158,25 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) * * Note that we need to create the OSSL_DECODER_CTX variable each time when * we use the different selection as a workaround. - * https://github.com/openssl/openssl/issues/20657 + * See https://github.com/openssl/openssl/issues/20657 for details. */ - OSSL_DECODER_CTX_free(dctx); - dctx = NULL; - dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, NULL, - EVP_PKEY_KEYPAIR, NULL, NULL); - if (!dctx) - goto out; - if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1) - goto out; - while (1) { - if (OSSL_DECODER_from_bio(dctx, bio) == 1) - goto out; - if (BIO_eof(bio)) - break; - pos2 = BIO_tell(bio); - if (pos2 < 0 || pos2 <= pos) - break; - ossl_clear_error(); - pos = pos2; - } - - OSSL_BIO_reset(bio); - OSSL_DECODER_CTX_free(dctx); - dctx = NULL; - dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, NULL, 0, NULL, NULL); - if (!dctx) - goto out; - if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1) - goto out; - while (1) { - if (OSSL_DECODER_from_bio(dctx, bio) == 1) - goto out; - if (BIO_eof(bio)) - break; - pos2 = BIO_tell(bio); - if (pos2 < 0 || pos2 <= pos) - break; - ossl_clear_error(); - pos = pos2; + int selections[] = { + EVP_PKEY_KEYPAIR, + EVP_PKEY_KEY_PARAMETERS, + EVP_PKEY_PUBLIC_KEY + }; + int selection_num = (int)(sizeof(selections) / sizeof(int)); + int i, j; + + for (i = 0; i < input_type_num; i++) { + for (j = 0; j < selection_num; j++) { + pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass); + if (pkey) { + goto out; + } + } } - out: - OSSL_DECODER_CTX_free(dctx); return pkey; } #else diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index da3ae5d6..5fe37e2d 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -82,8 +82,6 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase end def test_ed25519 - pend_on_openssl_issue_21493 - # Test vector from RFC 8032 Section 7.1 TEST 2 priv_pem = <<~EOF -----BEGIN PRIVATE KEY----- @@ -148,8 +146,6 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase end def test_x25519 - pend_on_openssl_issue_21493 - # Test vector from RFC 7748 Section 6.1 alice_pem = <<~EOF -----BEGIN PRIVATE KEY----- @@ -202,8 +198,6 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase end def test_compare? - pend_on_openssl_issue_21493 - key1 = Fixtures.pkey("rsa1024") key2 = Fixtures.pkey("rsa1024") key3 = Fixtures.pkey("rsa2048") diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index b5ffbe1c..3d4d05fe 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -144,22 +144,6 @@ module OpenSSL::TestUtils return false unless version !major || (version.map(&:to_i) <=> [major, minor, fix]) >= 0 end - - # OpenSSL 3: x25519 a decode from and then encode to a pem file corrupts the - # key if fips+base provider is used - # This issue happens in OpenSSL between 3.0,0 and 3.0.10 or between 3.1.0 and - # 3.1.2. - # https://github.com/openssl/openssl/issues/21493 - # https://github.com/openssl/openssl/pull/21519 - def pend_on_openssl_issue_21493 - if OpenSSL.fips_mode && - ( - (openssl?(3, 0, 0, 0) && !openssl?(3, 0, 0, 11)) || - (openssl?(3, 1, 0, 0) && !openssl?(3, 1, 0, 3)) - ) - pend('See <https://github.com/openssl/openssl/issues/21493>') - end - end end class OpenSSL::TestCase < Test::Unit::TestCase |