openssl: OpenSSL 3.0.8: ed25519 a decode from and then encode to a pem file corrupts the key if fips+base provider is used
At an unit test in the OpenSSL Ruby binding (https://github.com/ruby/openssl), I see an FIPS mode specific issue related to ed25519 encryption. In my understanding, the program reads an ed25519 public key PEM file and convert it to something, then convert it to the PEM file again. The result of the final PEM file should be the same with the original PEM file. But the content is different with the OpenSSL 3.0.8 FIPS mode.
Fortunately I was able to prepare a reproducing program with only C language without Ruby. You can see it below.
The Ruby program
First, just let me share how the issue look like in the OpenSSL Ruby binding. I am testing it on the branch on my forked repository of the ruby/openssl, https://github.com/junaruga/openssl/tree/wip/fips-ed25519-report here.
In the following case with the ed25519 public key,
$ cat ed25519_pub.pem
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
-----END PUBLIC KEY-----
$ cat /etc/fedora-release
Fedora release 37 (Thirty Seven)
$ ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
Non-FIPS mode
We expect that the following command returns the original ed25519_pub.pem’s content. And it works with the OpenSSL 3.0.8 non-FIPS mode.
$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
ruby -I lib -e "require 'openssl'; puts OpenSSL::PKey.read(File.read('ed25519_pub.pem')).public_to_pem"
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
-----END PUBLIC KEY-----
FIPS mode
$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-fips-debug/lib/ \
OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
ruby -I lib -e "require 'openssl'; puts OpenSSL::PKey.read(File.read('ed25519_pub.pem')).public_to_pem"
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
-----END PUBLIC KEY-----
A reproducing program with C
Here is the program, https://github.com/junaruga/report-openssl-fips-ed25519.
$ cat ed25519_pub.pem
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
-----END PUBLIC KEY-----
$ gcc --version
gcc (GCC) 12.2.1 20221121 (Red Hat 12.2.1-4)
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -o ed25519 ed25519.c -lcrypto
Non-FIPS mode
The printed text on the last lines after [DEBUG] ossl_membio2str buf->data: are the same with the ed25519_pub.pem.
$ OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.0.8-debug/ssl \
OPENSSL_MODULES=/home/jaruga/.local/openssl-3.0.8-debug/lib/ossl-modules \
./ed25519 ed25519_pub.pem
[DEBUG] Loaded providers:
default
[DEBUG] Got a pkey! 0x24294e0
[DEBUG] It's held by the provider default
[DEBUG] ossl_membio2str buf->data:
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
-----END PUBLIC KEY-----
FIPS mode
The printed text is different from with the ed25519_pub.pem. Is this an expected behavior?
$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl \
OPENSSL_MODULES=/home/jaruga/.local/openssl-3.0.8-fips-debug/lib/ossl-modules \
./ed25519 ed25519_pub.pem
[DEBUG] Loaded providers:
fips
base
[DEBUG] Got a pkey! 0x21476a0
[DEBUG] It's held by the provider fips
[DEBUG] ossl_membio2str buf->data:
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
-----END PUBLIC KEY-----
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 34 (18 by maintainers)
Commits related to this issue
- bug: OpenSSL 3.0.8 FIPS: ed25519 a decode from and then encode to a pem file wrong? #20758 Public key happens only for Fips — committed to rkarmaka98/openssl by rkarmaka98 a year ago
- bug: OpenSSL 3.0.8 FIPS: ed25519 a decode from and then encode to a pem file wrong? #20758 Changed logic for fips — committed to rkarmaka98/openssl by rkarmaka98 a year ago
Yeah, but as was previously said, our code says something different, and returns a bogus key. The issue is very likely not so much about the abuse of the FIPS module as it’s a problem with what data gets passed over an export/import dance when the decoder and the keymgmt are in different providers.
I’m starting to have it my mind to put together an experiment where the decoders and encoders are removed from the default provider, i.e. only exist in the base provider, just to see what falls apart.
This issue is plain bug in OpenSSL and should be fixed by https://github.com/openssl/openssl/pull/21519
Yes, just as I mentioned earlier.
This is where it gets messy. 1.1 and 1.0 don’t know about FIPS, so no problem for them. In 3.1 they are correctly flagged in the FIPS provider, so again no problem so long as you do error checking. 3.0 is the problematic case. If you are using the 3.0 FIPS provider, it is your responsibility to enforce that no EdDSA keys are used. This can be done by loading the key regardless and using
EVP_PKEY_is_a, checking for either ed25519 or ed448 and failing if so.Oops, sorry, this isn’t a public function yet.
What happens if you remove the default_properties setting?
Actually the ED25519 algorithm is not FIPS approved so it is not supported with fips=yes property. That still does not mean the failure mode should be to output a bogus public key. Instead the functions should fail to import/export ED25519 keys completely.