-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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 RSASVE from SP800-56Br2 #12750
Add RSASVE from SP800-56Br2 #12750
Conversation
@mattcaswell - I am a bit stuck with how to plumb this in.. It looks nearly like the normal rsa encrypt except the thing to encrypt is an internally generated random secret - not a passed in input.. The decrypt (recover) is just a straight decrypt (with a length check) to recover the secret.. The operations use RSA oeap. So I am wondering if I should have a generate and recover function that I just add to the rsa_enc dispatch table and then have EVP_PKEY_generate_init(), EVP_PKEY_generate() & EVP_PKEY_recover_init(), EVP_PKEY_recover() API's.. I have tried to create a separate function table and then dont quite know how to select it.. |
I believe it would be better to drop support for this in 3.0 or, if we really need it in, we should do the proper thing and treat RSA SVE as a KEM. KEM as an abstract operation has 3 fundamental components:
We would need:
"Abusing" the asym cipher operation in the way the draft currently does might work as a special handling for RSA SVE, but is a hack and I am afraid we would have to pay the cost of supporting this hack in the future, exactly as we are paying the price of merging SM2 too close to the beta of 1.1.1 and having to deal with a lot of ugliness now (and for many years to come). |
As previously stated - it is a fundamental requirement of SP800-56B - so it needs to go in. |
I think the approach needed is a minimal interface that can be expanded later using set_params to do other operations if required.. |
So a KEM looks like this..
For now I only need steps 1 & 2 because that is what is required for Sp800-56b.
Hopefully you can see that a KDF + WRAP is not needed here.. NOTES: Being able to add a KDF and wrap step can be done later iff they are needed (This could be done with set_params() OR another API..).. |
Why SP800-56B must be supported and claimed by the OpenSSL 3.0 FIPS module? There are other FIPS compliant key establishment methods supported (ephemeral DH and ECDH namely). The SP800-56B is unusable by TLS anyway. |
56A and 56B were part of the specs that we said we support in the initial design.. |
I nearly have a implementation taking into account @romen's comments.. |
The thing I initially did was overly complicated... I for some reason thought it was using OEAP.. it doesnt, so it is a simple RSA operation.. |
Is this still draft, or ready for review? I note we are missing documentation for the new |
I just sent a email in relation to this PR.. Feedback on the API's and the passed parameters in particular would be welcome.. |
I would prefer to not do documentation until people are happy with the direction of this PR. |
I haven't played with it, and so far only looked at the code, but so far this looks very good to me! Thanks @slontis for taking in my feedback! I have two notes so far:
As a side note, and trying to avoid yet another naming war by disclaiming that I am not particularly against wrap/unwrap, I wonder if KEM would be less surprising for external future users and if we are diverging from existing patterns by having
instead of
(I would prefer EVP_PKEY_KEM_*) |
I have done this by passing the algorithm to the init (so that it fetches the correct algorithm without having to use the default query) e.g. hmm yes my rsasve_init() assumes the thing it is being passed is a RSA*.. |
Does this name really apply to the RSASVE? |
If we need to allow a user to supply their own symmetric key what would be the best way of supplying this info.. |
Taken out of draft now that it has been documented. |
Are there other operations where we do this? Also this would probably also require a propquery arg?
This might be an issue, also it opens a door for an RSA key coming from a provider and an RSASVE implementation coming from another provider, and no key conversion inbetween, probably resulting in all kinds of segfaults!
To avoid adding a separate RSASVE keymgmt we could make RSASVE the default KEM for RSA, and let RSAFOO or RSABAR in OpenSSL 3.42 deal with adding the new keymgmts for them. So if we don't embed RSASVE in the RSA keymgmt, and we stick with the convention we decided upon of only one operation implementation for key type, it looks like adding a new keymgmt for a new operation is inevitable..
... but as you suggest actual code duplication should be minimal. Let's assume there are alternative KEM implementations built on top of RSA (which is not a fantasy scenario, given that there is an exponential number of alternative combinations of symm ciphers, kdf, aead modes for wrapping and knobs to play with): if FIPS already mandated 2 alternative ones and we already were implementing both of them now, having separate keymgmts would be the only way and probably we wouldn't be even discussing it! |
In my limited knowledge and under the duck test I ran, it seems to apply: as a black box it definitely seems to behave as one. Under the hood it might not come with all the bells and whistles of other academic results with full-fledged security proofs (I am not saying that it does not, just that I did not do enough research to confirm nor deny it), but its API does quack like a KEM API. |
This is an antipattern with KEM, I see the lure of it for testing purposes, but exposing such a mechanism to users opens many potential cans of worms (not the ones you would use for bait, more like the ones from Arrakis). Still an experimental external provider that is building its implementation and needs to access the guts could temporarily expose a settable parameter to do this. For the same reason, while it is ok if a provider decides to offer such a functionality to users through |
Rebased.. |
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.
I have a few nits for now:
One is left as a comment below, the others are warnings raised by check-doc
*** WARNING: line containing nothing but whitespace in paragraph at line 51 in file doc/man3/EVP_PKEY_KEM_decapsulate.pod
*** WARNING: line containing nothing but whitespace in paragraph at line 55 in file doc/man3/EVP_PKEY_KEM_decapsulate.pod
*** WARNING: line containing nothing but whitespace in paragraph at line 51 in file doc/man3/EVP_PKEY_KEM_encapsulate.pod
*** WARNING: line containing nothing but whitespace in paragraph at line 55 in file doc/man3/EVP_PKEY_KEM_encapsulate.pod
*** WARNING: line containing nothing but whitespace in paragraph at line 29 in file doc/man7/provider-kem.pod
I am also testing out one last thing about the behavior of normal RSA keys using RSASVE as an alias, to ensure we are not binding RSASVE as the default KEM for "plain RSA" keys. I'll get back about it later!
I applied these 2 patches on top of 95a2bdc4e8b884bd118f442becd8d21364c1325a From 44169cd4065de2caec06405c02b98779b2726979 Mon Sep 17 00:00:00 2001
From: Nicola Tuveri <[email protected]>
Date: Tue, 8 Sep 2020 13:18:34 +0300
Subject: [PATCH 1/2] [test][rsasve] Use TEST_int_eq instead of TEST_true
The newly added documentation states that
- `EVP_PKEY_KEM_encapsulate_init`
- `EVP_PKEY_KEM_encapsulate`
- `EVP_PKEY_KEM_decapsulate_init`
- `EVP_PKEY_KEM_decapsulate`
Should return 1 for success, 0 or negative for failure (and explicitly
-2 if the operation is not supported).
This commit replace TEST_true(foo()) with TEST_int_eq(foo(), 1) to test
for success of these functions within the rsasve_gen_recover() test.
---
test/evp_libctx_test.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/test/evp_libctx_test.c b/test/evp_libctx_test.c
index d73f6eeaef..f56a2e60c6 100644
--- a/test/evp_libctx_test.c
+++ b/test/evp_libctx_test.c
@@ -485,25 +485,27 @@ static int rsasve_gen_recover(void)
if (!TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(libctx, pub, NULL)))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_encapsulate_init(sctx)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_encapsulate_init(sctx), 1))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_encapsulate(sctx, NULL, &ctlen, NULL,
- &secretlen)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_encapsulate(sctx, NULL, &ctlen, NULL,
+ &secretlen), 1))
goto done;
if (!TEST_int_eq(ctlen, secretlen)
|| !TEST_int_eq(ctlen, 2048 / 8))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_encapsulate(sctx, ct, &ctlen, secret,
- &secretlen)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_encapsulate(sctx, ct, &ctlen, secret,
+ &secretlen), 1))
goto done;
if (!TEST_ptr(rctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL)))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_decapsulate_init(rctx)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_decapsulate_init(rctx), 1))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_decapsulate(rctx, NULL, &unwraplen, ct, ctlen)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_decapsulate(rctx, NULL, &unwraplen,
+ ct, ctlen), 1))
goto done;
- if (!TEST_true(EVP_PKEY_KEM_decapsulate(rctx, unwrap, &unwraplen, ct, ctlen)))
+ if (!TEST_int_eq(EVP_PKEY_KEM_decapsulate(rctx, unwrap, &unwraplen,
+ ct, ctlen), 1))
goto done;
if (!TEST_mem_eq(unwrap, unwraplen, secret, secretlen))
goto done;
--
2.27.0 From 36f23f47cb1a5fdb86b24ebeedac11df55400c96 Mon Sep 17 00:00:00 2001
From: Nicola Tuveri <[email protected]>
Date: Tue, 8 Sep 2020 13:33:25 +0300
Subject: [PATCH 2/2] [test] plain RSA keys should not support EVP_PKEY_KEM
operations
This adds negative testing to ensure that a plain RSA key does not
default to RSASVE if used in the context of the `EVP_PKEY_KEM_*`
operations.
Making RSASVE the default is not wrong per se, but we don't want to
commit to this behavior now without further consideration: in a future
release we might want to assign a default KEM algorithm for RSA keys and
we don't want to be held back by accidentally creating a default.
---
test/evp_libctx_test.c | 59 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/test/evp_libctx_test.c b/test/evp_libctx_test.c
index f56a2e60c6..f75f977dd1 100644
--- a/test/evp_libctx_test.c
+++ b/test/evp_libctx_test.c
@@ -518,6 +518,64 @@ done:
return ret;
}
+/*
+ * This test replicates 1:1 rsasve_gen_recover() changing RSASVE to RSA as the
+ * algorithm name to fetch, and expecting failures instead of successes.
+ *
+ * We want to ensure at this point that a "plain RSA" key does not support KEM
+ * operations (defaulting to RSASVE due to aliasing).
+ *
+ * This is because later on, in a future release, we might want to assign an
+ * explicit default KEM algorithm for "plain RSA" keys, depending on
+ * standardization results, and we shouldn't be held back by the accidental
+ * creation of defaults.
+ */
+static int rsasve_gen_recover_negative(void)
+{
+ int ret = 0;
+ EVP_PKEY *pub = NULL;
+ EVP_PKEY *priv = NULL;
+ EVP_PKEY_CTX *sctx = NULL, *rctx = NULL;
+ unsigned char secret[256] = { 0, };
+ unsigned char ct[256] = { 0, };
+ unsigned char unwrap[256] = { 0, };
+ size_t ctlen = 0, unwraplen = 0, secretlen = 0;
+ const char *alg = "RSA";
+
+ if (!TEST_true(rsa_keygen(alg, 2048, &pub, &priv)))
+ goto done;
+
+ if (!TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(libctx, pub, NULL)))
+ goto done;
+
+ if (!TEST_int_eq(EVP_PKEY_KEM_encapsulate_init(sctx), -2))
+ goto done;
+ if (!TEST_int_lt(EVP_PKEY_KEM_encapsulate(sctx, NULL, &ctlen, NULL,
+ &secretlen), 1))
+ goto done;
+ if (!TEST_int_lt(EVP_PKEY_KEM_encapsulate(sctx, ct, &ctlen, secret,
+ &secretlen), 1))
+ goto done;
+
+ if (!TEST_ptr(rctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL)))
+ goto done;
+ if (!TEST_int_eq(EVP_PKEY_KEM_decapsulate_init(rctx), -2))
+ goto done;
+ if (!TEST_int_lt(EVP_PKEY_KEM_decapsulate(rctx, NULL, &unwraplen,
+ ct, ctlen), 1))
+ goto done;
+ if (!TEST_int_lt(EVP_PKEY_KEM_decapsulate(rctx, unwrap, &unwraplen,
+ ct, ctlen), 1))
+ goto done;
+ ret = 1;
+done:
+ EVP_PKEY_free(pub);
+ EVP_PKEY_free(priv);
+ EVP_PKEY_CTX_free(rctx);
+ EVP_PKEY_CTX_free(sctx);
+ return ret;
+}
+
int setup_tests(void)
{
const char *prov_name = "default";
@@ -573,6 +631,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_cipher_reinit, sk_OPENSSL_CSTRING_num(cipher_names));
ADD_ALL_TESTS(test_cipher_reinit_partialupdate,
sk_OPENSSL_CSTRING_num(cipher_names));
+ ADD_TEST(rsasve_gen_recover_negative);
ADD_TEST(rsasve_gen_recover);
return 1;
}
--
2.27.0 This results in the following failures:
|
Also notice that, without applying the 2 patches above, replacing |
Ok should be reviewable now.. |
rebased again |
@romen - are you planning to review this PR? |
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.
@romen - are you planning to review this PR?
I am! I think all my feedback has been addressed, and yesterday I would have approved on the base of just looking at the incremental changes.
I decided anyway that given how late this featurization came, it warranted an extra fresh review pass, and I did.
My approval stands, and I renew the praise for the amount and quality of work @slontis did (and especially for keeping up with me and my extenuating discussions and requests!)
Still, given I am only partially confident on my knowledge of the "inner Core" and of the gritty and nasty little secrets of the OSSL_LIB_CTX
internals, I would ask @mattcaswell , @levitte or @paulidale to have at least a partial review of this PR w.r.t. the Core stuff.
I know in general @paulidale's review does not officially count for @slontis work, but in this case it's not about the required number of approvals and more about supporting my judgment on the parts I am not entirely confident about.
@romen I hope you are not too upset that I rebased to collapse the commits (effectively removing your commits). |
The rebase did not affect the content - but it did modify the commit message. |
Not at all!
In the past I have used this style to credit co-authors, as it is understood by github and other tools. I am not asking you to change it, it's just a note in case you were not aware of this tag! |
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.
Reapproval after force-push
I think this is a good idea to follow, generally speaking. Perhaps we should make it policy... |
24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually. |
Unfortunately the travis failures look relevant. |
SP800-56Br2 requires support for the RSA primitives for RSASVE generate and recover. As these are simple KEM operations another operation type has been added that can support future extensions. Added public functions EVP_PKEY_encapsulate_init(), EVP_PKEY_encapsulate(), EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate() Added EVP_KEM_* functions. Added OSSL_FUNC_kem_* dispatch functions Added EVP_PKEY_CTX_set_kem_op() so that different types of KEM can be added in the future. This value must currently be set to "RSASVE" after EVP_PKEY_encapsulate_init() & EVP_PKEY_decapsulate_init() as there is no default value. This allows the existing RSA key types, keymanagers, and encoders to be used with the encapsulation operations. The design of the public API's resulted from contributions from @romen & @levitte.
Just a simple make update to regenerate libcrypto.num was required. No other changes were made. |
SP800-56Br2 requires support for the RSA primitives for RSASVE generate and recover. As these are simple KEM operations another operation type has been added that can support future extensions. Added public functions EVP_PKEY_encapsulate_init(), EVP_PKEY_encapsulate(), EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate() Added EVP_KEM_* functions. Added OSSL_FUNC_kem_* dispatch functions Added EVP_PKEY_CTX_set_kem_op() so that different types of KEM can be added in the future. This value must currently be set to "RSASVE" after EVP_PKEY_encapsulate_init() & EVP_PKEY_decapsulate_init() as there is no default value. This allows the existing RSA key types, keymanagers, and encoders to be used with the encapsulation operations. The design of the public API's resulted from contributions from @romen & @levitte. Reviewed-by: Nicola Tuveri <[email protected]> Reviewed-by: Matt Caswell <[email protected]> (Merged from #12750)
Thanks... Merged to master. |
SP800-56B contains a variation of rsa encryption that needs to be inside the provider boundary..
i.e. generate a random secret and then encrypt it using rsa..
Checklist