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

Fix BIKE constant-time errors #1632

Merged
merged 5 commits into from
Jan 2, 2024
Merged

Fix BIKE constant-time errors #1632

merged 5 commits into from
Jan 2, 2024

Conversation

SWilson4
Copy link
Member

@SWilson4 SWilson4 commented Dec 18, 2023

I have reviewed the BIKE constant-time test failures and identified the offending lines of code. They are summarized below.

mask = (-1ULL) + (!secure_cmp32(pos_qw[j], i));

This appears to be triggered by the array access pos_qw[j]. After reviewing the function's code, I believe this to be a false positive and have documented it as a "pass". There's an existing pass for the AVX2 version of this function.

bike_static_assert(bits < 64, rotr_small_err);

The issue here seems to be the "<" operator. I changed this to !(bits >> 6) and it now passes (on my machine).

l = u[LSB3(a0)] ^ (u[LSB3(a0 >> 3)] << 3);

h = (u[LSB3(a0 >> 3)] >> 61);

These array accesses depend on the value of a0, which is one of the function arguments. Hence, I believe this to be (possible) secret-dependent behaviour. I added short loops to perform the array access in a secret-independent fashion, as is done in HQC.

After these changes, the constant-time tests pass for all variants of BIKE on my machine.

Fixes #1624.

  • Does this PR change the input/output behaviour of a cryptographic algorithm (i.e., does it change known answer test values)? (If so, a version bump will be required from x.y.z to x.(y+1).0.)
  • Does this PR change the list of algorithms available -- either adding, removing, or renaming? Does this PR otherwise change an API? (If so, PRs in fully supported downstream projects dependent on these, i.e., oqs-provider and OQS-OpenSSH will also need to be ready for review and merge by the time this is merged.)

@SWilson4 SWilson4 marked this pull request as draft December 18, 2023 17:18
@SWilson4
Copy link
Member Author

Following up on discussion in today's OQS meeting: I am running into a snag when attempting to resolve the constant-time error raised for this line of code:

bike_static_assert(bits < 64, rotr_small_err);

My initial thought was that the error was raised because of the use of "<" operator for comparison. My "quick fix" of using a bitshift to check the bits < 64 condition satisfied the Valgrind-powered constant-time tests but caused the Clang static analyzer to fail (see here). Presumably, the static analyzer doesn't recognize that !(bits >> 6) and bits < 64 are equivalent and is reporting an error because the assert statement may fail. I'm not sure how to resolve this in a way that will satisfy both the static analyzer and the constant-time tests. I'm also not certain that the "<" operator was causing the test failure in the first place.

If somebody more familiar with the inner workings of BIKE could take a look and offer input on

  1. whether or not this constitutes secret-dependent behaviour
  2. if yes, how to resolve it in a way that keeps the static analyzer happy.

Feedback on the other constant-time errors (and my proposed fixes) would also be very welcome!

@SWilson4
Copy link
Member Author

Tagging @crockeea who kindly offered to pass this along to somebody who knows about BIKE.

@SWilson4 SWilson4 added the help wanted Asking for support from non-core team label Dec 19, 2023
for (size_t i = 0; i < 8; ++i) {
// use a mask for secret-independent memory access
mask = LSB3(a0) - i;
l ^= (u[i] & (uint64_t)(0 - (1 - ((uint64_t)(mask | (0 - mask)) >> 63))));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can extract the mask computation to a function in utilities.h, something like:

// Returns 0xfff..ff if a == b, otherwise returns 0.
uint64_t secure_cmpeq64_mask(uint64_t a, uint64_t b) {
  uint64_t tmp = a - b;
  return (uint64_t)(0 - (1 - ((uint64_t)(tmp | (0 - tmp)) >> 63))));
}

and then use it here and in other places below like this:

l ^= u[i] & secure_cmpeq64_mask(LSB3(a0), i);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@dkostic
Copy link
Contributor

dkostic commented Dec 19, 2023

  1. whether or not this constitutes secret-dependent behaviour

It would constitute secret-dependent behaviour if the condition could ever fail. however, we are calling the function here: https://github.com/open-quantum-safe/liboqs/blob/main/src/kem/bike/additional_r4/decode_portable.c#L62 where we explicitly reduce the input mod 64.

  1. if yes, how to resolve it in a way that keeps the static analyzer happy.

we should just remove the assert, it's pointless because we reduce the argument mod 64 before calling the function.

@SWilson4
Copy link
Member Author

Thanks very much for the quick review @dkostic. I've made your suggested changes and confirmed that the constant-time tests now pass for BIKE on my machine (the CI run is a weekly scheduled job targeting main).

Would it be helpful if I submit a corresponding PR to the awslabs/bike-kem repo?

@dkostic
Copy link
Contributor

dkostic commented Dec 20, 2023

Thanks very much for the quick review @dkostic. I've made your suggested changes and confirmed that the constant-time tests now pass for BIKE on my machine (the CI run is a weekly scheduled job targeting main).

Would it be helpful if I submit a corresponding PR to the awslabs/bike-kem repo?

That'd be awesome and helpful!

@SWilson4 SWilson4 marked this pull request as ready for review December 21, 2023 00:21
@SWilson4 SWilson4 requested a review from baentsch December 21, 2023 15:16
dkostic pushed a commit to awslabs/bike-kem that referenced this pull request Dec 21, 2023
)

This PR addresses two issues which were caught by liboqs automated constant-time testing. One of these involved a number of secret-dependent array accesses in gf2x_mul_base_portable. This can be fixed by putting the array accesses inside short loops and using a mask. The second involved an unnecessary static_assert statement; the fix was to remove it.

See also the analogous liboqs PR: open-quantum-safe/liboqs#1632
@SWilson4
Copy link
Member Author

This has now been fixed upstream as well: awslabs/bike-kem@96aeca0

Copy link
Member

@baentsch baentsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing up- and downstream in sync (in a somewhat unusual order, but the result counts :).

@SWilson4 SWilson4 merged commit acac4e9 into main Jan 2, 2024
41 checks passed
trigpolynom pushed a commit to BytesLogik/liboqs that referenced this pull request Jan 13, 2024
* Document BIKE CT issues

* Document / fix BIKE constant-time errors

* Revert "< 64" comparison change

* Add and use secure_cmpeq64_mask function

* Remove unnecessary static_assert
@SWilson4 SWilson4 deleted the sw-bike-ct branch January 18, 2024 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Asking for support from non-core team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BIKE constant time tests failing
3 participants