-
Notifications
You must be signed in to change notification settings - Fork 4
[WIP] rfc: replace subtle with ctutils
#3
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
Draft
tarcieri
wants to merge
1
commit into
zkcrypto:main
Choose a base branch
from
tarcieri:tarcieri/replace-subtle-with-ctutils
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,149 @@ | ||||||
| - Feature Name: `replace_subtle_with_ctutils` | ||||||
| - Start Date: 2026-04-07 | ||||||
| - RFC PR: TBD | ||||||
| - Tracking Issue: TBD | ||||||
|
|
||||||
| # Summary | ||||||
| [summary]: #summary | ||||||
|
|
||||||
| This proposal is to replace the unmaintained `subtle` dependency which provides an assortment of | ||||||
| constant-time operations with the `ctutils` crate which has been developed | ||||||
| [under the RustCrypto organization](https://github.com/RustCrypto/utils/tree/master/ctutils). | ||||||
|
|
||||||
| # Motivation | ||||||
| [motivation]: #motivation | ||||||
|
|
||||||
| Though `subtle` has largely served the Rust ecosystem well, its maintenance status has become | ||||||
| problematic, with a [large number of open PRs](https://github.com/dalek-cryptography/subtle/pulls) | ||||||
| and no one to review them. This leaves `subtle` in a state where if there are problems it may be | ||||||
| difficult to address them, and it won't be able to take advantage of | ||||||
| [new Rust features](https://blog.trailofbits.com/2025/12/02/introducing-constant-time-support-for-llvm-to-protect-cryptographic-code/). | ||||||
|
|
||||||
| The strategy it implements to impede optimizations, an off-label usage of `ptr::read_volatile`, was | ||||||
| once the best option available on stable Rust. However, Rust itself has moved on, adding both stable | ||||||
| inline assembly which can be used to guarantee operations will not be removed by the compiler or | ||||||
| rewritten with branches, as well as `const fn` which enables functions that can potentially run at | ||||||
| compile-time, which is useful for calculating constants at compile-time. As an example use case, | ||||||
| the RustCrypto `primefield` crate | ||||||
| [uses `const fn` to compute the constants for `ff::PrimeField` at compile time](https://github.com/RustCrypto/elliptic-curves/blob/0732c4a/primefield/src/monty.rs#L452-L481), | ||||||
| effectively providing similar functionality to `ff-derive` without the use of a proc macro. | ||||||
|
|
||||||
| `ctutils` got its start as an extraction of the RustCrypto `crypto-bigint` crate, where we were | ||||||
| using `subtle` but effectively built an entirely parallel set of `ConstChoice` and `ConstCtOption` | ||||||
| types which we used to implement the core of algorithms as `const fn`. | ||||||
|
|
||||||
| It's built on the RustCrypto [`cmov` crate](https://github.com/RustCrypto/utils/tree/master/cmov) | ||||||
| which uses `asm!` to either invoke specific predication intrinsics (CMOV family on x86, CSEL on ARM) | ||||||
| on supported platforms, or otherwise falls back to a bitwise masking-based implementation similar to | ||||||
| `subtle`, but further augmented on certain platforms with `asm!` mask generation which both avoids | ||||||
| compiler optimizations (some of which have previously actually inserted branches) and for improving | ||||||
| performance. | ||||||
|
|
||||||
| `ctutils` also removes a number of problematic bounds imposed by `subtle` like `Copy` and `Default` | ||||||
| which meant its traits couldn't be used with heap-allocated types, which we needed to implement | ||||||
| crates like `rsa`, `dsa`, and `srp`. | ||||||
|
|
||||||
| We recently fully migrated `crypto-bigint` into a state where `ctutils` natively provided the | ||||||
| `Choice` and `CtOption` type used throughout the library, but it still optionally impls relevant | ||||||
| `subtle` traits. `ctutils` itself also features `subtle` interop for enabling incremental migrations | ||||||
| which allows the `Choice` and `CtOption` types to be bidirectionally converted between the two | ||||||
| libraries. Our `elliptic-curve` crate has leveraged this to use `crypto-bigint` internally while | ||||||
| still using `subtle` in its public API. However, that still leaves us in a state where we have two | ||||||
| constant-time libraries, and `subtle`-based APIs can't be used in `const fn`. | ||||||
|
|
||||||
| The current plan is to [migrate all of the RustCrypto crates to `ctutils`](https://github.com/RustCrypto/traits/issues/2275) | ||||||
| so it will likely already become a dependency to other projects as some point. We have PRs open to | ||||||
| give a similar treatment to all of our elliptic curve crates that we did to `crypto-bigint`: | ||||||
| natively migrating to `ctutils` but continuing to support `subtle` trait impls as an optional | ||||||
| dependency. The one place where we still continue to have `subtle` in our public API though is | ||||||
| through the `ff` and `group` traits, particularly ones which return `subtle::Choice` or | ||||||
| `subtle::CtOption`, e.g. `Field::is_zero`, `Field::invert`. | ||||||
|
|
||||||
| And so, this proposal is to migrate `ff` and `group` to use `ctutils` instead of `subtle`. Like in | ||||||
| the RustCrypto crates, `subtle` support could be optionally retained to some degree, but with | ||||||
| the aforementioned APIs migrated to use `ctutils::Choice` and `ctutils::CtOption`. | ||||||
|
|
||||||
| # Guide-level explanation | ||||||
| [guide-level-explanation]: #guide-level-explanation | ||||||
|
|
||||||
| `ctutils` as a constant-time library is largely `subtle`-shaped and should be fairly intuitive to | ||||||
| anyone who has previously used `subtle`. Its API primarily consists of: | ||||||
|
|
||||||
| - `Choice`: substitute for `bool` which internally wraps a `u8`. Accessing its inner value uses | ||||||
| `hint::black_box` which (despite some scary language in its docs) should be at least as good or | ||||||
| better than the `ptr::read_volatile` approach used by `subtle` (though `subtle` offers optional | ||||||
| `black_box` support). Note that `ctutils` uses `black_box` in a belt-and-suspenders manner, with | ||||||
| the real security provided by `cmov`. `ctutils::Choice` also offers a number of `const fn` | ||||||
| constructors which can perform various constant-time operations using bitwise masking. | ||||||
| - `CtOption`: substitute for `Option` which evaluates its combinators eagerly and unconditionally | ||||||
| rather than lazily. Unlike `subtle`, `ctutils::CtOption` can be used with non-`Copy` types. | ||||||
| - Traits for constant-time operations: | ||||||
| - `CtAssign`: conditional constant-time assignment, equivalent to | ||||||
| `subtle::ConditionallySelectable::conditional_assign`, but split out to permit impls on | ||||||
| DSTs that can't otherwise be returned from a select operation. | ||||||
| - `CtEq`: constant-time equality testing, equivalent to `subtle::ConstantTimeEq` | ||||||
| - `CtGt`/`CtLt`: constant-time greater/less, equivalent to `subtle::ConstantTimeGreater` and | ||||||
| `subtle::ConstantTimeLess` | ||||||
| - `CtNeg`: constant-time conditional negation, equivalent to `subtle::ConditionallyNegatable` | ||||||
| - `CtSelect`: constant-time selection, equivalent to `subtle::ConditionallySelectable` sans the | ||||||
| `conditional_assign` method. | ||||||
|
|
||||||
| ## Migration | ||||||
|
|
||||||
| Migrating from `subtle` to `ctutils` is a largely mechanical operation that can be done through a | ||||||
| series of find-and-replace operations, as documented in the | ||||||
| [`subtle` migration guide for `ctutils`](https://docs.rs/ctutils/latest/ctutils/#subtle-migration-guide). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (or whatever version we end up adopting, if this is where the RFC will point people at) |
||||||
|
|
||||||
| # Reference-level explanation | ||||||
| [reference-level-explanation]: #reference-level-explanation | ||||||
|
|
||||||
| TODO | ||||||
|
|
||||||
| # Drawbacks | ||||||
| [drawbacks]: #drawbacks | ||||||
|
|
||||||
| The Rust cryptography ecosystem has largely aligned on `subtle`. Switching to `ctutils` has the | ||||||
| unfortunate effect of splitting the ecosystem. While the interop support provides a workaround to | ||||||
| bridge the two ecosystems, it's certainly not as nice as having a single crate everyone uses. | ||||||
|
|
||||||
| # Rationale and alternatives | ||||||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||||||
|
|
||||||
| The main alternative is remaining on `subtle`, either in its current 2.x form, or trying to bring | ||||||
| the features and functionality of `ctutils` to `subtle` in the form of non-breaking or breaking | ||||||
| changes. | ||||||
|
|
||||||
| The number of breaking changes to `subtle` that would be required to have equivalent functionality | ||||||
| to `ctutils` is incredibly small and likely would have little to no impact. These have been proposed | ||||||
| in a number of issues and pull requests, which I'll deliberately list in reverse order here as | ||||||
| the most recent are probably the most relevant: | ||||||
|
|
||||||
| - [#148: Proposed breaking changes for a subtle v3.0.0](https://github.com/dalek-cryptography/subtle/issues/148) | ||||||
| - [#137: Change `ConditionallySelectable` supertrait from `Copy` to `Sized`](https://github.com/dalek-cryptography/subtle/pull/137) | ||||||
| - [#136: WIP breaking changes for a hypothetical v3.0](https://github.com/dalek-cryptography/subtle/pull/136) | ||||||
| - [#134: `const fn` support](https://github.com/dalek-cryptography/subtle/issues/134) | ||||||
|
|
||||||
| These are just some of the issues and PRs to `subtle` that I have opened. Unfortunately with no | ||||||
| reviewers these and PRs by others have gone unreviewed. | ||||||
|
|
||||||
| If it were possible to find reviewers for `subtle` and get it to a state where it has the major | ||||||
| functionality we're looking for from `ctutils`, making a small number of breaking changes to support | ||||||
| heap-allocated types, that would probably be the best outcome. Though we have already started | ||||||
| releasing RustCrypto crates that use `ctutils`, including `crypto-bigint`, if `subtle` could be | ||||||
| updated we could probably find a way to make it work, though as we release more and more crates it | ||||||
| becomes harder and harder. | ||||||
|
|
||||||
| # Prior art | ||||||
| [prior-art]: #prior-art | ||||||
|
|
||||||
| `subtle` is, of course, the prior art here. | ||||||
|
|
||||||
| # Unresolved questions | ||||||
| [unresolved-questions]: #unresolved-questions | ||||||
|
|
||||||
| TODO | ||||||
|
|
||||||
| # Future possibilities | ||||||
| [future-possibilities]: #future-possibilities | ||||||
|
|
||||||
| TODO | ||||||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 the other
subtleinterop things are worth mentioning explicitly (the bidirectionalFromimpls) since I think these may become useful for some library crates during this coordinated transition.