Skip to content

Commit 4d82669

Browse files
committed
subtle feature
Adds an off-by-default optional crate feature to support `subtle` traits including `ConstantTimeEq` and `ConditionallySelectable`
1 parent 431fe7d commit 4d82669

File tree

5 files changed

+76
-0
lines changed

5 files changed

+76
-0
lines changed

.github/workflows/hybrid-array.yml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
- run: cargo build --no-default-features --target ${{ matrix.target }} --features bytemuck
4141
- run: cargo build --no-default-features --target ${{ matrix.target }} --features extra-sizes
4242
- run: cargo build --no-default-features --target ${{ matrix.target }} --features serde
43+
- run: cargo build --no-default-features --target ${{ matrix.target }} --features subtle
4344
- run: cargo build --no-default-features --target ${{ matrix.target }} --features zeroize
4445
- run: cargo build --no-default-features --target ${{ matrix.target }} --all-features
4546

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typenum = { version = "1.17", features = ["const-generics"] }
2222
# optional dependencies
2323
bytemuck = { version = "1", optional = true, default-features = false }
2424
serde = { version = "1", optional = true, default-features = false }
25+
subtle = { version = "2", optional = true, default-features = false }
2526
zeroize = { version = "1.8", optional = true, default-features = false }
2627

2728
[dev-dependencies]

src/lib.rs

+38
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ use typenum::{Diff, Sum};
133133
#[cfg(feature = "bytemuck")]
134134
use bytemuck::{Pod, Zeroable};
135135

136+
#[cfg(feature = "subtle")]
137+
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
138+
136139
#[cfg(feature = "zeroize")]
137140
use zeroize::{Zeroize, ZeroizeOnDrop};
138141

@@ -854,6 +857,41 @@ where
854857
{
855858
}
856859

860+
#[cfg(feature = "subtle")]
861+
impl<T, U> ConditionallySelectable for Array<T, U>
862+
where
863+
Self: Copy,
864+
T: ConditionallySelectable,
865+
U: ArraySize,
866+
{
867+
#[inline]
868+
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
869+
let mut output = *a;
870+
output.conditional_assign(b, choice);
871+
output
872+
}
873+
874+
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
875+
for (a_i, b_i) in self.iter_mut().zip(other) {
876+
a_i.conditional_assign(b_i, choice)
877+
}
878+
}
879+
}
880+
881+
#[cfg(feature = "subtle")]
882+
impl<T, U> ConstantTimeEq for Array<T, U>
883+
where
884+
T: ConstantTimeEq,
885+
U: ArraySize,
886+
{
887+
#[inline]
888+
fn ct_eq(&self, other: &Self) -> Choice {
889+
self.iter()
890+
.zip(other.iter())
891+
.fold(Choice::from(1), |acc, (a, b)| acc & a.ct_eq(b))
892+
}
893+
}
894+
857895
#[cfg(feature = "zeroize")]
858896
impl<T, U> Zeroize for Array<T, U>
859897
where

tests/subtle.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//! Tests for `subtle` crate integration.
2+
3+
#![cfg(feature = "subtle")]
4+
5+
use hybrid_array::{Array, typenum::U3};
6+
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
7+
8+
#[test]
9+
fn constant_time_eq() {
10+
let a: Array<u8, U3> = Array([0, 0, 0]);
11+
let b: Array<u8, U3> = Array([1, 2, 3]);
12+
13+
assert!(bool::from(a.ct_eq(&a)));
14+
assert!(!bool::from(a.ct_ne(&a)));
15+
assert!(!bool::from(a.ct_eq(&b)));
16+
assert!(bool::from(a.ct_ne(&b)));
17+
}
18+
19+
#[test]
20+
fn conditional_select() {
21+
let a: Array<u8, U3> = Array([0, 0, 0]);
22+
let b: Array<u8, U3> = Array([1, 2, 3]);
23+
24+
let c = Array::conditional_select(&a, &b, Choice::from(0));
25+
assert_eq!(a, c);
26+
27+
let d = Array::conditional_select(&a, &b, Choice::from(1));
28+
assert_eq!(b, d);
29+
}

0 commit comments

Comments
 (0)