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

Current master does not load RDRAND engine by default? #16996

Closed
mouse07410 opened this issue Nov 9, 2021 · 23 comments
Closed

Current master does not load RDRAND engine by default? #16996

mouse07410 opened this issue Nov 9, 2021 · 23 comments
Labels
branch: 3.0 Merge to openssl-3.0 branch resolved: answered The issue contained a question which has been answered triaged: question The issue contains a question

Comments

@mouse07410
Copy link
Contributor

$ openssl3 version
OpenSSL 3.1.0-dev  (Library: OpenSSL 3.1.0-dev )
$ openssl rand -hex 16
$ openssl rand -engine rdrand  -hex 16
Engine "rdrand" set.
ac91a3fbbfda96c49b82316d3b6a301d
$

OpenSSL-1.1.1 seemed to have no problem finding RDRAND engine:

$ openssl11 version
OpenSSL 1.1.1l  24 Aug 2021
$ openssl11 rand -hex 16
1ddc8ecf8b358bd899744a35c2796d86
$ 

Needless to say, I want/need RDRAND engine to be loaded by default (in 3.+). What do I need to do with openssl.cnf to get it?

@mouse07410 mouse07410 added the issue: bug report The issue was opened to report a bug label Nov 9, 2021
@paulidale paulidale added branch: 3.0 Merge to openssl-3.0 branch triaged: question The issue contains a question and removed issue: bug report The issue was opened to report a bug labels Nov 9, 2021
@paulidale
Copy link
Contributor

Did you read the INSTALL.md file?

Configure with --with-rand-seed=rdcpu,os

@paulidale paulidale added the resolved: answered The issue contained a question which has been answered label Nov 9, 2021
@mouse07410
Copy link
Contributor Author

mouse07410 commented Nov 9, 2021

I did read it - and my config includes --with-rand-seed=rdcpu. I did not add ,os because (if possible) I want to use only RDRAND/RDSEED. And despite that, I'm having this problem - unless I explicitly specify the engine in the command line, openssl rand returns zero bytes.

@paulidale
Copy link
Contributor

That's unexpected, the engine isn't trying to use RDSEED whereas the built-in source does.

Is it possible that RDSEED is found but not working?

@mouse07410
Copy link
Contributor Author

That's unexpected...

My point exactly! ;-)

Is it possible that RDSEED is found but not working?

99.999% that RDSEED is working fine on these CPUs.

However, if memory serves me, RDSEED can fail, and much more often than RDRAND (e.g., see https://stackoverflow.com/questions/14413839/what-are-the-exhaustion-characteristics-of-rdrand-on-ivy-bridge).

@mouse07410
Copy link
Contributor Author

Running a test-program on this machine shows that RDSEED is not only detectable, but works quite well:

$ ./rdseed-test 
RDRAND: 1751837
RDSEED: 1752472, 1.00

Here's the source:

/* Compare the performance of RDSEED and RDRAND.
 *
 * Compute the CPU time used to fill a buffer with (pseudo) random bits 
 * using each instruction.
 *
 * Compile with: gcc -mrdrnd -mrdseed
 */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <x86intrin.h>

#define BUFSIZE (1<<24)

int main() {

  unsigned int ok, i;
  unsigned long long *rand = malloc(BUFSIZE*sizeof(unsigned long long)), 
                     *seed = malloc(BUFSIZE*sizeof(unsigned long long)); 

  clock_t start, end, bm;

  // RDRAND (the benchmark)
  start = clock();
  for (i = 0; i < BUFSIZE; i++) {
    while (!_rdrand64_step(&rand[i]))
        ;
  }
  bm = clock() - start;
  printf("RDRAND: %li\n", bm);

  // RDSEED
  start = clock();
  for (i = 0; i < BUFSIZE; i++) {
    while (!_rdseed64_step(&seed[i]))
        ;
  }
  end = clock();
  printf("RDSEED: %li, %.2lf\n", end - start, (double)(end-start)/bm);

  free(rand);
  free(seed);
  return 0;
}

@paulidale
Copy link
Contributor

In 1.1.1, the code calling RDRAND/RDSEED is identical to 3.0 and master as far as I can see. It doesn't fall back from RDRAND to RDSEED. This is deliberate since the former is specifically recommended as the source for RNGs.

The ,os addition on the configure line should help in the case when RDSEED does fail.

Was 1.1.1 also configured with just the CPU randomness source enabled?

@mouse07410
Copy link
Contributor Author

I changed configuration to --seed=rdcpu,os - no effect. Same problem in 3.+. I guess I'll leave the config with "rdcpu,os" for 3.x from now on, regardless.

1.1.1 has always been configured with "rdcpu,os". Thankfully, it didn't exhibit this weird problem.

RDSEED is considered "more secure", but in my opinion RDRAND is secure enough to fallback to it. Perhaps you can add a config (or runtime) parameter to enable this fallback?

Though I admit I can't be sure that RDSEED is the cause of it. It appears to me that 3.x somehow fails to "fully load" RDRAND engine - so, on one hand is aware that it's present and doors not fall back to another source ("os"?), but on the other hand, it didn't complete the actual loading - so requests do but reach it. Just my vague theory.

@paulidale
Copy link
Contributor

Neither 1.1.1 nor 3.0 use the RDRAND engine to access these instructions. Loading the engine adds a different path.

That one works and the other doesn't is a mystery currently.

@mouse07410
Copy link
Contributor Author

mouse07410 commented Nov 10, 2021

Anything I can do to help unravel this mystery?

Also, let me repeat myself: if indeed it's failing on RDSEED - I think it would be a good idea to have a config-parameter-controlled fallback to RDRAND.

Update

Is there any way to make RDRAND engine automatically loaded (and initialized)? Since there doesn't seem to be a separate .so or .dylib, I can't figure how to do it (there's no dynamic_path or something similar that I can point openssl.cnf at).

Here's one reason why I want it:

$ openssl3 version
OpenSSL 3.1.0-dev  (Library: OpenSSL 3.1.0-dev )
$ openssl3 speed -evp gost89-cnt
Doing gost89-cnt for 3s on 16 size blocks: 10328662 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 64 size blocks: 2891772 gost89-cnt's in 3.01s
Doing gost89-cnt for 3s on 256 size blocks: 743510 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 1024 size blocks: 187525 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 8192 size blocks: 23567 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 16384 size blocks: 11780 gost89-cnt's in 3.01s
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
002ECE1B01000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
002ECE1B01000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
$ openssl3 speed -engine rdrand -evp gost89-cnt
Engine "rdrand" set.
Doing gost89-cnt for 3s on 16 size blocks: 10090753 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 64 size blocks: 2816276 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 256 size blocks: 720825 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 1024 size blocks: 182395 gost89-cnt's in 3.00s
Doing gost89-cnt for 3s on 8192 size blocks: 22861 gost89-cnt's in 3.01s
Doing gost89-cnt for 3s on 16384 size blocks: 11329 gost89-cnt's in 3.00s
version: 3.1.0-dev
built on: Tue Nov  9 19:33:22 2021 UTC
options: bn(64,64)
compiler: clang -fPIC -arch x86_64 -O3 -std=gnu18 -march=native -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -g -DL_ENDIAN -DOPENSSL_PIC -D_REENTRANT -DOPENSSL_BUILDING_OPENSSL -DZLIB -DZLIB_SHARED
CPUINFO: OPENSSL_ia32cap=0x7ffef3ffffebffff:0x139efffb
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
gost89-cnt       53817.35k    60080.55k    61510.40k    62257.49k    62218.38k    61871.45k
$ 

@paulidale
Copy link
Contributor

Is it possible to determine that RDSEED is indeed the problem? The code is in:

static size_t get_hardware_random_value(unsigned char *buf, size_t len) {
/* Whichever comes first, use RDSEED, RDRAND or nothing */
if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) {
if (OPENSSL_ia32_rdseed_bytes(buf, len) != len)
return 0;
} else if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) {
if (OPENSSL_ia32_rdrand_bytes(buf, len) != len)
return 0;
} else
return 0;
return len;
}

If RDSEED is a problem, RDRAND might be too so it would be worthwhile checking this. It can be done via the OPENSSL_ia32cap environment variable which avoids a recompile.

@mouse07410
Copy link
Contributor Author

mouse07410 commented Nov 10, 2021

@paulidale first - doesn't the program I ran (see the source above) prove that both RDRAND and RDSEED basically work OK on this machine/CPU?

Second - I think I understand what the code you listed actually does, and probably can figure how to modify it to behave in the way I suggested. But I doubt I could use it as a "template" to write my standalone test...

And again, sorry for repeating myself - I strongly doubt the problem is RDRAND, because if it was - then choosing a different path by involving RDRAND engine* wouldn't've saved the day. For that engine uses RDRAND (and/or RDSEED) for sure. Thus, the problem must be somewhere in this path... Do I make sense?

Oh, in case it matters:

$ openssl info -cpusettings
OPENSSL_ia32cap=0x7ffaf3bfffebffff:0x40000008029c67af

And here's what Botan thinks of the CPU extensions:

$ botan cpuid
CPUID flags: sse2 ssse3 sse41 sse42 avx2 rdtsc bmi1 bmi2 adx aes_ni clmul rdrand rdseed

@paulidale
Copy link
Contributor

If it's not the seed generation, most likly be failing to get there. Not much happens post collection that's likely to fail. That fits with the known data.

Is it possible to set a breakpoint in ossl_pool_acquire_entropy() and single step to the point of failure? The would definitively point the finger at the seed generation, before or after:

  • if the function returns a positive value, the seed has been generated and the failure is later;
  • if it's never called, the failure is early and
  • if it returns zero, the failure is during generation.
    I'm at a loss as to what's wrong currently.

@mouse07410
Copy link
Contributor Author

Is it possible to set a breakpoint in ossl_pool_acquire_entropy() and single step to the point of failure?

It does not seem to reach there...?!

I tried:

$ lldb apps/openssl
(lldb) target create "apps/openssl"
Current executable set to '/Users/ur20980/src/openssl/apps/openssl' (x86_64).
(lldb) rbreak ossl_pool_acquire_entropy()
Breakpoint 1: where = libcrypto.3.dylib`ossl_pool_acquire_entropy + 23 at rand_unix.c:643:24, address = 0x0000000000298167
(lldb) run speed -evp gost89
Process 57302 launched: '/Users/ur20980/src/openssl/apps/openssl' (x86_64)
Doing gost89 for 3s on 16 size blocks: 12370233 gost89's in 3.00s
Doing gost89 for 3s on 64 size blocks: 3400775 gost89's in 3.00s
Doing gost89 for 3s on 256 size blocks: 862768 gost89's in 3.00s
Doing gost89 for 3s on 1024 size blocks: 220842 gost89's in 3.00s
Doing gost89 for 3s on 8192 size blocks: 27688 gost89's in 3.01s
Doing gost89 for 3s on 16384 size blocks: 13939 gost89's in 3.00s
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
00FE1B0001000000:error:4280007E:lib(133)::rng error:/Users/ur20980/src/grasshopper-engine/gost_crypt.c:878:
00FE1B0001000000:error:03000085:digital envelope routines:EVP_CIPHER_CTX_ctrl:ctrl operation not implemented:crypto/evp/evp_enc.c:1236:
Process 57302 exited with status = 1 (0x00000001) 
(lldb) run rand -hex 4
Process 57319 launched: '/Users/ur20980/src/openssl/apps/openssl' (x86_64)
Process 57319 exited with status = 1 (0x00000001) 
(lldb) run rand -engine rdrand -hex 4
Process 57325 launched: '/Users/ur20980/src/openssl/apps/openssl' (x86_64)
Engine "rdrand" set.
8d1e05e9
Process 57325 exited with status = 0 (0x00000000) 
(lldb) 

In case it helps:
https://github.com/gost-engine/engine/blob/df3ead272bd2019f98d16e6787f5df51556c0603/gost_crypt.c#L874-L882

and

openssl/crypto/evp/evp_enc.c

Lines 1211 to 1239 in 90c3113

case EVP_CTRL_AEAD_SET_MAC_KEY:
if (arg < 0)
return -1;
params[0] = OSSL_PARAM_construct_octet_string(
OSSL_CIPHER_PARAM_AEAD_MAC_KEY, ptr, sz);
break;
}
if (set_params)
ret = evp_do_ciph_ctx_setparams(ctx->cipher, ctx->algctx, params);
else
ret = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params);
goto end;
/* Code below to be removed when legacy support is dropped. */
legacy:
if (ctx->cipher->ctrl == NULL) {
ERR_raise(ERR_LIB_EVP, EVP_R_CTRL_NOT_IMPLEMENTED);
return 0;
}
ret = ctx->cipher->ctrl(ctx, type, arg, ptr);
end:
if (ret == EVP_CTRL_RET_UNSUPPORTED) {
ERR_raise(ERR_LIB_EVP, EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED);
return 0;
}
return ret;

@paulidale
Copy link
Contributor

That's useful. Now to figure out what to do next....

@mouse07410
Copy link
Contributor Author

Darn... I found the problem, though not its cause.

This works:

[engine_section]
#pkcs11 = pkcs11_section
gost = gost_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /opt/local/libexec/openssl3/lib/engines-3/pkcs11.dylib
MODULE_PATH = /Library/OpenSC/lib/opensc-pkcs11.so
init = 0

[gost_section]
engine_id = gost
dynamic_path = /opt/local/libexec/openssl3/lib/engines-3/gost.dylib
default_algorithms = ALL
#CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
PBE_PARAMS = "gost12_512"

This does not:

[engine_section]
pkcs11 = pkcs11_section
gost = gost_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /opt/local/libexec/openssl3/lib/engines-3/pkcs11.dylib
MODULE_PATH = /Library/OpenSC/lib/opensc-pkcs11.so
init = 0

[gost_section]
engine_id = gost
dynamic_path = /opt/local/libexec/openssl3/lib/engines-3/gost.dylib
default_algorithms = ALL
#CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
PBE_PARAMS = "gost12_512"

In other words, pkcs11 engine somehow screws up access to randomness - which is weird, because (a) it shouldn't, and (b) I have a PKCS#11 token present that outputs random data upon request (without needing a PIN). Commenting its entry out (partially!) remedies the situation.

I'd appreciate if you could elucidate on what may be going on.

@mtrojnar and @dengert would you like to comment here?

@paulidale
Copy link
Contributor

Phew! This makes some kind of sense.

I'm guessing the PKCS#11 engine is installing a default randomness source. This will override the built-in sources. I'd not even considered this possibility. Good sleuthing.

Adding the command line option will add the rdrand source over the top.

@dengert
Copy link
Contributor

dengert commented Nov 10, 2021 via email

@mouse07410
Copy link
Contributor Author

That might lead to an attack on your system.

Could you please provide more details?

@paulidale
Copy link
Contributor

I'm pretty sure the underlying problem is that there is a call to RAND_set_rand_method() or RAND_set_rand_engine() occurring (likely the latter).

These completely replace the built in RNG infrastructure with the RAND_METHOD/engine. If the engine then fails to produce output for any reason, the observed results will present.

Adding the RDRAND engine again replaces the RAND_METHOD and things begin working.

I've no idea why the PKCS#11 engine has stopped working with 3.0. It wasn't meant to.

@dengert
Copy link
Contributor

dengert commented Nov 10, 2021

@mouse07410, Sorry, I assumed you where asking on the OpenSC list and assumed you were trying to use a random number generator from a smart card via PKCS11. If the host was going to use the card to provide a random number that would be used help authenticate the user, it might be possible for an attacker to use a bogus device that returned a carefully crafted non-random number. This might help in hacking the authentication.

@mouse07410
Copy link
Contributor Author

@dengert thank you - I understand the threat now. As I see it, good defense would be to only use card-extracted randomness when the user is present and observes that the card is "genuine".

Also, Doug, it looks like libp11 claims to provide RAND_xxx() implementation, but fails to actually serve any randomness. Do you think it's "fixable"?

@mouse07410
Copy link
Contributor Author

mouse07410 commented Nov 14, 2021

I'm pretty sure the underlying problem is that there is a call to RAND_set_rand_method() or RAND_set_rand_engine() occurring (likely the latter).

@paulidale I checked the sources of the libp11 PKCS#11 engine - and neither of the two above calls is present there:

$ pwd
/Users/ur20980/src/libp11
$ find . -name '*.[ch]' -exec fgrep -n RAND_set {} \;
$ 

But I found this in src/eng_front.c (of my fork, upstream seems to have taken care of it):

.  .  .  .  .
static RAND_METHOD *PKCS11_get_rand_method(void) {

        static RAND_METHOD ops = {
                .bytes = rand_bytes,
        };

        return &ops;
}

/* This internal function is used by ENGINE_pkcs11() and possibly by the
 * "dynamic" ENGINE support too */
static int bind_helper(ENGINE *e)
{
        if (!ENGINE_set_id(e, PKCS11_ENGINE_ID) ||
                        !ENGINE_set_destroy_function(e, engine_destroy) ||
                        !ENGINE_set_init_function(e, engine_init) ||
                        !ENGINE_set_finish_function(e, engine_finish) ||
                        !ENGINE_set_ctrl_function(e, engine_ctrl) ||
                        !ENGINE_set_cmd_defns(e, engine_cmd_defns) ||
                        !ENGINE_set_name(e, PKCS11_ENGINE_NAME) ||
            //#if OPENSSL_VERSION_NUMBER  < 0x10100002L
                        !ENGINE_set_RAND(e, PKCS11_get_rand_method()) ||
            //#endif /* OPENSSL_VERSION_NUMBER */
#ifndef OPENSSL_NO_RSA
                        !ENGINE_set_RSA(e, PKCS11_get_rsa_method()) ||
#endif
.  .  .  .  .

Testing now with those lines removed.

@mouse07410
Copy link
Contributor Author

Removing that binding alleviated the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
branch: 3.0 Merge to openssl-3.0 branch resolved: answered The issue contained a question which has been answered triaged: question The issue contains a question
Projects
None yet
Development

No branches or pull requests

3 participants