Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkey: restore support for decoding "openssl ecparam -genkey" output #540

Merged
merged 2 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,56 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
/* First check DER */
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
OSSL_BIO_reset(bio);

/* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
OSSL_BIO_reset(bio);
if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
goto out;
while (OSSL_DECODER_from_bio(dctx, bio) != 1) {
if (BIO_eof(bio))
/*
* First check for private key formats. 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-----
* BggqhkjOPQMBBw==
* -----END EC PARAMETERS-----
* -----BEGIN EC PRIVATE KEY-----
* MHcCAQEEIAG8ugBbA5MHkqnZ9ujQF93OyUfL9tk8sxqM5Wv5tKg5oAoGCCqGSM49
* AwEHoUQDQgAEVcjhJfkwqh5C7kGuhAf8XaAjVuG5ADwb5ayg/cJijCgs+GcXeedj
* 86avKpGH84DXUlB23C/kPt+6fXYlitUmXQ==
* -----END EC PRIVATE KEY-----
*
* While the first PEM block is a proper encoding of ECParameters, thus
* OSSL_DECODER_from_bio() would pick it up, ruby/openssl used to return
* the latter instead. Existing applications expect this behavior.
*
* Note that normally, the input is supposed to contain a single decodable
* PEM block only, so this special handling should not create a new problem.
*/
OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
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_set_selection(dctx, 0);
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;
}

Expand Down
23 changes: 23 additions & 0 deletions test/openssl/test_pkey_ec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,29 @@ def test_ECPrivateKey
assert_equal pem, p256.export
end

def test_ECPrivateKey_with_parameters
p256 = Fixtures.pkey("p256")

# The format used by "openssl ecparam -name prime256v1 -genkey -outform PEM"
#
# "EC PARAMETERS" block should be ignored if it is followed by an
# "EC PRIVATE KEY" block
in_pem = <<~EOF
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
-----END EC PRIVATE KEY-----
EOF

key = OpenSSL::PKey::EC.new(in_pem)
assert_same_ec p256, key
assert_equal p256.to_der, key.to_der
end

def test_ECPrivateKey_encrypted
p256 = Fixtures.pkey("p256")
# key = abcdef
Expand Down