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
Add RSA key generation [ERL-165] #1299
Conversation
Support RSA key generation using generate_key(rsa, {bits, e}). This depends on the currently-experimental "dirty scheduler" support because key generation is a potentially lengthy process.
@@ -1008,7 +1027,8 @@ group_config(rsa = Type, Config) -> | |||
rsa_oaep(), | |||
no_padding() | |||
], | |||
[{sign_verify, SignVerify}, {pub_priv_encrypt, PubPrivEnc} | Config]; | |||
Generate = [{rsa, 1024, 3}, {rsa, 2048, 17}, {rsa, 3072, 65537}], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generate/1 test case fails on FIPS 140-2 enabled libs for the 1024 length keys. See the case ?config(fips, Config) of ... end
a few lines above for inspiration of a fix. I haven't found any clear indication anywhere whether the 2k length is enough, but the 1k is surely to short in the FIPS case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh. Chasing down some references, it looks like the required minimum key length for FIPS 140-2 validation is currently 2048 bits (from "FCS_CKM.1.1(1)" here).
There's probably no need to test key generation with a huge number of parameters; I'll just remove the 1024-bit test case.
If the underlying library is in FIPS mode, it'll refuse to generate keys shorter than 2048 bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two points on the documentation:
-
Dirty schedulers will lose there "experimental" status in OTP-20, so you can remove that word.
-
We will change enif_schedule_nif to raise 'notsup' exception instead of 'badarg' if dirty schedulers are not enabled. And I think this is more correct for crypto:generate_key. So, why not mention the 'notsup' exception in the note about dirty scheduler support.
to reflect that dirty schedulers are no longer considered "experimental", per a comment from sverker.
Our valgrind tests complain about ERR_load_crypto_strings() leaking memory. Seems to me like a call to ERR_free_string() in unload() when --library_refc == 0 should fix that. |
@@ -1027,7 +1027,7 @@ group_config(rsa = Type, Config) -> | |||
rsa_oaep(), | |||
no_padding() | |||
], | |||
Generate = [{rsa, 1024, 3}, {rsa, 2048, 17}, {rsa, 3072, 65537}], | |||
Generate = [{rsa, 2048, 3}, {rsa, 3072, 65537}], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunatly it still fails on the FIPS-enabled machine. Maybe 2k should be removed also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. 2k should be okay, from everything I've read, so I'd rather not remove the test without a better understanding of why it's failing. What error message does it throw?
I'll see if I can build a FIPS build and investigate locally, but I probably won't have time for a few days / a week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange... Unfortunately the test logs has been removed because they are kept only a couple of days. I've added the branch to testing again so we can see tomorrow what the message was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With fips mode 2k gives error, but 3k does not. The complaint is on the e value, not the modulus wich I didn't expect. Does FIPS prescribe a different algorithm for generate_key so the e becomes invalid?
14> crypto:enable_fips_mode(false).
true
15> f(),{Pub,Priv} = crypto:generate_key(rsa, {2048,3}), S = crypto:sign(rsa, sha256, <<"Hello world">>, Priv), crypto:verify(rsa, sha256, <<"Hello world">>, S, Pub).
true
16> f(),{Pub,Priv} = crypto:generate_key(rsa, {3072,65537}), S = crypto:sign(rsa, sha256, <<"Hello world">>, Priv), crypto:verify(rsa, sha256, <<"Hello world">>, S, Pub).
true
17>
17>
17> crypto:enable_fips_mode(true). true
18> f(),{Pub,Priv} = crypto:generate_key(rsa, {2048,3}), S = crypto:sign(rsa, sha256, <<"Hej hopp">>, Priv), {S, crypto:verify(rsa, sha256, <<"Hej hopp">>, S, Pub)}.
** exception error: {error,{openssl,[{"rsa routines","RSA_BUILTIN_KEYGEN",
"bad e value"}]}}
in function crypto:rsa_generate_key_nif/2
called as crypto:rsa_generate_key_nif(2048,<<3>>)
in call from crypto:generate_key/3 (crypto.erl, line 456)
19> f(),{Pub,Priv} = crypto:generate_key(rsa, {3072,65537}), S = crypto:sign(rsa, sha256, <<"Hello world">>, Priv), crypto:verify(rsa, sha256, <<"Hello world">>, S, Pub).
true
20> crypto:info_lib().
[{<<"OpenSSL">>,268439711,
<<"OpenSSL 1.0.1i-fips 6 Aug 2014">>}]
21>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! I understand now. Using a small public exponent e used to be considered a problem; I guess FIPS prohibits it. I'll change the test case to {2048,17}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! You found it, but {2048,17} still fails. However {2048,65537} works!
And don't forget the Valgrind fix that Sverker commented on.
Just for your information: when I test this in the shell, with fips disabled it takes 0.2-0.3 s to generate the key pair, but with fips enabled 0.9-1.3 s! So the fips mode does more than simply checking the sizes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For fun I just checked {1024,65537} and that works in fips mode.! But {512,65537} gives expected error:
{error,{openssl,[{"FIPS routines","RSA_BUILTIN_KEYGEN",
"key too short"}]}}
About ERR_load_crypto_strings() leaking memory. I don't think calling ERR_free_string() in unload will be correct as there can be two (current and old) different crypto.so loaded at the same time sharing the same libcrypto.so. We can't call ERR_free_string() until libcrypto.so is unloaded and that is not easy to know when it happens. Any ideas? |
@sverker - It looks like the I'm a little concerned about the possibility of the same libcrypto.so being loaded (indirectly) by some other erlang module, and being broken when crypto.so de-initalizes libcrypto.so. I'm not sure how to handle this, other than that openssl 1.1.0 claims to do the necessary de-initialization internally so that the problem doesn't occur. |
No, If OpenSSL 1.1.0 solves this by doing automatic de-initialization, then one solution could be to skip the fancy error messages for older versions. |
Any activity on this PR? |
This adds support for RSA key generation using
generate_key(rsa, {bits, e})
(see the ERL-165 bug comments section).Key generation is slow, so this relies on being able to schedule the computation using
ERL_NIF_DIRTY_JOB_CPU_BOUND
. The test case should be automatically skipped if dirty schedulers aren't available.I've tested this on an erlang linked against openssl 1.0.1, but it should work with 0.9.8 and 1.1.0 as well.