-
Notifications
You must be signed in to change notification settings - Fork 96
Make psa_close_key(0) and psa_destroy_key(0) succeed #292
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
Make psa_close_key(0) and psa_destroy_key(0) succeed #292
Conversation
Document that passing 0 to a close/destroy function does nothing and returns PSA_SUCCESS. Although this was not written explicitly, the specification strongly suggested that this would return PSA_ERROR_INVALID_HANDLE. While returning INVALID_HANDLE makes sense, it was awkward for a very common programming style where applications can store 0 in a handle variable to indicate that the handle has been closed or has never been open: applications had to either check if (handle != 0) before calling psa_close_key(handle) or psa_destroy_key(handle), or ignore errors from the close/destroy function. Now applications following this style can just call psa_close_key(handle) or psa_destroy_key(handle).
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.
This looks good 👍
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
TEST_EQUAL( psa_close_key( handle1 - 1 ), PSA_ERROR_INVALID_HANDLE ); | ||
TEST_EQUAL( psa_destroy_key( handle1 - 1 ), PSA_ERROR_INVALID_HANDLE ); | ||
} | ||
if( handle1 + 1 != 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.
When would handle1 + 1 be equal to zero? I don't think psa_import_key()
would ever return -1 for a handle, would it? Certainly not in this test. What are we trying to test with this and how can we guarantee we test 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.
It won't happen with our implementation (handle1
is deterministically PSA_KEY_SLOT_COUNT
), but (psa_key_handle_t)(-1)
is a potentially valid handle.
The goal of this test is to close a nonzero, invalid handle and check that this seems to keep the key store in a good state. We check this by checking that handle1
is still there and that the shutdown is clean.
/* 0 is special: it isn't a valid handle, but close/destroy | ||
* succeeds on it. */ | ||
TEST_EQUAL( psa_close_key( 0 ), PSA_SUCCESS ); | ||
TEST_EQUAL( psa_destroy_key( 0 ), PSA_SUCCESS ); |
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.
Do we need these checks here? They don't use handle1 at all. We get equivalent coverage from the close_invalid() and destroy_invalid() tests (because they are called with handle 0), don't we?
psa_destroy_key(0)
destroy_invalid:0:PSA_SUCCESS
psa_close_key(0)
close_invalid:0:PSA_SUCCESS
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.
Yes, there's some redundancy between this test case and the xxx_invalid
ones in psa_crypto.function
. I don't think this redundancy is harmful so I didn't bother to remove 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.
Redundancy was added by this PR. Redundancy is harmful to maintainability, as there are more places to update if behavior changes.
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.
No, the redundancy was there before, and I kept it to avoid scope creep. But ok, I'll fix 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've consolidated the two test functions.
TEST_EQUAL( psa_destroy_key( handle1 + 1 ), PSA_ERROR_INVALID_HANDLE ); | ||
if( handle1 - 1 != 0 ) | ||
{ | ||
TEST_EQUAL( psa_close_key( handle1 - 1 ), PSA_ERROR_INVALID_HANDLE ); |
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.
Do we ever get inside this block? As currently written, doesn't psa_import_key()
always return 1 as the handle? The determinism of our current handle allocation scheme may be at odds with what we are aiming to test here. We may need to call psa_import_key or psa_generate_key multiple times to get the coverage we are looking for, or depend entirely on the destroy_invalid() and close_invalid() tests.
We could add the "Allocate a handle and store a key in it" and "check that the original handle is intact" bits to the destroy_invalid() and close_invalid() tests in order to more directly test what we want, or add a handle parameter to this test and rename it from invalid_handle()
to something like test_invalid_handles_dont_invalidate_valid_handle()
...
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 currently allocate handles from the top, so handle1 - 1
is nonzero. But that's an implementation detail. There are many other possible ways to write this test, but I think the current way is good enough.
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 had read this as allocating from the bottom. If we switch to allocating from the bottom, we could easily end up not testing what we want to test anymore with these tests. Could you please change the if blocks to asserts and comment about this dependency? Then at least we won't silently reduce our test coverage if the assumptions the test was built on change.
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.
So I made this change to not try both valid+1 and valid-1, but now that I look back I don't understand why you requested this. Before, the tests covered both allocating from the top and allocating from the bottom, without making any assumption about the range. Now, the tests hard-code that the range starts at 0. Why do you object to making the test more robust against implementation details?
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.
Exactly the opposite. I would like the test to be robust against implementation details. The test as written had if statements which if not executed, we wouldn't notice. Replacing them with asserts will ensure we don't accidentally reduce test coverage.
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.
What would you assert on? I don't understand why you want to make the tests fail on certain changes in implementation details. The tests as written before were robust to implementation changes: there would have been no loss of coverage if the handle allocation mechanism had changed.
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 tests as written before were robust to implementation changes
I don't believe that was the case.
/* 0 is special: it isn't a valid handle, but close/destroy | ||
* succeeds on it. */ | ||
TEST_EQUAL( psa_close_key( 0 ), PSA_SUCCESS ); | ||
TEST_EQUAL( psa_destroy_key( 0 ), PSA_SUCCESS ); |
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.
Redundancy was added by this PR. Redundancy is harmful to maintainability, as there are more places to update if behavior changes.
TEST_EQUAL( psa_destroy_key( handle1 + 1 ), PSA_ERROR_INVALID_HANDLE ); | ||
if( handle1 - 1 != 0 ) | ||
{ | ||
TEST_EQUAL( psa_close_key( handle1 - 1 ), PSA_ERROR_INVALID_HANDLE ); |
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 had read this as allocating from the bottom. If we switch to allocating from the bottom, we could easily end up not testing what we want to test anymore with these tests. Could you please change the if blocks to asserts and comment about this dependency? Then at least we won't silently reduce our test coverage if the assumptions the test was built on change.
Consolidate the invalid-handle tests from test_suite_psa_crypto and test_suite_psa_crypto_slot_management. Start with the code in test_suite_psa_crypto_slot_management and adapt it to test one invalid handle value per run of the test function.
if( valid_handle == 1 ) | ||
invalid_handle = 2; | ||
else | ||
invalid_handle = valid_handle - 1; |
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.
Do we sometimes want to test invalid_handle = valid_handle + 1
? We may need a INVALID_HANDLE_UNOPENED_ABOVE
for this, renaming the - 1
case to INVALID_HANDLE_UNOPENED_BELOW
.
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 it would be better to test both, but you objected to that.
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 objected to the tests silently only testing one way without letting us know we reduced test coverage.
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.
So it's a misunderstanding then? The tests were testing both ways, skipping one of the ways if it was not applicable.
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 didn't like skipping if one wasn't applicable, as both might be silently skipped. I'd like to always see both ways tested (above and below). If this means we have to allocate multiple keys, we should do that.
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.
No, it was not possible for both to be silently skipped. handle1 - 1 != 0
and handle1 + 1 != 0
can't both be false.
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
CI only failed on ATECC jobs, which is a known unrelated issue. |
* ARMmbed#292: Make psa_close_key(0) and psa_destroy_key(0) succeed * ARMmbed#299: Allow xxx_drbg_set_entropy_len before xxx_drbg_seed * ARMmbed#259: Check `len` against buffers size upper bound in PSA tests * ARMmbed#288: Add ECDSA tests with hash and key of different lengths * ARMmbed#305: CTR_DRBG: grab a nonce from the entropy source if needed * ARMmbed#316: Stop transactions from being reentrant * ARMmbed#317: getting_started: Make it clear that keys are passed in * ARMmbed#314: Fix pk_write with EC key to use a constant size for the private value * ARMmbed#298: Test a build without any asymmetric cryptography * ARMmbed#284: Fix some possibly-undefined variable warnings * ARMmbed#315: Define MBEDTLS_PK_SIGNATURE_MAX_SIZE * ARMmbed#318: Finish side-porting commits from mbedtls-restricted that missed the split
Although this was not written explicitly, the specification strongly suggested that this would return
PSA_ERROR_INVALID_HANDLE
. While returningINVALID_HANDLE
makes sense, it was awkward for a very common programming style where applications can store 0 in a handle variable to indicate that the handle has been closed or has never been open: applications had to either check if(handle != 0)
before callingpsa_close_key(handle)
orpsa_destroy_key(handle)
, or ignore errors from the close/destroy function. Now applications following this style can just callpsa_close_key(handle)
orpsa_destroy_key(handle)
.