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

OSSL_DECODER_CTX_set_selection doesn't apply the selection value properly #20657

Open
junaruga opened this issue Mar 30, 2023 · 46 comments
Open
Labels
branch: master Merge to master branch branch: 3.0 Merge to openssl-3.0 branch branch: 3.1 Merge to openssl-3.1 branch: 3.2 Merge to openssl-3.2 triaged: bug The issue/pr is/fixes a bug

Comments

@junaruga
Copy link

junaruga commented Mar 30, 2023

I read carefully to decide which is better in the openssl-users@openssl.org mailing list or opening this issue ticket on GitHub. As my question is about the OpenSSL API used in the OpenSSL Ruby bindings. I am trying to fix a bug in the OpenSSL Ruby bindings. I thought that perhaps my question is more close to the developing OpenSSL rather than using OpenSSL. But let me know if you think my case is to ask on the mailing list. I am happy to post it on it. Sorry for that.


I am debugging the OpenSSL Ruby bindings to fix a bug. Please let me know what's wrong in the code. Perhaps the APIs are wrongly called?

You can reproduce this bug by doing git clone on my forked repository branch: https://github.com/junaruga/openssl/tree/wip/fips-read-report that includes some debugging commits on the master branch. However, the reproducing steps are a bit complicated, please let me know if there are commands that you want me to run to find additional info.

Reproducing steps

Environment

My local environment is Fedora 37. However I was able to reproduce this issue on the Ubuntu (the ubuntu-latest) on the GitHub Actions too. And this issue also happens with the both cases of OpenSSL built from the source code without any patch files, and OpenSSL RPM package on RHEL 9.1.

In the reproducing steps below, the used OpenSSL version is OpenSSL 3.0.8 compiled from the source without any patch files. The LD_LIBRARY_PATH is used to load the OpenSSL.

$ cat /etc/fedora-release 
Fedora release 37 (Thirty Seven)

$ rpm -q gcc
gcc-12.2.1-4.fc37.x86_64

$ 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.

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-fips-debug/lib/ \
  ~/.local/openssl-3.0.8-fips-debug/bin/openssl version
OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)

1. Install OpenSSL with FIPS mode option.

I compiled the openssl with fips mode, and debug flags (-O0 -g3 -ggdb3 -gdwarf-5 flags.) as I wanted to debug. But this issue happens with the openssl compiled without the debug flags.

$ ./Configure --prefix=$HOME/.local/openssl-3.0.8-fips-debug --libdir=lib shared linux-x86_64 enable-fips -O0 -g3 -ggdb3 -gdwarf-5
$ make -j4
$ make install

And here is the OpenSSL config file used in the later process.

$ cat ~/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf
config_diagnostics = 1
openssl_conf = openssl_init

.include /home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/fipsmodule.cnf
#.include ./fipsmodule.cnf

[openssl_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
fips = fips_sect
base = base_sect

[base_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes

Then I used this program to check if the fips mode is available.

$ 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 \
  ~/git/openssl-test/fips_mode
FIPS mode provider available: 1
FIPS mode enabled: 1

2. Install Ruby and Compile OpenSSL Ruby bindings.

Below is the steps to install Ruby and to compile the Ruby OpenSSL bindings with the latest stable Ruby 3.2. I am compiling with the -O0 -g3 -ggdb3 -gdwarf-5 flags. You can skip the section. Note that at that time, Ruby 3.2.1 was the latest stable one. But now the Ruby 3.2.2 is the latest stable one.

Install Ruby

In the case of compiling with the source on the GitHub.

$ pwd
/home/jaruga/git/ruby

$ git clone https://github.com/ruby/ruby.git

$ cd ruby

$ pwd
/home/jaruga/git/ruby/ruby

$ git checkout v3_2_1

Run autoconf to generate the configure script.

$ ./autogen.sh

Or in the case of downloading the Ruby 3.2.1 source article from the Ruby official website.

Then I installed the Ruby with the commands below. The --enable-mkmf-verbose option makes the bundle exec rake compile command in the later process print the C compiler (gcc) command lines.

$ ./configure \
  --prefix=/usr/local/ruby-3.2.1 \
  --enable-shared \
  --enable-mkmf-verbose
$ make
$ make install

Or in the case of using Ruby RPM package on Fedora Linux, you see an error by the bundle exec rake compile in the later process. Here is a workaround.

$ sudo dnf install ruby ruby-devel

Then you can set the PATH for the installed Ruby.

.bashrc

...
PATH="/usr/local/ruby-3.2.1/bin:${PATH}"
PATH="${HOME}/.gem/ruby/3.2.0/bin:${PATH}"
...
export PATH

Compile OpenSSL Ruby bindings.

If you want to compile with the branch on my forked repository to reproduce this issue ticket:

$ git clone -b wip/fips-read-report https://github.com/junaruga/openssl.git

or if you want to compile with the original repository:

$ git clone https://github.com/ruby/openssl.git

Then I installed the dependency RubyGems packages.

$ cd openssl

$ pwd
/home/jaruga/git/ruby/openssl

$ which ruby
/usr/local/ruby-3.2.1/bin/ruby

$ which bundle
/usr/local/ruby-3.2.1/bin/bundle

$ ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]

$ bundle exec install --standalone

I compiled the OpenSSL Ruby bindings.

$ bundle exec rake compile

If you want to clean to compile again by bundle exec rake compile, you can run the command below.

$ rm -rf tmp/ lib/openssl.so

3. Run the command raising the error.

I created a testing pem file.

$ openssl genrsa -out key.pem 4096

Then I ran the OpenSSL Ruby binding to read the pem file from the OpenSSL Ruby binding. In the result of the command, you see the error message "Could not parse PKey (OpenSSL::PKey::PKeyError)" that comes from the OpenSSL Ruby binding, and it comes from the following the OSSL_DECODER_from_bio(dctx, bio) returning 0. See below.

The other parts in the output, the [DEBUG] ... is by my printf debugging log. And the ... Input type: ... is by the ERR_print_errors_fp(stdout). The ossl_pkey_read_generic function is called 2 times, and the OSSL_DECODER_from_bio function is called 3 times in each ossl_pkey_read_generic function called.

$ 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'; OpenSSL::PKey.read(File.read('key.pem'))"
[DEBUG] Calling ossl_pkey_read_generic from ossl_dh_initialize.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
003C0D92E17F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
003C0D92E17F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
[DEBUG] Calling OSSL_DECODER_from_bio 3.
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
003C0D92E17F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 3.
-e:1:in `read': Could not parse PKey (OpenSSL::PKey::PKeyError)
	from -e:1:in `<main>'

$ echo $?
1

The error comes from the OSSL_DECODER_from_bio returning the 0.

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L149

ext/openssl/ossl_pkey.c#L149

 145     OSSL_BIO_reset(bio);
 146     OSSL_DECODER_CTX_set_selection(dctx, 0);
 147     while (1) {
 148         printf("[DEBUG] Calling OSSL_DECODER_from_bio 3.\n");
 149         if (OSSL_DECODER_from_bio(dctx, bio) == 1) /* <= This OSSL_DECODER_from_bio returns 0! */
 150             goto out;
 151         ERR_print_errors_fp(stdout);
 152         if (BIO_eof(bio))
 153             break;
 154         pos2 = BIO_tell(bio);
 155         if (pos2 < 0 || pos2 <= pos)
 156             break;
 157         ossl_clear_error();
 158         pos = pos2;
 159     }

Debugging

ltrace

First, I captured the ltrace log by the command below. Because I think the ltrace log is good to see how the OpenSSL APIs are called in the process. You can see the OSSL_DECODER_from_bio is called totally 6 times in the ltrace. So, the 6th called OSSL_DECODER_from_bio fails and causes the error. Note that unfortunately, the log is by the ltrace in the first part, and by the ruby in the second part unfortuntely.

$ 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 \
  ltrace -ttt -f -l openssl.so -l libssl.so.3 -l libcrypto.so.3 \
  ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))" >& ltrace_ttt.log

GDB

Debug around the OSSL_DECODER_from_bio

I debugged the gdb by the command below. The reason why I am setting the LD_LIBRARY_PATH in the gdb prompt is because the system openssl is the dependency of the gdb command. The gdb fails hiding the system openssl by referring to the manually installed openssl by LD_LIBRARY_PATH.

$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
  gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-fips-debug/lib/

After some steps, below is soon after calling the 6th OSSL_DECODER_from_bio returning the 0 as a error. And the values of the input arguments *dctx and *bio are below.

(gdb) b ossl_pkey_read_generic
(gdb) r
(gdb) c
(gdb) n
(gdb) f
#0  ossl_pkey_read_generic (bio=0x7be610, pass=4) at ../../../../ext/openssl/ossl_pkey.c:151
151	        ERR_print_errors_fp(stdout);
(gdb) p *dctx
$4 = {start_input_type = 0x7fffe57d5489 "PEM", input_structure = 0x0, selection = 0, decoder_insts = 0x7bf030, construct = 0x7fffe51e5640 <decoder_construct_pkey>, 
  cleanup = 0x7fffe51e5984 <decoder_clean_pkey_construct_arg>, construct_data = 0x69ed30, pwdata = {type = is_pem_password, _ = {expl_passphrase = {
        passphrase_copy = 0x7fffe5792cdd <ossl_pem_passwd_cb> "UH\211\345SH\203\354HH\211}ȉuĉU\300H\211M\270H\213E\270H\211E\350H\213E\350H\211\307\350q\361\377\377\204\300\017\204", <incomplete sequence \356>, passphrase_len = 4}, pem_password = {password_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, password_cbarg = 0x4}, ossl_passphrase = {passphrase_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, passphrase_cbarg = 0x4}, 
      ui_method = {ui_method = 0x7fffe5792cdd <ossl_pem_passwd_cb>, ui_method_data = 0x4}}, flag_cache_passphrase = 1, cached_passphrase = 0x0, cached_passphrase_len = 0}}
(gdb) p *bio
$5 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, callback_ex = 0x0, cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, num = 0, ptr = 0x7c1c30, next_bio = 0x0, 
  prev_bio = 0x0, references = 1, num_read = 1504, num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}

And here is the backtrace.

(gdb) bt
#0  ossl_pkey_read_generic (bio=0x7be610, pass=4) at ../../../../ext/openssl/ossl_pkey.c:151
#1  0x00007fffe57ad75c in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, 
    self=140737035361920) at ../../../../ext/openssl/ossl_pkey.c:222
#2  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, 
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#3  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, 
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, 
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#4  vm_exec_core (ec=0x0, initial=initial@entry=0) at /home/jaruga/src/ruby-3.2.1/insns.def:820
#5  0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#6  0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#7  0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, n=n@entry=0x7ffff7e7bab8)
    at eval.c:289
#8  0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7bab8) at eval.c:330
#9  0x0000000000401102 in rb_main (argv=0x7fffffffda48, argc=5) at ./main.c:38
#10 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

Debug deeply in the OSSL_DECODER_from_bio

As a reference, I stepped in the OSSL_DECODER_from_bio. Running the GDB from the start again, then here is a part that causes the error in the OSSL_DECODER_from_bio. As the ok is 0, and the decoder_process returns the 0.

(gdb) f
#0  decoder_process (params=0x7fffffffd100, arg=0x7fffffffd2b0)
    at crypto/encode_decode/decoder_lib.c:747
747	            ok = (rv > 0);
(gdb) p rv
$6 = 0

Here are input arguments of the function decoder_process and local variables at the same point crypto/encode_decode/decoder_lib.c:747.

(gdb) p *params
$8 = {key = 0x7fffe53ffb0b "data-structure", data_type = 4, data = 0x7fffe53ffb5e, 
  data_size = 14, return_size = 18446744073709551615}
(gdb) p *data
$9 = {ctx = 0x7be7c0, bio = 0x0, current_decoder_inst_index = 36, recursion = 1, 
  flag_next_level_called = 1, flag_construct_called = 1, flag_input_structure_checked = 0}
(gdb) i lo
rv = 0
p = 0x7fffe53ffb1f
trace_data_structure = 0x7fffffffd178 ""
data = 0x7fffffffd2b0
ctx = 0x7be7c0
decoder_inst = 0x7c0460
decoder = 0x7b9b20
cbio = 0x0
bio = 0x0
loc = 140737039563551
i = 1
ok = 0
new_data = {ctx = 0x7be7c0, bio = 0x0, current_decoder_inst_index = 0, recursion = 2, 
  flag_next_level_called = 0, flag_construct_called = 0, flag_input_structure_checked = 0}
data_type = 0x0
data_structure = 0x0
__func__ = "decoder_process"

Here is the backtrace.

(gdb) bt
#0  decoder_process (params=0x7fffffffd100, arg=0x7fffffffd2b0)
    at crypto/encode_decode/decoder_lib.c:747
#1  0x00007fffe5363268 in pem2der_decode (vctx=0x7c0440, cin=0x7c0b30, selection=0, 
    data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffd2b0, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x7be7f8)
    at providers/implementations/encode_decode/decode_pem2der.c:204
#2  0x00007fffe51e3d6e in decoder_process (params=0x0, arg=0x7fffffffd3e0)
    at crypto/encode_decode/decoder_lib.c:962
#3  0x00007fffe51e248a in OSSL_DECODER_from_bio (ctx=0x7be7c0, in=0x7bdea0)
    at crypto/encode_decode/decoder_lib.c:81
#4  0x00007fffe57ad64a in ossl_pkey_read_generic (bio=0x7bdea0, pass=4)
    at ../../../../ext/openssl/ossl_pkey.c:149
#5  0x00007fffe57ad75c in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, 
    self=140737035361920) at ../../../../ext/openssl/ossl_pkey.c:222
#6  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, 
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#7  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, 
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, 
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#8  vm_exec_core (ec=0x7fffe53d6ef4, initial=140737039563551, initial@entry=0)
    at /home/jaruga/src/ruby-3.2.1/insns.def:820
#9  0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#10 0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#11 0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, n=n@entry=0x7ffff7e7bab8)
    at eval.c:289
#12 0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7bab8) at eval.c:330
#13 0x0000000000401102 in rb_main (argv=0x7fffffffda48, argc=5) at ./main.c:38
#14 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

Please let me know if you want to see additional information. I am happy to help for that! Thank you for reading this, and thank you for your help.

@junaruga junaruga added the issue: question The issue was opened to ask a question label Mar 30, 2023
@t8m t8m added triaged: question The issue contains a question and removed issue: question The issue was opened to ask a question labels Mar 31, 2023
@t8m
Copy link
Member

t8m commented Mar 31, 2023

The problem is the Red Hat FIPS support in their openssl package is only related to the upstream OpenSSL FIPS module and there are some serious differences - for example the crypto-policies support.

So trying to debug it with the OpenSSL FIPS module can be problematic or even impossible if the bug is due to the additional Red Hat patches.

@beldmit
Copy link
Member

beldmit commented Mar 31, 2023

@junaruga It may be a Red Hat specific problem. in RHEL FIPS provider is auto activated when the system is in FIPS mode, and if the OpenSSL configuration file is present and loaded. So I'd look in OpenSSL initialization.

@junaruga
Copy link
Author

junaruga commented Mar 31, 2023

Guys, thank you for your responses!

I was able to reproduced this issue with not only OpenSSL 3.0 RPM package on RHEL 9 FIPS mode but also with the OpenSSL 3.0 compiled from the OpenSSL source directly without any patch files on Fedora 37. The reproducing steps above is with the OpenSSL 3.0 compiled from the source directly.

Why do you think this may be a Red Hat specific problem?

@beldmit
Copy link
Member

beldmit commented Mar 31, 2023

If this is an universal problem then I'd anyway check via strace if openssl config and providers are loaded.

@junaruga
Copy link
Author

junaruga commented Mar 31, 2023

Thanks!

I suppose this is an universal problem. Because I can reproduce this issue with OpenSSL 3.0.8 with FIPS mode compiled from the source on the GitHub Actions Ubuntu on my forked branch: wip/fips-read-test-report from the https://github.com/ruby/openssl. The CI log is here.

My concern is the openssl genrsa -out key.pem 4096 failed to create the key.pem on the Ubuntu case on the GitHub Actions. So, I added the key.pem file I created on my local Fedora to test. The CI log is here.

The log in Ubuntu case on the GitHub Actions:

$HOME/.openssl/openssl-3.0.8/bin/openssl genrsa -out key.pem 4096
 Error initializing RSA context
4047441F347F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:../crypto/evp/evp_fetch.c:349:Global default library context, Algorithm (rsaEncryption : 104), Properties (<null>)
Error: Process completed with exit code 1.

I just prepared the strace log and ltrace -ttt -S log files including system calls on the repository below for your convenience.
https://github.com/junaruga/report-openssl-fips-read-error

@junaruga
Copy link
Author

Sorry, maybe my assumption was wrong about where the problematic code is. I said the problem is the 6th call of OSSL_DECODER_from_bio(dctx, bio) returning 0 after printf("[DEBUG] Calling OSSL_DECODER_from_bio 3.\n");. But I think the correct problematic code is 5th call of the OSSL_DECODER_from_bio(dctx, bio) after printf("[DEBUG] Calling OSSL_DECODER_from_bio 2.\n");.

Because I compared the result between with OpenSSL 3.0.8 FIPS enabled and OpenSSL FIPS disabled now.

non-FIPS mode (FIPS mode disabled)

Below steps are for the OpenSSL 3.0.8 non-FIPS mode compiled from the source without any patch files (not RPM pacakge).

I compiled the OpenSSL 3.0.8 non-FIPS mode with the debug flags from the source by the commands below.

./Configure --prefix=$HOME/.local/openssl-3.0.8-debug --libdir=lib shared linux-x86_64 -O0 -g3 -ggdb3 -gdwarf-5
make -j4
make install

Then I confirmed that the version and the FIPS provide is not available and FIPS mode is not enabled as expected.

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
  ~/.local/openssl-3.0.8-debug/bin/openssl version
OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)
$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
  ~/git/openssl-test/fips_mode
FIPS mode provider available: 0
FIPS mode enabled: 0

Then below is the result of running the OpenSSL Ruby binding. You see the OSSL_DECODER_from_bio is called totally 5 times in the non-FIPS mode case, while the OSSL_DECODER_from_bio is called totally 6 times in the case of FIPS mode case above in the first comment. That means the 5th call of the OSSL_DECODER_from_bio (after [DEBUG] Calling OSSL_DECODER_from_bio 2.) returns 1 in the non-FIPS mode, while it returns 0 in the FIPS mode.

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
  ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
[DEBUG] Calling ossl_pkey_read_generic from ossl_dh_initialize.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
00CCC6DB7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
00CCC6DB7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
[DEBUG] Calling OSSL_DECODER_from_bio 3.
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data. 
[DEBUG] Calling OSSL_DECODER_from_bio 1.
00CCC6DB7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.

$ echo $?
0

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L133

ext/openssl/ossl_pkey.c#L133

 130     OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
 131     while (1) {
 132         printf("[DEBUG] Calling OSSL_DECODER_from_bio 2.\n");
 133         if (OSSL_DECODER_from_bio(dctx, bio) == 1) /* <= This OSSL_DECODER_from_bio returns 1 in the non-FIPS mode case, but it returns 0 in the FIPS mode case! */
 134             goto out;
 135         ERR_print_errors_fp(stdout);
 136         if (BIO_eof(bio))
 137             break;
 138         pos2 = BIO_tell(bio);
 139         if (pos2 < 0 || pos2 <= pos)
 140             break;
 141         ossl_clear_error();
 142         pos = pos2;
 143     }

And I debugged with GDB again for the non-FIPS mode.

$ gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-debug/lib/
(gdb) b ossl_pkey_read_generic
(gdb) r
(gdb) c
(gdb) n

Here is the line soon after the 5th called OSSL_DECODER_from_bio(dctx, bio). And I printed the value of the input arguments dctx and bio.

(gdb) f
#0  ossl_pkey_read_generic (bio=0x79a180, pass=4) at ../../../../ext/openssl/ossl_pkey.c:134
134	            goto out;
(gdb) p *dctx
$1 = {start_input_type = 0x7fffe57d5489 "PEM", input_structure = 0x0, selection = 135, 
  decoder_insts = 0x79ac80, construct = 0x7fffe51e5640 <decoder_construct_pkey>, 
  cleanup = 0x7fffe51e5984 <decoder_clean_pkey_construct_arg>, construct_data = 0x6a18f0, 
  pwdata = {type = is_pem_password, _ = {expl_passphrase = {
        passphrase_copy = 0x7fffe5792cdd <ossl_pem_passwd_cb> "UH\211\345SH\203\354HH\211}ȉuĉU\300H\211M\270H\213E\270H\211E\350H\213E\350H\211\307\350q\361\377\377\204\300\017\204", <incomplete sequence \356>, passphrase_len = 4}, pem_password = {
        password_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, password_cbarg = 0x4}, 
      ossl_passphrase = {passphrase_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, 
        passphrase_cbarg = 0x4}, ui_method = {
        ui_method = 0x7fffe5792cdd <ossl_pem_passwd_cb>, ui_method_data = 0x4}}, 
    flag_cache_passphrase = 1, cached_passphrase = 0x0, cached_passphrase_len = 0}}
(gdb) p *bio
$2 = {libctx = 0x0, method = 0x7fffe54c80a0 <mem_method>, callback = 0x0, callback_ex = 0x0, 
  cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, num = 0, 
  ptr = 0x79d880, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 1598, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c3300}

Here is the backtrace.

(gdb) bt
#0  ossl_pkey_read_generic (bio=0x79a180, pass=4) at ../../../../ext/openssl/ossl_pkey.c:134
#1  0x00007fffe57ad75c in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, 
    self=140737042308920) at ../../../../ext/openssl/ossl_pkey.c:222
#2  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, 
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#3  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, 
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, 
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#4  vm_exec_core (ec=0x0, initial=initial@entry=0)
    at /home/jaruga/src/ruby-3.2.1/insns.def:820
#5  0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#6  0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#7  0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, n=n@entry=0x7ffff7e7ba90)
    at eval.c:289
#8  0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7ba90) at eval.c:330
#9  0x0000000000401102 in rb_main (argv=0x7fffffffdaa8, argc=5) at ./main.c:38
#10 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

I updated the repository: https://github.com/junaruga/report-openssl-fips-read-error adding the non-FIPS mode ltrace and strace log files.

@junaruga
Copy link
Author

junaruga commented Mar 31, 2023

I compared the input arguments dctx and bio before calling the 5th call of the OSSL_DECODER_from_bio between FIPS mode and non-FIPS mode that is ext/openssl/ossl_pkey.c:133.

I found one difference between the 2 cases. The value of the bio->num_read was different.

FIPS mode

$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
  gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-fips-debug/lib/
(gdb) f
#0  ossl_pkey_read_generic (bio=0x7be610, pass=4) at ../../../../ext/openssl/ossl_pkey.c:133
133	        if (OSSL_DECODER_from_bio(dctx, bio) == 1)
(gdb) p *dctx
$5 = {start_input_type = 0x7fffe57d5489 "PEM", input_structure = 0x0, selection = 135, 
  decoder_insts = 0x7bf030, construct = 0x7fffe51e5640 <decoder_construct_pkey>, 
  cleanup = 0x7fffe51e5984 <decoder_clean_pkey_construct_arg>, construct_data = 0x69ed30, 
  pwdata = {type = is_pem_password, _ = {expl_passphrase = {
        passphrase_copy = 0x7fffe5792cdd <ossl_pem_passwd_cb> "UH\211\345SH\203\354HH\211}ȉuĉU\300H\211M\270H\213E\270H\211E\350H\213E\350H\211\307\350q\361\377\377\204\300\017\204", <incomplete sequence \356>, passphrase_len = 4}, pem_password = {
        password_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, password_cbarg = 0x4}, 
      ossl_passphrase = {passphrase_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, 
        passphrase_cbarg = 0x4}, ui_method = {
        ui_method = 0x7fffe5792cdd <ossl_pem_passwd_cb>, ui_method_data = 0x4}}, 
    flag_cache_passphrase = 1, cached_passphrase = 0x0, cached_passphrase_len = 0}}
(gdb) p *bio
$6 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, callback_ex = 0x0, 
  cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, num = 0, 
  ptr = 0x7c1c30, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 1504, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}
(gdb) p bio->num_read
$7 = 1504

Non-FIPS mode

$ gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-debug/lib/
(gdb) f
#0  ossl_pkey_read_generic (bio=0x79a220, pass=4) at ../../../../ext/openssl/ossl_pkey.c:133
133	        if (OSSL_DECODER_from_bio(dctx, bio) == 1)
(gdb) p *dctx
$1 = {start_input_type = 0x7fffe57d5489 "PEM", input_structure = 0x0, selection = 135, 
  decoder_insts = 0x79ad20, construct = 0x7fffe51e5640 <decoder_construct_pkey>, 
  cleanup = 0x7fffe51e5984 <decoder_clean_pkey_construct_arg>, construct_data = 0x67c7f0, 
  pwdata = {type = is_pem_password, _ = {expl_passphrase = {
        passphrase_copy = 0x7fffe5792cdd <ossl_pem_passwd_cb> "UH\211\345SH\203\354HH\211}ȉuĉU\300H\211M\270H\213E\270H\211E\350H\213E\350H\211\307\350q\361\377\377\204\300\017\204", <incomplete sequence \356>, passphrase_len = 4}, pem_password = {
        password_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, password_cbarg = 0x4}, 
      ossl_passphrase = {passphrase_cb = 0x7fffe5792cdd <ossl_pem_passwd_cb>, 
        passphrase_cbarg = 0x4}, ui_method = {
        ui_method = 0x7fffe5792cdd <ossl_pem_passwd_cb>, ui_method_data = 0x4}}, 
    flag_cache_passphrase = 1, cached_passphrase = 0x0, cached_passphrase_len = 0}}
(gdb) p *bio
$2 = {libctx = 0x0, method = 0x7fffe54c80a0 <mem_method>, callback = 0x0, callback_ex = 0x0, 
  cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, num = 0, 
  ptr = 0x79d920, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 1598, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c8280}
(gdb) p bio->num_read
$3 = 1598

@junaruga
Copy link
Author

junaruga commented Apr 1, 2023

I just added the new section "Reproducing steps - 2. Install Ruby and Compile OpenSSL Ruby bindings. - Install Ruby" on the first comment above, as I thought you may want to reproduce this issue on your environment.

@levitte
Copy link
Member

levitte commented Apr 3, 2023

I'm looking at all this, but can't really see directly what's happening. The BIO that's passed to OSSL_DECODER_from_bio() is a memory BIO, if I understand correctly, which is formed by this line:

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L220

Can you confirm that the contents that this BIO handles are what they are supposed to be? Also, does ossl_obj2bio() simply set up a straight BIO_s_mem() or does it do some sort of adaptation of its own?

The reason I'm asking all this is that I've started to suspect that the BIO_reset() calls that are done in ossl_pkey_read_generic() might not work as expected... I've run into trouble with that before, but my memory on this is admitedly a bit vague (it's been a few years)

@junaruga
Copy link
Author

junaruga commented Apr 3, 2023

Sure! Let me confirm it. And I will let you know it here.

@junaruga
Copy link
Author

junaruga commented Apr 4, 2023

I'm looking at all this, but can't really see directly what's happening. The BIO that's passed to OSSL_DECODER_from_bio() is a memory BIO, if I understand correctly, which is formed by this line:

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L220

I think so. The bio variable is created at the ossl_obj2bio. And it seems that ossl_obj2bio only does the BIO_s_mem in it.

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_bio.c#L21

So, below is the state of the bio soon after calling OSSL_DECODER_from_bio. (num_read = 0) on running the program on FIPS mode.

$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
  gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-fips-debug/lib/
(gdb) f
#0  ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, self=140737035361920)
    at ../../../../ext/openssl/ossl_pkey.c:221
221	    printf("[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.\n");
(gdb) p *bio
$24 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, 
  callback_ex = 0x0, cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, 
  num = 0, ptr = 0x7c1c00, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 0, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}
(gdb) p bio->num_read
$25 = 0

Then After calling the first OSSL_DECODER_from_bio, the bio variable changes. The num_read changes from 0 to 1504.

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L101

(gdb) n
[DEBUG] Calling OSSL_DECODER_from_bio 1.
101	    if (OSSL_DECODER_from_bio(dctx, bio) == 1)
(gdb) p *bio
$26 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, 
  callback_ex = 0x0, cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, 
  num = 0, ptr = 0x7c1c00, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 0, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}
(gdb) n
103	    ERR_print_errors_fp(stdout);
(gdb) p *bio
$27 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, 
  callback_ex = 0x0, cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, 
  num = 0, ptr = 0x7c1c00, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 1504, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}

Then soon after calling the OSSL_BIO_reset, the num_read is still 1504. I expected the OSSL_BIO_reset changes the num_read to 0 again. Is it right?

(gdb) n
00DCE8F7FF7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
104	    OSSL_BIO_reset(bio);
(gdb) n
107	    if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
(gdb) p *bio
$28 = {libctx = 0x0, method = 0x7fffe54c90a0 <mem_method>, callback = 0x0, 
  callback_ex = 0x0, cb_arg = 0x0, init = 1, shutdown = 1, flags = 512, retry_reason = 0, 
  num = 0, ptr = 0x7c1c00, next_bio = 0x0, prev_bio = 0x0, references = 1, num_read = 1504, 
  num_write = 0, ex_data = {ctx = 0x0, sk = 0x0}, lock = 0x6c7730}

When calling the OSSL_BIO_reset at https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L104 , it calls the function BIO_ctrl, not BIO_reset. Is it right?

(gdb) bt
#0  BIO_ctrl (b=0x7bec20, cmd=1, larg=0, parg=0x0) at crypto/bio/bio_lib.c:567
#1  0x00007fffe57ad529 in ossl_pkey_read_generic (bio=0x7bec20, pass=4)
    at ../../../../ext/openssl/ossl_pkey.c:104
#2  0x00007fffe57ad75c in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048,-
    self=140737035361920) at ../../../../ext/openssl/ossl_pkey.c:222
#3  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90,-
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#4  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>,-
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>,-
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#5  vm_exec_core (ec=0x7bec20, initial=1, initial@entry=0)
    at /home/jaruga/src/ruby-3.2.1/insns.def:820
#6  0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#7  0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#8  0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, n=n@entry=0x7ffff7e7bab8)
    at eval.c:289
#9  0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7bab8) at eval.c:330
#10 0x0000000000401102 in rb_main (argv=0x7fffffffda48, argc=5) at ./main.c:38
#11 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

Reference: BIO_reset and BIO_ctrl and other related functions:
https://www.openssl.org/docs/man3.1/man3/BIO_reset.html

@junaruga
Copy link
Author

junaruga commented Apr 6, 2023

Guys, do you have any other info do you want to see? I am happy to provide it. Thanks.

@junaruga
Copy link
Author

junaruga commented Apr 6, 2023

When checking the OSSL_BIO_reset(bio) after the 4th call of the OSSL_DECODER_from_bio(dctx, bio),
https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L104

The OSSL_BIO_reset(bio) is calling BIO_ctrl(b,BIO_CTRL_RESET,0,NULL)
https://github.com/openssl/openssl/blob/openssl-3.0.8/include/openssl/bio.h.in#L532

# define BIO_reset(b)            (int)BIO_ctrl(b,BIO_CTRL_RESET,0,NULL)

Then at the following line in the function mem_ctrl called in the function BIO_ctrl.

https://github.com/openssl/openssl/blob/openssl-3.0.8/crypto/bio/bss_mem.c#L272

271                 /* For read only case just reset to the start again */
272                 *bbm->buf = *bbm->readp;
(gdb) bt
#0  mem_ctrl (b=0x7be370, cmd=1, num=0, ptr=0x0) at crypto/bio/bss_mem.c:272
#1  0x00007fffe50f4502 in BIO_ctrl (b=0x7be370, cmd=1, larg=0, parg=0x0)
    at crypto/bio/bio_lib.c:580
#2  0x00007fffe57ad529 in ossl_pkey_read_generic (bio=0x7be370, pass=4)
    at ../../../../ext/openssl/ossl_pkey.c:104
#3  0x00007fffe57ad75c in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, 
    self=140737035361760) at ../../../../ext/openssl/ossl_pkey.c:222
#4  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, 
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#5  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, 
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, 
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#6  vm_exec_core (ec=0x7be370, initial=1, initial@entry=0)
    at /home/jaruga/src/ruby-3.2.1/insns.def:820
#7  0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#8  0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#9  0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, 
    n=n@entry=0x7ffff7e7b9c8) at eval.c:289
#10 0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7b9c8) at eval.c:330
#11 0x0000000000401102 in rb_main (argv=0x7fffffffda48, argc=5) at ./main.c:38
#12 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

It is processed as read only.

(gdb) p b->flags & BIO_FLAGS_MEM_RDONLY
$58 = 512

@t8m
Copy link
Member

t8m commented Apr 6, 2023

So these calls work fine if run with default provider but they fail with fips+base providers? If you call OSSL_PROVIDER_available("fips") and OSSL_PROVIDER_available("base") do these calls return 1?

@junaruga
Copy link
Author

junaruga commented Apr 6, 2023

So these calls work fine if run with default provider but they fail with fips+base providers?

Yes, right.

As I commented at #20657 (comment), the call of the OSSL_DECODER_from_bio at the ext/openssl/ossl_pkey.c#L133 below fails with fips+base providers. But it works fine with the default provider.

https://github.com/junaruga/openssl/blob/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c#L133

 133         if (OSSL_DECODER_from_bio(dctx, bio) == 1) /* <= This OSSL_DECODER_from_bio returns 1 in the non-FIPS mode case, but it returns 0 in the FIPS mode case! */
 134             goto out;

If you call OSSL_PROVIDER_available("fips") and OSSL_PROVIDER_available("base") do these calls return 1?

Yes, right. Both the OSSL_PROVIDER_available("fips") and OSSL_PROVIDER_available("base") return the 1 on the OpenSSL 3.0.8 with FIPS mode enabled installed from the source (/home/jaruga/.local/openssl-3.0.8-fips-debug).

I tested my testing OpenSSLs /home/jaruga/.local/openssl-3.0.8-fips-debug and /home/jaruga/.local/openssl-3.0.8-debug with the following small program, https://github.com/junaruga/openssl-test/blob/8a3e508f679a0b92186dc9ef8c7f17f0a925423d/fips_mode.c .

$ 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 \
  ~/git/openssl-test/fips_mode
Base provider available: 1
FIPS provider available: 1
FIPS mode enabled: 1

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
  ~/git/openssl-test/fips_mode
Base provider available: 0
FIPS provider available: 0
FIPS mode enabled: 0

@junaruga
Copy link
Author

junaruga commented Apr 6, 2023

I updated my testing branch on the forked repository: https://github.com/junaruga/openssl/commits/wip/fips-read-report by adding more debug logs to print the base/fips providers and the fips-enabled, and the result of the OSSL_DECODER_from_bio 2 with one additional commit. I hope it's better for us to see the difference between the FIPS and non-FIPS mode easily The result is below.

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'; OpenSSL::PKey.read(File.read('key.pem'))"
[DEBUG] Calling ossl_pkey_read_generic from ossl_dh_initialize.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
003C66CC977F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 2 failed.
003C66CC977F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
[DEBUG] Calling OSSL_DECODER_from_bio 3.
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Base provider available: 1
[DEBUG] FIPS provider available: 1
[DEBUG] FIPS mode enabled: 1 
[DEBUG] Calling OSSL_DECODER_from_bio 1.
003C66CC977F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 2 failed.
[DEBUG] Calling OSSL_DECODER_from_bio 3.
-e:1:in `read': Could not parse PKey (OpenSSL::PKey::PKeyError)
  from -e:1:in `<main>'

$ echo $?
1

Non-FIPS mode

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-debug/lib/ \
  ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
[DEBUG] Calling ossl_pkey_read_generic from ossl_dh_initialize.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
004CCA7C327F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 2 failed.
004CCA7C327F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
[DEBUG] Calling OSSL_DECODER_from_bio 3.
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Base provider available: 0
[DEBUG] FIPS provider available: 0
[DEBUG] FIPS mode enabled: 0
[DEBUG] Calling OSSL_DECODER_from_bio 1.
004CCA7C327F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 2 ok.

$ echo $?
0

@t8m
Copy link
Member

t8m commented Apr 6, 2023

I do not think the problem is related to the bio reset call. It is fairly strange because the decoders in default and base providers are identical implementations and if fips and base providers are properly loaded there should be really no difference if you're importing 4096 bit RSA key in unencrypted PEM format.

Could you please put breakpoint to rsa_d2i_PKCS8 in the debugger to see if it is called in the non-fips and fips cases and if there are any differences in the return value?

@junaruga
Copy link
Author

Could you please put breakpoint to rsa_d2i_PKCS8 in the debugger to see if it is called in the non-fips and fips cases and if there are any differences in the return value?

Sure. I checked it. The rsa_d2i_PKCS8 is called from the 5h call of the OSSL_DECODER_from_bio soon after printing the "[DEBUG] Calling OSSL_DECODER_from_bio 2." in both the FIPS and non-FIPS cases. And the rsa_d2i_PKCS8 (key = ctx->desc->d2i_PKCS8(NULL, &derp, der_len, ctx)) returns NULL in both FIPS and non-FIPS mode cases at providers/implementations/encode_decode/decode_der2key.c:214.

Below is the working log on GDB.

FIPS mode

$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug/ssl/openssl_fips.cnf \
  gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-fips-debug/lib/
(gdb) b rsa_d2i_PKCS8
Breakpoint 1 at 0x7fffe53615b8: file providers/implementations/encode_decode/decode_der2key.c, line 506.

Below is the line where the rsa_d2i_PKCS8 is called.

(gdb) bt
#0  rsa_d2i_PKCS8 (key=0x0, der=0x7fffffffcf10, der_len=2375, ctx=0x7c2610) at providers/implementations/encode_decode/decode_der2key.c:506
#1  0x00007fffe5360ec7 in der2key_decode (vctx=0x7c2610, cin=0x7c0a30, selection=135, data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffcf80, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x7bec98) at providers/implementations/encode_decode/decode_der2key.c:213
#2  0x00007fffe51e3d6e in decoder_process (params=0x7fffffffd100, arg=0x7fffffffd2b0) at crypto/encode_decode/decoder_lib.c:962 
#3  0x00007fffe5363268 in pem2der_decode (vctx=0x7c08e0, cin=0x7c09b0, selection=135, data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffd2b0, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x7bec98) at providers/implementations/encode_decode/decode_pem2der.c:204
#4  0x00007fffe51e3d6e in decoder_process (params=0x0, arg=0x7fffffffd3e0) at crypto/encode_decode/decoder_lib.c:962
#5  0x00007fffe51e248a in OSSL_DECODER_from_bio (ctx=0x7bec60, in=0x7be340) at crypto/encode_decode/decoder_lib.c:81
#6  0x00007fffe57ad5b0 in ossl_pkey_read_generic (bio=0x7be340, pass=4) at ../../../../ext/openssl/ossl_pkey.c:133
#7  0x00007fffe57ad7b2 in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, self=140737035361920) at ../../../../ext/openssl/ossl_pkey.c:226
#8  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#9  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, ec=<optimized out>)
    at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#10 vm_exec_core (ec=0x0, initial=140737488342800, initial@entry=0) at /home/jaruga/src/ruby-3.2.1/insns.def:820
#11 0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true) at vm.c:2383
#12 0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#13 0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, n=n@entry=0x7ffff7e7bab8) at eval.c:289
#14 0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7bab8) at eval.c:330
#15 0x0000000000401102 in rb_main (argv=0x7fffffffda48, argc=5) at ./main.c:38
#16 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57

Then after stepping forward to get the return value, the decode_der2key.c:214 is where I can print the return value key from the ctx->desc->d2i_PKCS8(NULL, &derp, der_len, ctx) calling the rsa_d2i_PKCS8.

(gdb) f
#0  der2key_decode (vctx=0x7c2610, cin=0x7c0a30, selection=135, 
    data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffcf80, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x7bec98)
    at providers/implementations/encode_decode/decode_der2key.c:214
214             if (ctx->flag_fatal) {
(gdb) l
209     ERR_set_mark();
210     if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
211         derp = der;
212         if (ctx->desc->d2i_PKCS8 != NULL) {
213             key = ctx->desc->d2i_PKCS8(NULL, &derp, der_len, ctx);
214             if (ctx->flag_fatal) {
215                 ERR_clear_last_mark();
216                 goto end;
217             }
218         } else if (ctx->desc->d2i_private_key != NULL) {

The return value is NULL. I also printed the input values of the function and the local variables too.

(gdb) p key
$1 = (void *) 0x0

(gdb) i lo
ctx = 0x7c2610
der = 0x7c3b10 "0\202\tC\002\001" 
derp = 0x7c4457 ""
der_len = 2375
key = 0x0
ok = 0
__func__ = "der2key_decode"

(gdb) p *derp
$2 = 0 '\000'

(gdb) p der_len
$3 = 2375

(gdb) p *ctx
$4 = {provctx = 0x6ff1f0, desc = 0x7fffe54e4280 <PrivateKeyInfo_rsapss_desc>, 
  selection = 135, flag_fatal = 0}

Non-FIPS mode

I did the same thing with the FIPS mode case to get the return value.

$ gdb --args ruby -I lib -e "require 'openssl'; OpenSSL::PKey.read(File.read('key.pem'))"
(gdb) set environment LD_LIBRARY_PATH /home/jaruga/.local/openssl-3.0.8-debug/lib/
(gdb) b rsa_d2i_PKCS8
Breakpoint 2 at 0x7fffe53615b8: file providers/implementations/encode_decode/decode_der2key.c, line 506.

Below is the line where the rsa_d2i_PKCS8 is called.

(gdb) bt
#0  rsa_d2i_PKCS8 (key=0x0, der=0x7fffffffcf70, der_len=2375, ctx=0x79e5f0)
    at providers/implementations/encode_decode/decode_der2key.c:506
#1  0x00007fffe5360ec7 in der2key_decode (vctx=0x79e5f0, cin=0x79c920, selection=135, 
    data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffcfe0, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x79ac58)
    at providers/implementations/encode_decode/decode_der2key.c:213
#2  0x00007fffe51e3d6e in decoder_process (params=0x7fffffffd160, arg=0x7fffffffd310)
    at crypto/encode_decode/decoder_lib.c:962
#3  0x00007fffe5363268 in pem2der_decode (vctx=0x79c890, cin=0x79c9e0, selection=135, 
    data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffd310, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x79ac58)
    at providers/implementations/encode_decode/decode_pem2der.c:204
#4  0x00007fffe51e3d6e in decoder_process (params=0x0, arg=0x7fffffffd440)
    at crypto/encode_decode/decoder_lib.c:962
#5  0x00007fffe51e248a in OSSL_DECODER_from_bio (ctx=0x79ac20, in=0x79a220)
    at crypto/encode_decode/decoder_lib.c:81
#6  0x00007fffe57ad5b0 in ossl_pkey_read_generic (bio=0x79a220, pass=4)
    at ../../../../ext/openssl/ossl_pkey.c:133
#7  0x00007fffe57ad7b2 in ossl_pkey_new_from_data (argc=1, argv=0x7ffff7443048, 
    self=140737042308920) at ../../../../ext/openssl/ossl_pkey.c:226
#8  0x00007ffff7b309f7 in vm_call_cfunc_with_frame (ec=0x40a0c0, reg_cfp=0x7ffff7542f90, 
    calling=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_insnhelper.c:3268
#9  0x00007ffff7b35d44 in vm_sendish (method_explorer=<optimized out>, 
    block_handler=<optimized out>, cd=<optimized out>, reg_cfp=<optimized out>, 
    ec=<optimized out>) at /home/jaruga/src/ruby-3.2.1/vm_callinfo.h:367
#10 vm_exec_core (ec=0x0, initial=140737488342896, initial@entry=0)
    at /home/jaruga/src/ruby-3.2.1/insns.def:820
#11 0x00007ffff7b3bdf9 in rb_vm_exec (ec=0x40a0c0, jit_enable_p=jit_enable_p@entry=true)
    at vm.c:2383
#12 0x00007ffff7b3cde8 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2633
#13 0x00007ffff7951755 in rb_ec_exec_node (ec=ec@entry=0x40a0c0, 
    n=n@entry=0x7ffff7e7ba90) at eval.c:289
#14 0x00007ffff7957c7b in ruby_run_node (n=0x7ffff7e7ba90) at eval.c:330
#15 0x0000000000401102 in rb_main (argv=0x7fffffffdaa8, argc=5) at ./main.c:38
#16 main (argc=<optimized out>, argv=<optimized out>) at ./main.c:57
(gdb) f
#0  der2key_decode (vctx=0x79e5f0, cin=0x79c920, selection=135, 
    data_cb=0x7fffe51e36e9 <decoder_process>, data_cbarg=0x7fffffffcfe0, 
    pw_cb=0x7fffe525bc84 <ossl_pw_passphrase_callback_dec>, pw_cbarg=0x79ac58)
    at providers/implementations/encode_decode/decode_der2key.c:214
214             if (ctx->flag_fatal) {
(gdb) l
209     ERR_set_mark();
210     if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
211         derp = der;
212         if (ctx->desc->d2i_PKCS8 != NULL) {
213             key = ctx->desc->d2i_PKCS8(NULL, &derp, der_len, ctx);
214             if (ctx->flag_fatal) {
215                 ERR_clear_last_mark();
216                 goto end;
217             }
218         } else if (ctx->desc->d2i_private_key != NULL) {

The return value is NULL as well as the FIPS mode case.

(gdb) p key
$1 = (void *) 0x0

(gdb) i lo
ctx = 0x79e5f0
der = 0x79fad0 "0\202\tC\002\001"
derp = 0x7a0417 ""
der_len = 2375
key = 0x0
ok = 0
__func__ = "der2key_decode"

(gdb)  p *derp
$3 = 0 '\000'

(gdb) p der_len
$4 = 2375
(gdb) p *ctx

$5 = {provctx = 0x78b010, desc = 0x7fffe54e3280 <PrivateKeyInfo_rsapss_desc>, 
  selection = 135, flag_fatal = 0}

@levitte
Copy link
Member

levitte commented Apr 12, 2023

Just to avoid having to deal with Ruby, I made a test program that essentially does what your extension does, but limits itself to the problem domain. I can confirm seeing the same problem in my runs.

https://gist.github.com/levitte/7a27cebdb9537ff0a59641c9a5bed53d

@levitte
Copy link
Member

levitte commented Apr 12, 2023

With an OpenSSL built with enable-trace, I added these lines to my program:

    BIO *trace_bio = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
    OSSL_trace_set_channel(OSSL_TRACE_CATEGORY_DECODER, trace_bio);

That's a lot of output, but one line that I think tells a bit of the story is this (where {n} is really 0 or 1):

(ctx 0x...) >> Running constructor => {n}

When running with the FIPS module, the last such line has {n} being 0, while with the default module, it's 1. That gives me an indication where to look:

static int decoder_construct_pkey(OSSL_DECODER_INSTANCE *decoder_inst,
const OSSL_PARAM *params,
void *construct_data)

@junaruga
Copy link
Author

Wow, thank you for the test program without Ruby and your investigation! I also learned the enable-trace option in the Configure script from you!

@levitte
Copy link
Member

levitte commented Apr 12, 2023

I figured it out...

This is caused by the combination of a decoder in one provider and the keymgmt in another. This causes an export/import dance ('cause you must assume that they have different internal representations of keys, even if they are the same key type), seen here:

struct evp_keymgmt_util_try_import_data_st import_data;
import_data.keymgmt = keymgmt;
import_data.keydata = NULL;
import_data.selection = data->selection;
/*
* No need to check for errors here, the value of
* |import_data.keydata| is as much an indicator.
*/
(void)decoder->export_object(decoderctx,
object_ref, object_ref_sz,
&evp_keymgmt_util_try_import,
&import_data);
keydata = import_data.keydata;
import_data.keydata = NULL;

However, that's not the problem per se. However, there are indeed two bugs:

  1. In the snippet above, data->selection is used, which is fine in itself. It's set in ossl_decoder_ctx_setup_for_pkey(), precisely here:

    process_data->selection = ctx->selection;

    However, later calls to OSSL_DECODER_CTX_set_selection() never updates this, so the initial selection from the OSSL_DECODER_CTX_new_for_pkey() call remains throughout the lifetime of that OSSL_DECODER_CTX. This means that the setting of the selection EVP_PKEY_KEYPAIR later on in ossl_pkey_read_generic() has no effect.

    This is the reason that Calling OSSL_DECODER_from_bio 2 failed.

  2. The selection 0 isn't treated right. It should mean "gimme whatever you've got", but our keymgmt implementations don't cooperate with that mindset in the export/import scenario... I'm not quite sure how this should be resolved.

I think it shouldn't be too hard to fix the first bug. The second... not so sure.

@levitte
Copy link
Member

levitte commented Apr 12, 2023

The first bug that I mention is actually fairly easy to demonstrate. All I had to do was to apply this little patch:

@@ -9,7 +18,8 @@
     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, "DER", NULL, NULL,
+                                         EVP_PKEY_KEYPAIR, NULL, NULL);
     if (!dctx)
         goto out;
     if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, PEM_def_callback, pass) != 1)

And then I get nice runs in all cases:

$ OPENSSL_CONF=./20657.cnf OPENSSL_CONF_INCLUDE=/home/levitte/.local/opt/openssl-3.0/ssl OPENSSL_MODULES=/home/levitte/.local/opt/openssl-3.0/lib/ossl-modules ./20657 key.pem
Loaded providers:
  fips
  base
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
40C72990E97F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:../crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
Got a pkey! 0x558171f95830
It's held by the provider fips
$ ./20657 key.pem
Loaded providers:
  default
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
40D7E3D7E07F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:../crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
Got a pkey! 0x564cbb9970f0
It's held by the provider default

@levitte
Copy link
Member

levitte commented Apr 12, 2023

Do note that said small patch to ossl_pkey_read_generic() is a viable workaround, BTW. You might want to apply it to your code, @junaruga .

@junaruga
Copy link
Author

Thank you!! I am still reading your comment to understand it. And I will apply your patch above to my my OpenSSL Ruby binding code!

@junaruga
Copy link
Author

junaruga commented Apr 12, 2023

I tested the OpenSSL Ruby bindings with your patch. The result was there is an error in the 1st call of the ossl_pkey_read_generic called from the ossl_dh_initialize., before the problematic step. As you know, the ossl_pkey_read_generic is called 2 times from the ossl_dh_initialize and ossl_pkey_new_from_data in the process. I am checking why. As a note, the error message could not parse pkey (OpenSSL::PKey::DHError) is different from the Could not parse PKey (OpenSSL::PKey::PKeyError).

$ git diff
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 00a7a9c..6bec437 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -90,7 +90,8 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
     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, "DER", 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)
$ 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'; OpenSSL::PKey.read(File.read('key.pem'))"
[DEBUG] Calling ossl_pkey_read_generic from ossl_dh_initialize.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
000C50DE7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 2 failed.
000C50DE7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
[DEBUG] Calling OSSL_DECODER_from_bio 3.
000C50DE7C7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: PEM
/home/jaruga/var/git/ruby/openssl/lib/openssl/pkey.rb:132:in `initialize': could not parse pkey (OpenSSL::PKey::DHError)
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/pkey.rb:132:in `new'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/pkey.rb:132:in `new'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/ssl.rb:37:in `<class:SSLContext>'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/ssl.rb:23:in `<module:SSL>'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/ssl.rb:22:in `<module:OpenSSL>'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl/ssl.rb:21:in `<top (required)>'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl.rb:21:in `require_relative'
	from /home/jaruga/var/git/ruby/openssl/lib/openssl.rb:21:in `<top (required)>'
	from <internal:/usr/local/ruby-3.2.1/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:88:in `require'
	from <internal:/usr/local/ruby-3.2.1/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:88:in `require'
	from -e:1:in `<main>'

@junaruga junaruga changed the title How to fix OSSL_DECODER_from_bio returning 0 as failure on OpenSSL 3 FIPS mode? OSSL_DECODER_CTX_set_selection don't set the selection value properly on OpenSSL 3 FIPS mode. Apr 17, 2023
@junaruga
Copy link
Author

junaruga commented Apr 17, 2023

I changed this issue ticket's title to the "OSSL_DECODER_CTX_set_selection doesn't set the selection value properly on OpenSSL 3 FIPS mode.". I hope it's better to describe this issue.

@junaruga junaruga changed the title OSSL_DECODER_CTX_set_selection don't set the selection value properly on OpenSSL 3 FIPS mode. OSSL_DECODER_CTX_set_selection doesn't set the selection value properly on OpenSSL 3 FIPS mode. Apr 17, 2023
@t8m t8m changed the title OSSL_DECODER_CTX_set_selection doesn't set the selection value properly on OpenSSL 3 FIPS mode. OSSL_DECODER_CTX_set_selection doesn't apply the selection value properly Apr 17, 2023
@t8m t8m added branch: master Merge to master branch triaged: bug The issue/pr is/fixes a bug branch: 3.0 Merge to openssl-3.0 branch branch: 3.1 Merge to openssl-3.1 and removed triaged: question The issue contains a question labels Apr 17, 2023
@junaruga
Copy link
Author

@t8m I see that you renamed this issue ticket's title to "OSSL_DECODER_CTX_set_selection doesn't apply the selection value properly", removing the "on OpenSSL 3 FIPS mode". In my understanding, this issue only happens in the FIPS mode case. Is that right?

@levitte
Copy link
Member

levitte commented Apr 18, 2023

@junaruga, the issue is more generic than that. The way the DECODER functionality works, the provider implementations for it may live in a different provider than the one handling the keys themselves, and that's the combination where we have this issue. For you, it ended up being triggered by the combination of FIPS and base providers (the latter being where the DECODER implementations are), but it might as well have been the combination of the OQS and base providers.

@levitte
Copy link
Member

levitte commented Apr 18, 2023

... also, OSSL_DECODER_CTX_set_selection() doesn't do its job right, that's undeniable.

@junaruga
Copy link
Author

@levitte thanks for your explanation! OK. I understood it.

In my case there are 2 providers "base" and "fips". And the "base" provider handles the DECODER implementation, and "fips" provider handles (holds) the key. The issue happened as one of the cases that a provider handling the decoder is different from a provider handling a key.

@junaruga
Copy link
Author

junaruga commented Apr 28, 2023

With an OpenSSL built with enable-trace, I added these lines to my program:

@levitte I tried your way to debug with enable-trace option above for the code without workaround patch.

$ ./Configure \
  --prefix=$HOME/.local/openssl-3.0.8-fips-debug-trace \
  shared \
  enable-fips \
  enable-trace \
  -O0 -g3 -ggdb3 -gdwarf-5

$ make -j$(nproc)

$ make install

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ \
  /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/bin/openssl version
OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)

I added the following lines to your reproducing program.

diff --git a/test/20657.c b/test/20657.c
index bb4446a..6a404e0 100644
--- a/test/20657.c
+++ b/test/20657.c
@@ -5,6 +5,7 @@
 #include <openssl/pem.h>
 #include <openssl/bio.h>
 #include <openssl/provider.h>
+#include <openssl/trace.h>
 
 /* BEGIN COPY */
 /* The following is extracted from https://github.com/junaruga/openssl/raw/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c and modified to get rid of Ruby specific stuff */
@@ -136,6 +137,10 @@ static int print_provider(OSSL_PROVIDER *prov, void *unused)
 
 int main(int argc, char *argv[])
 {
+    /* Trace */
+    BIO *trace_bio = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+    OSSL_trace_set_channel(OSSL_TRACE_CATEGORY_DECODER, trace_bio);
+
     static char data[1024 * 1024];
     EVP_PKEY *pkey;
$ gcc -o 20657 20657.c -lcrypto

Then I expected a trace log in stderr. However I haven't seen it. Do you know what's wrong in my code?

$ OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl/openssl_fips.cnf \
  OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl \
  OPENSSL_MODULES=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ossl-modules \
  /home/jaruga/git/report-openssl-fips-read-error/test/20657 key.pem
Loaded providers:
  fips
  base
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
40A7FA86CB7F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 3.
40A7FA86CB7F0000:error:00800000:unknown library:ossl_pkey_new_from_data:unknown library:20657.c:126:Could not parse PKey

@levitte
Copy link
Member

levitte commented May 1, 2023

$ gcc -o 20657 20657.c -lcrypto

This links the program to the default libcrypto, not your build. To link with your build, you must tell gcc where it is:

$ gcc -I /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/include -L /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64 -o 20657 20657.c -lcrypto

Also, when you run the program, remember to set LD_LIBRARY_PATH, the same way you did when running /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/bin/openssl version

@junaruga
Copy link
Author

junaruga commented May 2, 2023

@levitte Oh thanks!!

However, I still don't see the trace log on your way. I added the OSSL_trace_enabled to see if the trace is enabled or not. And the trace is not enabled. Do you know why the trace is not enabled?

diff --git a/test/20657.c b/test/20657.c
index bb4446a..d09b421 100644
--- a/test/20657.c
+++ b/test/20657.c
@@ -5,6 +5,7 @@
 #include <openssl/pem.h>
 #include <openssl/bio.h>
 #include <openssl/provider.h>
+#include <openssl/trace.h>
 
 /* BEGIN COPY */
 /* The following is extracted from https://github.com/junaruga/openssl/raw/41bc792df2cf54660264bd6fc6368044f2877e99/ext/openssl/ossl_pkey.c and modified to get rid of Ruby specific stuff */
@@ -136,6 +137,15 @@ static int print_provider(OSSL_PROVIDER *prov, void *unused)
 
 int main(int argc, char *argv[])
 {
+    /* Trace */
+    BIO *trace_bio = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+    OSSL_trace_set_channel(OSSL_TRACE_CATEGORY_DECODER, trace_bio);
+    OSSL_trace_set_prefix(OSSL_TRACE_CATEGORY_DECODER, "BEGIN TRACE");
+    OSSL_trace_set_suffix(OSSL_TRACE_CATEGORY_DECODER, "END TRACE");
+
+    int trace_enabled = OSSL_trace_enabled(OSSL_TRACE_CATEGORY_DECODER);
+    printf("Trace enabled: %d\n", (trace_enabled) ? 1 : 0);
+
     static char data[1024 * 1024];
     EVP_PKEY *pkey;
$ gcc -I /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/include -L /home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64 -o 20657 20657.c -lcrypto

$ LD_LIBRARY_PATH=LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ \
  OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl/openssl_fips.cnf \
  OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl \
  OPENSSL_MODULES=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ossl-modules \
  ./20657 key.pem
Trace enabled: 0
Loaded providers:
  fips
  base
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
[DEBUG] Calling OSSL_DECODER_from_bio 1.
4037B270E47F0000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:101:No supported data to decode. Input type: DER
[DEBUG] Calling OSSL_DECODER_from_bio 2.
[DEBUG] Calling OSSL_DECODER_from_bio 3.
4037B270E47F0000:error:00800000:unknown library:ossl_pkey_new_from_data:unknown library:20657.c:126:Could not parse PKey

@junaruga
Copy link
Author

junaruga commented May 5, 2023

Oh I am sorry. I found my mistake (typo) in the command above about how to print the trace log. You see that I was setting LD_LIBRARY_PATH=LD_LIBRARY_PATH=... on the comment above. The tracing works with correcting my typo.

$ LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ \
  OPENSSL_CONF=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl/openssl_fips.cnf \
  OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/ssl \
  OPENSSL_MODULES=/home/jaruga/.local/openssl-3.0.8-fips-debug-trace/lib64/ossl-modules \
  ./20657 key.pem
Trace enabled: 1
Loaded providers:
  fips
  base
[DEBUG] Calling ossl_pkey_read_generic from ossl_pkey_new_from_data.
BEGIN TRACE
(ctx 0x21ae510) Looking for <NULL> decoders with selection 0
    input type: DER, input structure: <NULL>
END TRACE
...<snips>...
BEGIN TRACE
(ctx 0x21ae510) > [36] Running decoder instance 0x21c6f70 => 0 (recursed further: yes, construct called: yes)
END TRACE
4087FAFCD37F0000:error:00800000:unknown library:ossl_pkey_new_from_data:unknown library:20657.c:126:Could not parse PKey

@paulidale paulidale added the resolved: not a bug The issue is not considered a bug label May 10, 2023
@levitte
Copy link
Member

levitte commented May 10, 2023

Incorrectly closed, see #20657 (comment)

@levitte levitte reopened this May 10, 2023
@levitte levitte removed the resolved: not a bug The issue is not considered a bug label May 10, 2023
@junaruga
Copy link
Author

How is the progress about this issue?

@junaruga
Copy link
Author

I tested this issue with the reproducing program 20657.c on the latest master branch 06a0d40 . And it seems the issue still exists on it.

$ gcc \
  -I /home/jaruga/.local/openssl-3.2.0.dev-fips-debug-06a0d40322/include \
  -L /home/jaruga/.local/openssl-3.2.0.dev-fips-debug-06a0d40322/lib \
  -o 20657 20657.c -lcrypto

$ OPENSSL_CONF=$(pwd)/openssl_fips.cnf \
  OPENSSL_CONF_INCLUDE=/home/jaruga/.local/openssl-3.2.0.dev-fips-debug-06a0d40322/ssl \
  OPENSSL_MODULES=/home/jaruga/.local/openssl-3.2.0.dev-fips-debug-06a0d40322/lib/ossl-modules \
  LD_LIBRARY_PATH=/home/jaruga/.local/openssl-3.2.0.dev-fips-debug-06a0d40322/lib \
  ./20657 key.pem
Loaded providers:
  fips
  base
4097042C6A7F0000:error:00800000:unknown library:main:unknown library:20657.c:153:Could not read PKey

@junaruga
Copy link
Author

junaruga commented Aug 1, 2023

Now I understood why the issue happens with the help of the #20657 (comment) . It's not new information. But let me explain.

Cause

I used my updated reprocucer.c with the tracing logs to check it by msyelf. You can also see the tracing logs log/trace_non_fips.log and log/trace_fips.log on this repository too.

In the non-FIPS case, the log is below. 1 is ok. 0 is not ok.

$ grep 'Running constructor' log/trace_non_fips.log 
(ctx 0x22efba0) >> Running constructor
(ctx 0x22efba0) >> Running constructor => 0
(ctx 0x22efba0) >>> Running constructor
(ctx 0x22efba0) >>> Running constructor => 1

In the FIPS case, the log is below.

$ grep 'Running constructor' log/trace_fips.log 
(ctx 0x1310780) >> Running constructor
(ctx 0x1310780) >> Running constructor => 0
(ctx 0x1310780) >>> Running constructor
(ctx 0x1310780) >>> Running constructor => 0

The source code is below.

OSSL_TRACE_BEGIN(DECODER) {
BIO_printf(trc_out,
"(ctx %p) %s Running constructor\n",
(void *)new_data.ctx, LEVEL);
} OSSL_TRACE_END(DECODER);
rv = ctx->construct(decoder_inst, params, ctx->construct_data);
OSSL_TRACE_BEGIN(DECODER) {
BIO_printf(trc_out,
"(ctx %p) %s Running constructor => %d\n",
(void *)new_data.ctx, LEVEL, rv);
} OSSL_TRACE_END(DECODER);

And the ctx->construct(decoder_inst, params, ctx->construct_data); calls the crypto/encode_decode/decoder_pkey.c function decoder_construct_pkey magically. I just checked it on GDB.

static int decoder_construct_pkey(OSSL_DECODER_INSTANCE *decoder_inst,
const OSSL_PARAM *params,
void *construct_data)

And in the decoder_construct_pkey function, the non-FIPS case (only one "default" provider) is the case of the if (keymgmt_prov == decoder_prov). And the FIPS case ("base" provider and "fips" provider), is the case of the else.

if (keymgmt_prov == decoder_prov) {
keydata = evp_keymgmt_load(keymgmt, object_ref, object_ref_sz);
} else {
struct evp_keymgmt_util_try_import_data_st import_data;
import_data.keymgmt = keymgmt;
import_data.keydata = NULL;
import_data.selection = data->selection;
/*
* No need to check for errors here, the value of
* |import_data.keydata| is as much an indicator.
*/
(void)decoder->export_object(decoderctx,
object_ref, object_ref_sz,
&evp_keymgmt_util_try_import,
&import_data);
keydata = import_data.keydata;
import_data.keydata = NULL;
}

And in the else, the data->selection is not updated by the OSSL_DECODER_CTX_set_selection, and the keydata is NULL.

How to fix?

The data->selection above comes from the struct decoder_pkey_data_st *data = construct_data in the decoder_construct_pkey in the decoder_pkey.c.

So, my guess is that we need to update the selection value in the construct_data (struct decoder_pkey_data_st *) in the (OSSL_DECODER_CTX *)ctx in the OSSL_DECODER_CTX_set_selection function in the decoder_lib.c.

int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection)
{
if (!ossl_assert(ctx != NULL)) {
ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
/*
* 0 is a valid selection, and means that the caller leaves
* it to code to discover what the selection is.
*/
ctx->selection = selection;
return 1;
}

It might be like this.

diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c
index d239536175..5ff29121f7 100644
--- a/crypto/encode_decode/decoder_lib.c
+++ b/crypto/encode_decode/decoder_lib.c
@@ -175,6 +175,9 @@ int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection)
      * it to code to discover what the selection is.
      */
     ctx->selection = selection;
+
+    ctx->construct_data->selection = selection;
+
     return 1;
 }

However, the problem is the ctx->construct_data is (void *) in the decoder_lib.c where OSSL_DECODER_CTX_set_selection exists. The OSSL_DECODER_CTX is the struct ossl_decoder_ctx_st.

void *construct_data;

In the crypto/encode_decode/decoder_pkey.c, the struct decoder_pkey_data_st is defined and used to access the members of the construct_data. But we can not use this struct decoder_pkey_data_st in the function OSSL_DECODER_CTX_set_selection to access the construct_data->selection. This is a challenge to fix this issue.

struct decoder_pkey_data_st {
OSSL_LIB_CTX *libctx;
char *propq;
int selection;
STACK_OF(EVP_KEYMGMT) *keymgmts;
char *object_type; /* recorded object data type, may be NULL */
void **object; /* Where the result should end up */
};

@junaruga
Copy link
Author

junaruga commented Aug 2, 2023

In the code, when calling the following decoder->export_object, the der2key_export_object is called in the decode_der2key.c.

import_data.keymgmt = keymgmt;
import_data.keydata = NULL;
import_data.selection = data->selection;
/*
* No need to check for errors here, the value of
* |import_data.keydata| is as much an indicator.
*/
(void)decoder->export_object(decoderctx,
object_ref, object_ref_sz,
&evp_keymgmt_util_try_import,
&import_data);

static int der2key_export_object(void *vctx,
const void *reference, size_t reference_sz,
OSSL_CALLBACK *export_cb, void *export_cbarg)
{
struct der2key_ctx_st *ctx = vctx;
OSSL_FUNC_keymgmt_export_fn *export =
ossl_prov_get_keymgmt_export(ctx->desc->fns);
void *keydata;
if (reference_sz == sizeof(keydata) && export != NULL) {
/* The contents of the reference is the address to our object */
keydata = *(void **)reference;
return export(keydata, ctx->selection, export_cb, export_cbarg);
}
return 0;
}

And you see the ctx->selection is used in the function der2key_export_object.

So, the (struct der2key_ctx_st *) decoderctx has the member selection, and the value is 135, the value of the EVP_PKEY_KEYPAIR in this case. So if we can cast the decoderctx to (struct der2key_ctx_st *) in the decoder_pkey.c, we can set the value of the decoderctx.selection into the import_data.selection. But I cannot actually do it. Hmm.

@junaruga
Copy link
Author

junaruga commented Aug 8, 2023

I am experimenting one idea to fix this issue on the latest master branch 7a2bb21 including the commits by the #21519 related to this issue. I am working on this branch on my forked repository for that. The latest one commit on the branch is for the idea.

The idea is to change the member selection to selectionp (the pointer to ctx->selection in the struct decoder_pkey_data_st to access the value set by OSSL_DECODER_CTX_set_selection.. The change is below.

diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index e3aaa44902..01958e3487 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -61,7 +61,7 @@ DEFINE_STACK_OF(EVP_KEYMGMT)
 struct decoder_pkey_data_st {
     OSSL_LIB_CTX *libctx;
     char *propq;
-    int selection;
+    int *selectionp;             /* A pointer of the selection */
 
     STACK_OF(EVP_KEYMGMT) *keymgmts;
     char *object_type;           /* recorded object data type, may be NULL */
@@ -155,11 +155,11 @@ static int decoder_construct_pkey(OSSL_DECODER_INSTANCE *decoder_inst,
 
             import_data.keymgmt = keymgmt;
             import_data.keydata = NULL;
-            if (data->selection == 0)
+            if (*(data->selectionp) == 0)
                 /* import/export functions do not tolerate 0 selection */
                 import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
             else
-                import_data.selection = data->selection;
+                import_data.selection = *(data->selectionp);
 
             /*
              * No need to check for errors here, the value of
@@ -417,7 +417,7 @@ static int ossl_decoder_ctx_setup_for_pkey(OSSL_DECODER_CTX *ctx,
 
     process_data->object    = NULL;
     process_data->libctx    = libctx;
-    process_data->selection = ctx->selection;
+    process_data->selectionp = &(ctx->selection);
     process_data->keymgmts  = keymgmts;
 
     /*
@@ -561,7 +561,7 @@ ossl_decoder_ctx_for_pkey_dup(OSSL_DECODER_CTX *src,
 
         process_data_dest->object    = (void **)pkey;
         process_data_dest->libctx    = process_data_src->libctx;
-        process_data_dest->selection = process_data_src->selection;
+        process_data_dest->selectionp = process_data_src->selectionp;
         if (!OSSL_DECODER_CTX_set_construct_data(dest, process_data_dest)) {
             ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_OSSL_DECODER_LIB);
             goto err;

However, the value set by the OSSL_DECODER_CTX_set_selection is not propagated to the construct_data->selectionp in the decoder_construct_pkey.

I am debugging this with my reproducer.

At the following step to set the process_data initially with the selection: 0, the value of the process_data->selectionp is 0x5391e0.

In ossl_decoder_ctx_setup_for_pkey in crypto/encode_decode/decoder_pkey.c

418     process_data->object    = NULL;
419     process_data->libctx    = libctx;
420     process_data->selectionp = &(ctx->selection);
421     process_data->keymgmts  = keymgmts;
(gdb) f
#0  ossl_decoder_ctx_setup_for_pkey (ctx=0x5391d0, keytype=0x0, libctx=0x0, propquery=0x0)
    at crypto/encode_decode/decoder_pkey.c:421
421     process_data->keymgmts  = keymgmts;
(gdb) p process_data->selectionp
$1 = (int *) 0x5391e0
(gdb) p &(ctx->selection)
$2 = (int *) 0x5391e0

Then when setting the EVP_PKEY_KEYPAIR in the OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR); in the reproducer.c, The code is below. The value of the &(ctx->selection) is 0x551be0. It's same value from the code above. So far so good.

(gdb) f
#0  OSSL_DECODER_CTX_set_selection (ctx=0x551bd0, selection=135)
    at crypto/encode_decode/decoder_lib.c:178
178     return 1;
(gdb) p ctx->selection
$4 = 135
(gdb) p &(ctx->selection)
$5 = (int *) 0x551be0

But then on the following step to get the data->selectionp, the value of the data->selectionp is 0x551be0. This is different pointer address from the code above. This is weird to me. The *(data->selectionp) is 0, not 135. Do you know why the pointer address in this step is different from the steps above? What do you think about this idea to fix this issue?

In decoder_construct_pkey in crypto/encode_decode/decoder_pkey.c

158             if (*(data->selectionp) == 0)
159                 /* import/export functions do not tolerate 0 selection */
160                 import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
161             else
162                 import_data.selection = *(data->selectionp);
(gdb) f
#0  decoder_construct_pkey (decoder_inst=0x551c40, params=0x7fffffffd140,
    construct_data=0x5527d0) at crypto/encode_decode/decoder_pkey.c:158
158             if (*(data->selectionp) == 0)
(gdb) p data->selectionp
$6 = (int *) 0x5391e0
(gdb) p *(data->selectionp)
$7 = 0

@t8m t8m added the branch: 3.2 Merge to openssl-3.2 label Oct 26, 2023
@beldmit
Copy link
Member

beldmit commented Dec 19, 2023

What is the status of this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
branch: master Merge to master branch branch: 3.0 Merge to openssl-3.0 branch branch: 3.1 Merge to openssl-3.1 branch: 3.2 Merge to openssl-3.2 triaged: bug The issue/pr is/fixes a bug
Projects
None yet
Development

No branches or pull requests

5 participants