Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 73 additions & 45 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,91 @@ on: [push, pull_request]

name: Test

permissions:
contents: read

env:
RUSTFLAGS: "-D warnings"

jobs:
no_std:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.85.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }}

test:
name: cargo test
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.85.0 # MSRV
- stable
- beta
- nightly
- 1.60.0
steps:
- name: checkout
uses: actions/checkout@v2
- name: toolchain
uses: actions-rs/toolchain@v1
uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: thumbv7em-none-eabi
override: true
- name: test
uses: actions-rs/cargo@v1
with:
command: test
- name: nightly
uses: actions-rs/cargo@v1
with:
command: test
args: --features nightly
- name: no-default-features
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features
- name: std
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features std
- name: std const-generics
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features "std const-generics"
- name: std i128
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features "std i128"
- name: std i128 const-generics
uses: actions-rs/cargo@v1
- run: cargo check
- run: cargo test
- run: cargo test --release

# Test using `cargo careful`
test-careful:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@nightly
- run: cargo install cargo-careful
- run: cargo careful test --all-features

# Test using `cargo miri`
test-miri:
runs-on: ubuntu-latest
env:
MIRIFLAGS: "-Zmiri-symbolic-alignment-check -Zmiri-strict-provenance"
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- s390x-unknown-linux-gnu
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@nightly
- run: rustup component add miri && cargo miri setup
- run: cargo miri test --target ${{ matrix.target }} --all-features --lib

clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
command: test
args: --no-default-features --features "std i128 const-generics"
- name: no std build
uses: actions-rs/cargo@v1
toolchain: 1.92.0 # Pinned to prevent breakages
components: clippy
- run: cargo clippy --workspace --all-features --lib --bins --tests -- -D warnings

rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
command: build
args: --no-default-features --target thumbv7em-none-eabi
toolchain: stable
components: rustfmt
- run: cargo fmt --all -- --check
26 changes: 5 additions & 21 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
name = "subtle"
# Before incrementing:
# - update CHANGELOG
# - 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.6.0"
edition = "2018"
version = "3.0.0-pre"
edition = "2024"
rust-version = "1.85.0"
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
"Henry de Valence <hdevalence@hdevalence.ca>"]
readme = "README.md"
Expand All @@ -17,23 +17,7 @@ documentation = "https://docs.rs/subtle"
categories = ["cryptography", "no-std"]
keywords = ["cryptography", "crypto", "constant-time", "utilities"]
description = "Pure-Rust traits and utilities for constant-time cryptographic implementations."
exclude = [
"**/.gitignore",
".travis.yml",
]

[badges]
travis-ci = { repository = "dalek-cryptography/subtle", branch = "master"}
exclude = [".github", ".gitignore"]

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

[features]
const-generics = []
# DEPRECATED: As of 2.5.1, this feature does nothing.
core_hint_black_box = []
default = ["std", "i128"]
std = []
i128 = []
# DEPRECATED: As of 2.4.1, this feature does nothing.
nightly = []
rand = { version = "0.9" }
50 changes: 6 additions & 44 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
// - Henry de Valence <hdevalence@hdevalence.ca>

#![no_std]
#![allow(clippy::inline_fn_without_body)]
#![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.6.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)
//!
Expand All @@ -22,7 +22,7 @@
//! type is a wrapper around a `u8` that holds a `0` or `1`.
//!
//! ```toml
//! subtle = "2.6"
//! subtle = "3.0.0-pre"
//! ```
//!
//! This crate represents a “best-effort” attempt, since side-channels
Expand Down Expand Up @@ -58,7 +58,7 @@
//!
//! ## Minimum Supported Rust Version
//!
//! Rust **1.41** or higher.
//! Rust **1.85** or higher.
//!
//! Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump.
//!
Expand Down Expand Up @@ -88,17 +88,11 @@
//! [docs]: https://docs.rs/subtle
//! [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::hint::black_box;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
use core::option::Option;

#[cfg(feature = "core_hint_black_box")]
use core::hint::black_box;

/// The `Choice` struct represents a choice for use in conditional assignment.
///
/// It is a wrapper around a `u8`, which should have the value either `1` (true)
Expand Down Expand Up @@ -209,30 +203,6 @@ impl Not for Choice {
}
}

/// This function is a best-effort attempt to prevent the compiler from knowing
/// anything about the value of the returned `u8`, other than its type.
///
/// Because we want to support stable Rust, we don't have access to inline
/// assembly or test::black_box, so we use the fact that volatile values will
/// never be elided to register values.
///
/// Note: Rust's notion of "volatile" is subject to change over time. While this
/// code may break in a non-destructive way in the future, “constant-time” code
/// is a continually moving target, and this is better than doing nothing.
#[cfg(not(feature = "core_hint_black_box"))]
#[inline(never)]
fn black_box<T: Copy>(input: T) -> T {
unsafe {
// Optimization barrier
//
// SAFETY:
// - &input is not NULL because we own input;
// - input is Copy and always live;
// - input is always properly aligned.
core::ptr::read_volatile(&input)
}
}

impl From<u8> for Choice {
#[inline]
fn from(input: u8) -> Choice {
Expand Down Expand Up @@ -371,7 +341,6 @@ generate_integer_equal!(u8, i8, 8);
generate_integer_equal!(u16, i16, 16);
generate_integer_equal!(u32, i32, 32);
generate_integer_equal!(u64, i64, 64);
#[cfg(feature = "i128")]
generate_integer_equal!(u128, i128, 128);
generate_integer_equal!(usize, isize, ::core::mem::size_of::<usize>() * 8);

Expand Down Expand Up @@ -468,7 +437,7 @@ pub trait ConditionallySelectable: Copy {
#[inline]
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
let t: Self = *a;
a.conditional_assign(&b, choice);
a.conditional_assign(b, choice);
b.conditional_assign(&t, choice);
}
}
Expand Down Expand Up @@ -542,7 +511,6 @@ generate_integer_conditional_select!( u8 i8);
generate_integer_conditional_select!( u16 i16);
generate_integer_conditional_select!( u32 i32);
generate_integer_conditional_select!( u64 i64);
#[cfg(feature = "i128")]
generate_integer_conditional_select!(u128 i128);

/// `Ordering` is `#[repr(i8)]` where:
Expand Down Expand Up @@ -572,7 +540,6 @@ impl ConditionallySelectable for Choice {
}
}

#[cfg(feature = "const-generics")]
impl<T, const N: usize> ConditionallySelectable for [T; N]
where
T: ConditionallySelectable,
Expand Down Expand Up @@ -676,10 +643,7 @@ impl<T> CtOption<T> {
/// exposed.
#[inline]
pub fn new(value: T, is_some: Choice) -> CtOption<T> {
CtOption {
value: value,
is_some: is_some,
}
CtOption { value, is_some }
}

/// Returns the contained value, consuming the `self` value.
Expand Down Expand Up @@ -915,7 +879,6 @@ generate_unsigned_integer_greater!(u8, 8);
generate_unsigned_integer_greater!(u16, 16);
generate_unsigned_integer_greater!(u32, 32);
generate_unsigned_integer_greater!(u64, 64);
#[cfg(feature = "i128")]
generate_unsigned_integer_greater!(u128, 128);

impl ConstantTimeGreater for cmp::Ordering {
Expand Down Expand Up @@ -976,7 +939,6 @@ impl ConstantTimeLess for u8 {}
impl ConstantTimeLess for u16 {}
impl ConstantTimeLess for u32 {}
impl ConstantTimeLess for u64 {}
#[cfg(feature = "i128")]
impl ConstantTimeLess for u128 {}

impl ConstantTimeLess for cmp::Ordering {
Expand Down
Loading