Skip to content

WIP: Use constant time by default#5969

Closed
mattcaswell wants to merge 8 commits intoopenssl:masterfrom
mattcaswell:safe-bn
Closed

WIP: Use constant time by default#5969
mattcaswell wants to merge 8 commits intoopenssl:masterfrom
mattcaswell:safe-bn

Conversation

@mattcaswell
Copy link
Member

The BIGNUM library defaults to a non-constant time implementation of various operations. Where we want to force a constant time implementation we have to remember to set the BN_FLG_CONSTTIME flag. Experience has shown that we often forget to do this. For example see:

This is not intended to be an exhaustive list - there are probably others cases that I haven't listed.

The difficulty of changing the default is that this will have a performance impact for cases where constant time is not a requirement. It is unclear to me how many of those cases there are are and whether we should be worried about it or not.

This PR switches the default so that we always use a constant time implementation unless data is specifically marked as public. It also attempts to track what data is private and what data is public. For example if an operation takes two inputs and has one output (e.g. BN_add()) then we mark the output as public if both inputs are also public. Otherwise we mark the output as private. When its gets to a decision as to whether to take a constant time path or not, if any input is marked as private then the constant time path is taken. BIGNUMs are created private by default. This PR adds a number of new functions for creating public BIGNUMs instead and uses them throughout libcrypto where applicable.

This is WIP because there are no tests or documentation as yet. Before I go any further with this I'd like feedback on the approach and whether this is something we should pursue now (i.e. for 1.1.1) or later (post 1.1.1) or not at all.

@briansmith
Copy link
Contributor

See https://github.com/HACS-workshop/spectre-mitigations/blob/master/crypto_guidelines.md#1-do-not-conditionally-choose-between-constant-and-non-constant-time. I'm biased, but I think BoringSSL's solution, which was to use separate APIs for constant-time and non-constant-time implementations, is better, in general.

@davidben
Copy link
Contributor

Agreed, though I suppose that too is unsurprising. :-)

Echoing what I wrote on openssl-project:

In a crypto library, something like "modular exponentiation" is not a complete API contract. Rather, you need contracts like "modular exponentiation with secret fully-reduced base and public exponent" or "modular exponentiation with secret fully-reduced base and secret exponent publicly bounded by such-and-such". Cast in that light, I think it becomes much clearer that they should be separate functions. You could even imagine embedding secrecy into the type (either for safety or to take advantage of some future compiler annotations), in which case separate functions is necessary.

This PR instead makes things even further dependent on bit propagation, which was a large part of why the old scheme was error-prone and difficult to analyze. While the flipped default probably helps a bit, most BIGNUM functions do not allocate their results, so things are still ultimately dependent on bit propagation correctly overwriting whatever was in the result BIGNUM originally.

Separate functions, in contrast, place the algorithm decision right at the call site, where one typically thinks about it. It's much easier to reason about what code runs where, and whether the right code runs.

@paulidale
Copy link
Contributor

Separate functions feels better to me as well. Trying to track public vs private automatically is a noble ideal but it relies on the programmer setting things correctly every time. This PR will be easier/safer than having to remember to set and clear the constant time flag but the risk seems high still. Separate functions has this risk too but it is more in the face -- someone looking at the documentation will see both versions immediately rather than having to remember to look at this other page of documentation, which they mightn't even know exists, for the public/private differentiator. I'd advocate adding a suffix _public (for want of a better name) to the non-constant time flavours and making the shorter/existing names constant time. This means a performance hit for people who call BN directly, at least until they amend their code, but it shouldn't break anything.

A separate item along similar lines would be making the small memory model builds only have the constant time flavour of these routines.

@mattcaswell
Copy link
Member Author

Thanks for the link and other feedback. A separate API does seem like a good idea. I'm going to leave this PR open for a while longer to see if there is any other feedback.

@randombit
Copy link
Contributor

I agree with above commentators that it is better to have distinct API endpoints for what are in fact distinct operations.

Bikeshedding here, but I'd say rather than _public - which may be easily confusing to an end user (ie, does _public mean it's the public API function and the non-_public is internal?) - use _vartime as is done in the Ed25519/X25519 code. Already public appears in many contexts within OpenSSL source, where vartime does not, so it would more easily catch the eye of a reviewer.

@mattcaswell
Copy link
Member Author

I opened #6078 to track this issue, so I'm going to close this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants