Skip to content

Commit

Permalink
Merge branch 'release/2.5' into main
Browse files Browse the repository at this point in the history
isislovecruft committed Feb 28, 2023
2 parents bd282be + 574347d commit 6b6a81a
Showing 12 changed files with 206 additions and 55 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -27,7 +27,9 @@ script:
cargo test --no-default-features --features std &&
cargo test --no-default-features --features "std i128" &&
cargo test --no-default-features --features "std core_hint_black_box" &&
cargo test --no-default-features --features "std i128 core_hint_black_box"
cargo test --no-default-features --features "std const-generics" &&
cargo test --no-default-features --features "std i128 core_hint_black_box" &&
cargo test --no-default-features --features "std i128 core_hint_black_box const-generics"

notifications:
slack:
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,21 @@

Entries are listed in reverse chronological order.

## 2.5.0

* Add constant-timedness note to the documentation for `CtOption::unwrap_or_else`.
* Add `CtOption::expect`.
* Add `ConstantTimeEq::ct_ne` with default implementation.
* Add new `core_hint_black_box` feature from Diane Hosfelt and Amber
Sprenkels which utilises the original `black_box` functionality from
when subtle was first written, which has now found it's way into the
Rust standard library.
* Add new `const-generics` feature from @survived which adds support
for subtle traits for generic arrays `[T; N]`.
* Add new feature for supporting `core::cmp::Ordering` for types which
implement subtle traits, patch from @tarcieri.
* Update `rand` dependency to 0.8.

## 2.4.1

* Fix a bug in how the README was included in the documentation builds
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ name = "subtle"
# - update html_root_url
# - update README if necessary by semver
# - if any updates were made to the README, also update the module documentation in src/lib.rs
version = "2.4.1"
version = "2.5.0"
edition = "2018"
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
"Henry de Valence <hdevalence@hdevalence.ca>"]
readme = "README.md"
@@ -25,9 +26,10 @@ exclude = [
travis-ci = { repository = "dalek-cryptography/subtle", branch = "master"}

[dev-dependencies]
rand = { version = "0.7" }
rand = { version = "0.8" }

[features]
const-generics = []
core_hint_black_box = []
default = ["std", "i128"]
std = []
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ instead of `bool` which are intended to execute in constant-time. The `Choice`
type is a wrapper around a `u8` that holds a `0` or `1`.

```toml
subtle = "2.4"
subtle = "2.5"
```

This crate represents a “best-effort” attempt, since side-channels
@@ -30,6 +30,9 @@ Rust versions from 1.66 or higher support a new best-effort optimization
barrier ([`core::hint::black_box`]). To use the new optimization barrier,
enable the `core_hint_black_box` feature.

Rust versions from 1.51 or higher have const generics support. You may enable
`const-generics` feautre to have `subtle` traits implemented for arrays `[T; N]`.

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

@@ -58,7 +61,7 @@ which attempts to provide a more comprehensive approach for preventing
software side-channels in Rust code.

From version `2.2`, it was based on Diane Hosfelt and Amber Sprenkels' work on
"Secret Types in Rust". Version `2.3` adds the `core_hint_black_box` feature,
"Secret Types in Rust". Version `2.5` adds the `core_hint_black_box` feature,
which uses the original method through the [`core::hint::black_box`] function
from the Rust standard library.

21 changes: 17 additions & 4 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@

[package]
name = "subtle-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies.subtle]
path = ".."
features = ["nightly"]
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
features = ["nightly", "const-generics"]

[dependencies]
libfuzzer-sys = "0.4"

# Prevent this from interfering with workspaces
[workspace]
@@ -21,15 +22,27 @@ members = ["."]
[[bin]]
name = "conditional_assign_u8"
path = "fuzzers/conditional_assign_u8.rs"
test = false
doc = false

[[bin]]
name = "conditional_assign_u16"
path = "fuzzers/conditional_assign_u16.rs"
test = false
doc = false

[[bin]]
name = "conditional_assign_i8"
path = "fuzzers/conditional_assign_i8.rs"
test = false
doc = false

[[bin]]
name = "conditional_assign_i128"
path = "fuzzers/conditional_assign_i128.rs"
test = false
doc = false

[[bin]]
name = "conditional_assign_array"
path = "fuzzers/conditional_assign_array.rs"
29 changes: 29 additions & 0 deletions fuzz/fuzzers/conditional_assign_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;
extern crate subtle;
extern crate core;

use core::convert::TryFrom;

use subtle::ConditionallySelectable;

fuzz_target!(|data: &[u8]| {
let chunk_size: usize = 16;

if data.len() % chunk_size != 0 {
return;
}

for bytes in data.chunks(chunk_size) {
let mut x = [0u8; 16];
let y = <[u8; 16]>::try_from(bytes).unwrap();

x.conditional_assign(&y, 0.into());
assert_eq!(x, [0u8; 16]);

x.conditional_assign(&y, 1.into());
assert_eq!(x, y);
}
});
16 changes: 5 additions & 11 deletions fuzz/fuzzers/conditional_assign_i128.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;
extern crate subtle;
extern crate core;

use libfuzzer_sys::fuzz_target;
use core::intrinsics::transmute;

use subtle::ConditionallySelectable;

fuzz_target!(|data: &[u8]| {
@@ -20,10 +14,10 @@ fuzz_target!(|data: &[u8]| {
unsafe {
let mut x: i128 = 0;
let y: i128 = transmute::<[u8; 16], i128>([
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]]);
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
bytes[15],
]);

x.conditional_assign(&y, 0.into());
assert_eq!(x, 0);
8 changes: 1 addition & 7 deletions fuzz/fuzzers/conditional_assign_i8.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;
extern crate subtle;
extern crate core;

use libfuzzer_sys::fuzz_target;
use core::intrinsics::transmute;

use subtle::ConditionallySelectable;

fuzz_target!(|data: &[u8]| {
8 changes: 1 addition & 7 deletions fuzz/fuzzers/conditional_assign_u16.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;
extern crate subtle;
extern crate core;

use libfuzzer_sys::fuzz_target;
use core::intrinsics::transmute;

use subtle::ConditionallySelectable;

fuzz_target!(|data: &[u8]| {
7 changes: 1 addition & 6 deletions fuzz/fuzzers/conditional_assign_u8.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;
extern crate subtle;
extern crate core;

use libfuzzer_sys::fuzz_target;
use subtle::ConditionallySelectable;

fuzz_target!(|data: &[u8]| {
102 changes: 89 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
#![no_std]
#![deny(missing_docs)]
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
#![doc(html_root_url = "https://docs.rs/subtle/2.4.1")]
#![doc(html_root_url = "https://docs.rs/subtle/2.5.0")]

//! # subtle [![](https://img.shields.io/crates/v/subtle.svg)](https://crates.io/crates/subtle) [![](https://img.shields.io/badge/dynamic/json.svg?label=docs&uri=https%3A%2F%2Fcrates.io%2Fapi%2Fv1%2Fcrates%2Fsubtle%2Fversions&query=%24.versions%5B0%5D.num&colorB=4F74A6)](https://doc.dalek.rs/subtle) [![](https://travis-ci.org/dalek-cryptography/subtle.svg?branch=master)](https://travis-ci.org/dalek-cryptography/subtle)
//!
@@ -22,7 +22,7 @@
//! type is a wrapper around a `u8` that holds a `0` or `1`.
//!
//! ```toml
//! subtle = "2.4"
//! subtle = "2.5"
//! ```
//!
//! This crate represents a “best-effort” attempt, since side-channels
@@ -41,6 +41,13 @@
//! inner `u8` by passing it through a volatile read. For more information, see
//! the _About_ section below.
//!
//! Rust versions from 1.66 or higher support a new best-effort optimization
//! barrier ([`core::hint::black_box`]). To use the new optimization barrier,
//! enable the `core_hint_black_box` feature.
//!
//! Rust versions from 1.51 or higher have const generics support. You may enable
//! `const-generics` feautre to have `subtle` traits implemented for arrays `[T; N]`.
//!
//! Versions prior to `2.2` recommended use of the `nightly` feature to enable an
//! optimization barrier; this is not required in versions `2.2` and above.
//!
@@ -63,10 +70,15 @@
//!
//! This library aims to be the Rust equivalent of Go’s `crypto/subtle` module.
//!
//! The optimization barrier in `impl From<u8> for Choice` was based on Tim
//! Maclean's [work on `rust-timing-shield`][rust-timing-shield], which attempts to
//! provide a more comprehensive approach for preventing software side-channels in
//! Rust code.
//! Old versions of the optimization barrier in `impl From<u8> for Choice` were
//! based on Tim Maclean's [work on `rust-timing-shield`][rust-timing-shield],
//! which attempts to provide a more comprehensive approach for preventing
//! software side-channels in Rust code.
//!
//! From version `2.2`, it was based on Diane Hosfelt and Amber Sprenkels' work on
//! "Secret Types in Rust". Version `2.5` adds the `core_hint_black_box` feature,
//! which uses the original method through the [`core::hint::black_box`] function
//! from the Rust standard library.
//!
//! `subtle` is authored by isis agora lovecruft and Henry de Valence.
//!
@@ -81,12 +93,14 @@
//! **USE AT YOUR OWN RISK**
//!
//! [docs]: https://docs.rs/subtle
//! [`core::hint::black_box`]: https://doc.rust-lang.org/core/hint/fn.black_box.html
//! [rust-timing-shield]: https://www.chosenplaintext.ca/open-source/rust-timing-shield/security
#[cfg(feature = "std")]
#[macro_use]
extern crate std;

use core::cmp;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
use core::option::Option;

@@ -229,7 +243,7 @@ fn black_box(input: u8) -> u8 {
}

#[cfg(feature = "core_hint_black_box")]
#[inline]
#[inline(never)]
fn black_box(input: u8) -> u8 {
debug_assert!((input == 0u8) | (input == 1u8));
core::hint::black_box(input)
@@ -371,6 +385,14 @@ generate_integer_equal!(u64, i64, 64);
generate_integer_equal!(u128, i128, 128);
generate_integer_equal!(usize, isize, ::core::mem::size_of::<usize>() * 8);

/// `Ordering` is `#[repr(i8)]` making it possible to leverage `i8::ct_eq`.
impl ConstantTimeEq for cmp::Ordering {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice {
(*self as i8).ct_eq(&(*other as i8))
}
}

/// A type which can be conditionally selected in constant time.
///
/// This trait also provides generic implementations of conditional
@@ -388,7 +410,6 @@ pub trait ConditionallySelectable: Copy {
/// # Example
///
/// ```
/// # extern crate subtle;
/// use subtle::ConditionallySelectable;
/// #
/// # fn main() {
@@ -411,7 +432,6 @@ pub trait ConditionallySelectable: Copy {
/// # Example
///
/// ```
/// # extern crate subtle;
/// use subtle::ConditionallySelectable;
/// #
/// # fn main() {
@@ -437,7 +457,6 @@ pub trait ConditionallySelectable: Copy {
/// # Example
///
/// ```
/// # extern crate subtle;
/// use subtle::ConditionallySelectable;
/// #
/// # fn main() {
@@ -532,13 +551,52 @@ generate_integer_conditional_select!( u64 i64);
#[cfg(feature = "i128")]
generate_integer_conditional_select!(u128 i128);

/// `Ordering` is `#[repr(i8)]` where:
///
/// - `Less` => -1
/// - `Equal` => 0
/// - `Greater` => 1
///
/// Given this, it's possible to operate on orderings as if they're integers,
/// which allows leveraging conditional masking for predication.
impl ConditionallySelectable for cmp::Ordering {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let a = *a as i8;
let b = *b as i8;
let ret = i8::conditional_select(&a, &b, choice);

// SAFETY: `Ordering` is `#[repr(i8)]` and `ret` has been assigned to
// a value which was originally a valid `Ordering` then cast to `i8`
unsafe { *((&ret as *const _) as *const cmp::Ordering) }
}
}

impl ConditionallySelectable for Choice {
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Choice(u8::conditional_select(&a.0, &b.0, choice))
}
}

#[cfg(feature = "const-generics")]
impl<T, const N: usize> ConditionallySelectable for [T; N]
where
T: ConditionallySelectable,
{
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut output = *a;
output.conditional_assign(b, choice);
output
}

fn conditional_assign(&mut self, other: &Self, choice: Choice) {
for (a_i, b_i) in self.iter_mut().zip(other) {
a_i.conditional_assign(b_i, choice)
}
}
}

/// A type which can be conditionally negated in constant time.
///
/// # Note
@@ -784,7 +842,6 @@ pub trait ConstantTimeGreater {
/// # Example
///
/// ```
/// # extern crate subtle;
/// use subtle::ConstantTimeGreater;
///
/// let x: u8 = 13;
@@ -837,7 +894,7 @@ macro_rules! generate_unsigned_integer_greater {
Choice::from((bit & 1) as u8)
}
}
}
};
}

generate_unsigned_integer_greater!(u8, 8);
@@ -847,6 +904,16 @@ generate_unsigned_integer_greater!(u64, 64);
#[cfg(feature = "i128")]
generate_unsigned_integer_greater!(u128, 128);

impl ConstantTimeGreater for cmp::Ordering {
#[inline]
fn ct_gt(&self, other: &Self) -> Choice {
// No impl of `ConstantTimeGreater` for `i8`, so use `u8`
let a = (*self as i8) + 1;
let b = (*other as i8) + 1;
(a as u8).ct_gt(&(b as u8))
}
}

/// A type which can be compared in some manner and be determined to be less
/// than another of the same type.
pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
@@ -868,7 +935,6 @@ pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
/// # Example
///
/// ```
/// # extern crate subtle;
/// use subtle::ConstantTimeLess;
///
/// let x: u8 = 13;
@@ -898,3 +964,13 @@ impl ConstantTimeLess for u32 {}
impl ConstantTimeLess for u64 {}
#[cfg(feature = "i128")]
impl ConstantTimeLess for u128 {}

impl ConstantTimeLess for cmp::Ordering {
#[inline]
fn ct_lt(&self, other: &Self) -> Choice {
// No impl of `ConstantTimeLess` for `i8`, so use `u8`
let a = (*self as i8) + 1;
let b = (*other as i8) + 1;
(a as u8).ct_lt(&(b as u8))
}
}
38 changes: 36 additions & 2 deletions tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
extern crate rand;
extern crate subtle;
use std::cmp;

use rand::rngs::OsRng;
use rand::RngCore;
@@ -96,6 +95,19 @@ fn custom_conditional_select_i16() {
assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514);
}

#[test]
fn ordering_conditional_select() {
assert_eq!(
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()),
cmp::Ordering::Less
);

assert_eq!(
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()),
cmp::Ordering::Greater
);
}

macro_rules! generate_integer_equal_tests {
($($t:ty),*) => ($(
let y: $t = 0; // all 0 bits
@@ -149,6 +161,16 @@ fn choice_equal() {
assert!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8() == 1);
}

#[test]
fn ordering_equal() {
let a = cmp::Ordering::Equal;
let b = cmp::Ordering::Greater;
let c = a;

assert_eq!(a.ct_eq(&b).unwrap_u8(), 0);
assert_eq!(a.ct_eq(&c).unwrap_u8(), 1);
}

#[test]
fn test_ctoption() {
let a = CtOption::new(10, Choice::from(1));
@@ -334,6 +356,12 @@ fn greater_than_u128() {
generate_greater_than_test!(u128);
}

#[test]
fn greater_than_ordering() {
assert_eq!(cmp::Ordering::Less.ct_gt(&cmp::Ordering::Greater).unwrap_u8(), 0);
assert_eq!(cmp::Ordering::Greater.ct_gt(&cmp::Ordering::Less).unwrap_u8(), 1);
}

#[test]
/// Test that the two's compliment min and max, i.e. 0000...0001 < 1111...1110,
/// gives the correct result. (This fails using the bit-twiddling algorithm that
@@ -389,3 +417,9 @@ fn less_than_u64() {
fn less_than_u128() {
generate_less_than_test!(u128);
}

#[test]
fn less_than_ordering() {
assert_eq!(cmp::Ordering::Greater.ct_lt(&cmp::Ordering::Less).unwrap_u8(), 0);
assert_eq!(cmp::Ordering::Less.ct_lt(&cmp::Ordering::Greater).unwrap_u8(), 1);
}

0 comments on commit 6b6a81a

Please sign in to comment.