-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
PK: refactor wrappers in the USE_PSA case #7814
Conversation
Normally yes, but in this case we'll defer it so we can add a single entry later for all the changes to opaque keys. |
@mpg I opened the PR yesterday mostly to:
That's why it's not labeled "need-review" yet ;) |
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.
Just a first general look. Looking pretty good. Noticed a minor behaviour change we could do (as we never documented otherwise) that would make things more uniform and allow further code sharing.
Also, we should have a consistent naming scheme. I'm note sure yet what exactly it should be, but let's brainstorm it. Initial proposal to get the ball rolling:
type_operation_legacy
(for exampleecdsa_sign_legacy
) -> the full wrapper for the non-USE_PSA
casetype_operation_use_psa
-> the core function for theUSE_PSA
casetype_operation_use_psa_from_psa
-> the wrapper for opaque keys and theUSE_PSA_EC_DATA
casestype_operation_use_psa_from_legacy
-> the wrapper for the!USE_PSA_EC_DATA
case (and for RSA in all cases)
Then perhaps something like #define type_operation_wrapper type_operation_xxx
, probably close to the function's definition, for names we can use in the info structures? (Just a suggestion.)
library/pk_wrap.c
Outdated
* that: | ||
* - opaque keys are available as long as USE_PSA_CRYPTO is defined and even | ||
* if !PK_USE_PSA_EC_DATA | ||
* - opaque keys do not support PSA_ALG_DETERMINISTIC_ECDSA() */ |
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.
Thanks for taking care not to change the defaults. However in this case I think the difference was not intentional, so we can actually merge the two functions and prefer deterministic when available. (For now: we'll change again in #7715 but it will be simpler if there's a single place to change - I think this could go in ecdsa_sign_psa()
above once all three cases are aligned, by making it take an mbedtls_md_type_t
param instead of psa_algorigthm_t
- which I agree made perfect sense considering the constraints.)
I don't think the guard is an issue: we can define ecdsa_sign_wrap_psa_held
unconditionally, then ecdsa_sign_wrap_ecp_keypair
only when !PK_USE_PSA_EC_DATA
. Then in the non-opaque info structure we use one or the other depending on PK_USE_PSA_EC_DATA
. Or just define a function pointer externally to avoid cluttering the info structure, whichever is more readable.
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.
we can actually merge the two functions and prefer deterministic when available
I have a question here: when wrapping into opaque key, the alg
is given as parameter to mbedtls_pk_wrap_as_opaque
. There are test functions, like pk_psa_sign()
, that only set alg_psa = PSA_ALG_ECDSA(PSA_ALG_SHA_256)
and not the deterministic one. What should I do in these cases?
- add the option to set deterministic ECDSA? or
- add the guard to skip this test?
I'm asking because at the beginning I didn't notice the fact that opaque keys didn't support deterministic ECDSA and there were tests failing. That's why I come up with introducing separate function and comments.
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.
Following our discussion on Slack, it turned out that actually opaque keys cannot be easily changed to use deterministic ECDSA, because not only the key is owned externally, but also its properties. So if the application requires ALG_ECDSA
and we set ALG_ECDSA_DETERMINISTIC
the operations will be rejected.
This means that a proper wrapper for opaque keys will be added to the list proposed here
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.
Actually, the right thing to do in the opaque wrapper would be to look which of ALG_ECDSA
or ALG_ECDSA_DETERMINISTIC
is supported by the key and pick that one. (Currently we just assume that the key allows ALG_ECDSA
which is an arbitrary and undocumented restriction.)
It's out of the original scope of this PR, but it seems like small addition, so perhaps that's something we can do as part of this clean-up. Otherwise I'll create a separate issue to track it.
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.
Oh right, I didn't thought about it, thanks! No need for another PR, let's do it in this one
library/pk_wrap.c
Outdated
psa_algorithm_t psa_sig_md = | ||
PSA_ALG_ECDSA(mbedtls_md_psa_alg_from_type(md_alg)); | ||
|
||
if (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(pk->priv_id) == PSA_KEY_ID_NULL) { |
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.
Pre-existing: this seems redundant: surely psa_sign_hash()
will check this for us and return an error, and then hopefully PSA_PK_ECDSA_TO_MBEDTLS_ERR()
will translate that to something sensible.
library/pk_wrap.c
Outdated
mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; | ||
psa_status_t status; | ||
((void) f_rng); | ||
((void) p_rng); | ||
#if defined(MBEDTLS_ECDSA_DETERMINISTIC) |
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.
Pre-existing: this should be PSA_WANT_ALG_DETERMINISTIC_ECDSA
really.
NULL, | ||
#endif | ||
rsa_debug, | ||
.type = MBEDTLS_PK_RSA, |
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.
Oh yes, thank you! :)
However, shouldn't we still have
#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
.verify_rs_func = NULL,
.sign_rs_func = NULL,
#endif
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.
maybe I'm missing something here, but it seems to me that the original code only had
#if defined()
#endif
So those functions should always be NULL
and we don't necessarily need to fill all the fields. Am I wrong?
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.
According to the standard you're correct, but I was worried that perhaps some compilers would complain if we don't explicitly initialize all the fields.
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.
Well in this case can we wait for the CI feedback before going back to fill all the fields? I like the new "shape" since it looks much clearer to me and also avoid declaring things we're not going to use.
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.
Clang does, in fact, complain about a_struct_with_two_fields x = {0}
. That's why we have all these PSA_xxx_INIT
macros instead of just telling people to write {0}
.
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.
Clang does, in fact, complain about
a_struct_with_two_fields x = {0}
. That's why we have all thesePSA_xxx_INIT
macros instead of just telling people to write{0}
.
And I think that's also why C23 introduced {}
.
That said, apparently the warnings are not always the same in the presence of designated initializers.
Well in this case can we wait for the CI feedback before going back to fill all the fields? I like the new "shape" since it looks much clearer to me and also avoid declaring things we're not going to use.
I'm divided. On one hand, I was going to suggest the same myself: as long as the CI is happy we're happy. OTOH, we only have a relatively small selection of compilers on our CI (GCC, Clang, a few versions of MSVC, not even IAR), so who knows that other compilers are doing? (On the other other hand, we could wait and see if people report issues with the compiler they're using. But then again, isn't it better to try and prevent that from happening?)
I would prefer to keep the same wrappers' name functions for all the cases instead of defining several functions and then having a macro to redefine symbols. There are a couple of macros in my current implementation, but only to save some code size (i.e not having a function to call another one without doing anything useful). For the record I also tried to redefine some |
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.
Looking pretty good to me, just one question left, and then I want to make a final pass to be sure.
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
This commit also add tests to verify the functionality Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
I have some questions for reviewers:
|
Ah, good question. On one hand, I agree that grouping by guards would reduce the total number of I had a look at the file, and I think I like the way it's organized now. For each operation, we have the same logical order:
I think that still keep things logically grouped by guard within each operation. Let's see what the review think, it's probably partially a matter of taste, but I like it the way it is now.
No, I don't think that makes sense, or would be particularly useful to anyone, so let's save our time and not do it. (Note: if we did it, we'd have to document it (explaining all the limitations) and test it too.) |
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
return PSA_PK_ECDSA_TO_MBEDTLS_ERR(status); | ||
} | ||
|
||
return ecdsa_verify_psa(key, key_len, curve, curve_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.
I know that it's just a public key, but don't we want to clear the key buffer here?
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 think that not too long ago we had a similar situation in an old PR and together with @mpg we agreed that it was not an issue if the buffer holding the public key was not cleared on exit. Basically with only this information you cannot derive any private secret, right? And at the same time the public key is something publicly available by definition so no new information is leaked here in IMO.
@mpg wdyt?
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.
That's my recollection as well, we don't need to wipe buffers that only contained public keys.
return ret; | ||
} | ||
|
||
return ecdsa_verify_psa(key, key_len, curve, curve_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.
Same question about clearing a key buffer here.
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.
Ok, I'll keep note of this as well, but let's wait for the feedback on the previous comment before taking any action here ;)
psa_status_t status; | ||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; | ||
uint8_t prv_key_buf[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH]; | ||
size_t prv_key_len; |
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.
prv_key_buf, which is now internal for this function, is not cleared on failure.
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.
Oh, this is really bad indeed. Thanks for catching it!
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 left some questions. The grouping of functions is fine the way it is now :)
…pair_psa Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
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.
LGTM, thanks!
This PR tries:
pk_wrap.c
in order to make it clearerResolves #7746
PR checklist