Skip to content

Commit f845f90

Browse files
authored
Merge pull request #71 from dalek-cryptography/remove-asm
Remove inline assembly.
2 parents ae6aa12 + c5078ca commit f845f90

File tree

2 files changed

+47
-54
lines changed

2 files changed

+47
-54
lines changed

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: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
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))]
@@ -24,25 +23,23 @@ extern crate std;
2423

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

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

@@ -51,11 +48,11 @@ impl Choice {
5148
///
5249
/// # Note
5350
///
54-
/// This function only exists as an escape hatch for the rare case
51+
/// This function only exists as an **escape hatch** for the rare case
5552
/// where it's not possible to use one of the `subtle`-provided
5653
/// trait impls.
5754
///
58-
/// To convert a `Choice` to a `bool`, use the `From` implementation instead.
55+
/// **To convert a `Choice` to a `bool`, use the `From` implementation instead.**
5956
#[inline]
6057
pub fn unwrap_u8(&self) -> u8 {
6158
self.0
@@ -136,30 +133,19 @@ impl Not for Choice {
136133
}
137134
}
138135

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")))]
136+
/// This function is a best-effort attempt to prevent the compiler from knowing
137+
/// anything about the value of the returned `u8`, other than its type.
138+
///
139+
/// Because we want to support stable Rust, we don't have access to inline
140+
/// assembly or test::black_box, so we use the fact that volatile values will
141+
/// never be elided to register values.
142+
///
143+
/// Note: Rust's notion of "volatile" is subject to change over time. While this
144+
/// code may break in a non-destructive way in the future, “constant-time” code
145+
/// is a continually moving target, and this is better than doing nothing.
155146
#[inline(never)]
156147
fn black_box(input: u8) -> u8 {
157148
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.
163149

164150
unsafe {
165151
// Optimization barrier

0 commit comments

Comments
 (0)