Skip to content

Commit 1699aa4

Browse files
committed
Merge branch 'release/2.2.3'
2 parents 5f901f8 + 233bac3 commit 1699aa4

File tree

4 files changed

+58
-55
lines changed

4 files changed

+58
-55
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
Entries are listed in reverse chronological order.
44

5+
## 2.2.3
6+
7+
* Remove the `nightly`-only asm-based `black_box` barrier in favor of the
8+
volatile-based one, fixing compilation on current nightlies.
9+
510
## 2.2.2
611

712
* Update README.md to clarify that 2.2 and above do not require the `nightly`

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
[package]
22
name = "subtle"
3-
version = "2.2.2"
3+
# Before incrementing:
4+
# - update CHANGELOG
5+
# - update html_root_url
6+
# - update README if necessary by semver
7+
version = "2.2.3"
48
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
59
"Henry de Valence <hdevalence@hdevalence.ca>"]
610
readme = "README.md"

README.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,34 @@ It consists of a `Choice` type, and a collection of traits using `Choice`
66
instead of `bool` which are intended to execute in constant-time. The `Choice`
77
type is a wrapper around a `u8` that holds a `0` or `1`.
88

9+
```toml
10+
subtle = "2.2"
11+
```
12+
913
This crate represents a “best-effort” attempt, since side-channels
1014
are ultimately a property of a deployed cryptographic system
1115
including the hardware it runs on, not just of software.
1216

1317
The traits are implemented using bitwise operations, and should execute in
14-
constant time provided that a) the bitwise operations are constant-time and b)
15-
the operations are not optimized into a branch.
18+
constant time provided that a) the bitwise operations are constant-time and
19+
b) the bitwise operations are not recognized as a conditional assignment and
20+
optimized back into a branch.
1621

17-
To prevent the latter possibility, the crate attempts to hide the value of a
18-
`Choice`'s inner `u8` from the optimizer, by passing it through either an
19-
inline assembly block or a volatile read. For more information, see the
20-
_About_ section below.
21-
22-
```toml
23-
[dependencies.subtle]
24-
version = "2.2"
25-
```
22+
For a compiler to recognize that bitwise operations represent a conditional
23+
assignment, it needs to know that the value used to generate the bitmasks is
24+
really a boolean `i1` rather than an `i8` byte value. In an attempt to
25+
prevent this refinement, the crate tries to hide the value of a `Choice`'s
26+
inner `u8` by passing it through a volatile read. For more information, see
27+
the _About_ section below.
2628

2729
Versions prior to `2.2` recommended use of the `nightly` feature to enable an
2830
optimization barrier; this is not required in versions `2.2` and above.
2931

32+
Note: the `subtle` crate contains `debug_assert`s to check invariants during
33+
debug builds. These invariant checks involve secret-dependent branches, and
34+
are not present when compiled in release mode. This crate is intended to be
35+
used in release mode.
36+
3037
## Documentation
3138

3239
Documentation is available [here][docs].

src/lib.rs

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
// - Henry de Valence <hdevalence@hdevalence.ca>
1010

1111
#![no_std]
12-
#![cfg_attr(feature = "nightly", feature(asm))]
1312
#![cfg_attr(feature = "nightly", feature(external_doc))]
1413
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
1514
#![cfg_attr(feature = "nightly", deny(missing_docs))]
1615
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
16+
#![doc(html_root_url = "https://docs.rs/subtle/2.2.3")]
1717

1818
//! Note that docs will only build on nightly Rust until
1919
//! [RFC 1990 stabilizes](https://github.com/rust-lang/rust/issues/44732).
@@ -24,25 +24,23 @@ extern crate std;
2424

2525
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
2626

27-
/// The `Choice` struct represents a choice for use in conditional
28-
/// assignment.
29-
///
30-
/// It is a wrapper around a `u8`, which should have the value either
31-
/// `1` (true) or `0` (false).
32-
///
33-
/// With the `nightly` feature enabled, the conversion from `u8` to
34-
/// `Choice` passes the value through an optimization barrier, as a
35-
/// best-effort attempt to prevent the compiler from inferring that the
36-
/// `Choice` value is a boolean. This strategy is based on Tim
37-
/// Maclean's [work on `rust-timing-shield`][rust-timing-shield],
38-
/// which attempts to provide a more comprehensive approach for
39-
/// preventing software side-channels in Rust code.
40-
///
41-
/// The `Choice` struct implements operators for AND, OR, XOR, and
42-
/// NOT, to allow combining `Choice` values.
43-
/// These operations do not short-circuit.
44-
///
45-
/// [rust-timing-shield]: https://www.chosenplaintext.ca/open-source/rust-timing-shield/security
27+
/// The `Choice` struct represents a choice for use in conditional assignment.
28+
///
29+
/// It is a wrapper around a `u8`, which should have the value either `1` (true)
30+
/// or `0` (false).
31+
///
32+
/// The conversion from `u8` to `Choice` passes the value through an optimization
33+
/// barrier, as a best-effort attempt to prevent the compiler from inferring that
34+
/// the `Choice` value is a boolean. This strategy is based on Tim Maclean's
35+
/// [work on `rust-timing-shield`][rust-timing-shield], which attempts to provide
36+
/// a more comprehensive approach for preventing software side-channels in Rust
37+
/// code.
38+
///
39+
/// The `Choice` struct implements operators for AND, OR, XOR, and NOT, to allow
40+
/// combining `Choice` values. These operations do not short-circuit.
41+
///
42+
/// [rust-timing-shield]:
43+
/// https://www.chosenplaintext.ca/open-source/rust-timing-shield/security
4644
#[derive(Copy, Clone, Debug)]
4745
pub struct Choice(u8);
4846

@@ -51,11 +49,11 @@ impl Choice {
5149
///
5250
/// # Note
5351
///
54-
/// This function only exists as an escape hatch for the rare case
52+
/// This function only exists as an **escape hatch** for the rare case
5553
/// where it's not possible to use one of the `subtle`-provided
5654
/// trait impls.
5755
///
58-
/// To convert a `Choice` to a `bool`, use the `From` implementation instead.
56+
/// **To convert a `Choice` to a `bool`, use the `From` implementation instead.**
5957
#[inline]
6058
pub fn unwrap_u8(&self) -> u8 {
6159
self.0
@@ -136,30 +134,19 @@ impl Not for Choice {
136134
}
137135
}
138136

139-
/// This function is a best-effort attempt to prevent the compiler
140-
/// from knowing anything about the value of the returned `u8`, other
141-
/// than its type.
142-
///
143-
/// Uses inline asm when available, otherwise it's a no-op.
144-
#[cfg(all(feature = "nightly", not(any(target_arch = "asmjs", target_arch = "wasm32"))))]
145-
#[inline(always)]
146-
fn black_box(mut input: u8) -> u8 {
147-
debug_assert!((input == 0u8) | (input == 1u8));
148-
149-
// Move value through assembler, which is opaque to the compiler, even though we don't do anything.
150-
unsafe { asm!("" : "=r"(input) : "0"(input) ) }
151-
152-
input
153-
}
154-
#[cfg(any(target_arch = "asmjs", target_arch = "wasm32", not(feature = "nightly")))]
137+
/// This function is a best-effort attempt to prevent the compiler from knowing
138+
/// anything about the value of the returned `u8`, other than its type.
139+
///
140+
/// Because we want to support stable Rust, we don't have access to inline
141+
/// assembly or test::black_box, so we use the fact that volatile values will
142+
/// never be elided to register values.
143+
///
144+
/// Note: Rust's notion of "volatile" is subject to change over time. While this
145+
/// code may break in a non-destructive way in the future, “constant-time” code
146+
/// is a continually moving target, and this is better than doing nothing.
155147
#[inline(never)]
156148
fn black_box(input: u8) -> u8 {
157149
debug_assert!((input == 0u8) | (input == 1u8));
158-
// We don't have access to inline assembly or test::black_box, so we use the fact that
159-
// volatile values will never be elided to register values.
160-
//
161-
// Note: Rust's notion of "volatile" is subject to change over time. While this code may break
162-
// in a non-destructive way in the future, it is better than doing nothing.
163150

164151
unsafe {
165152
// Optimization barrier

0 commit comments

Comments
 (0)