dcrypt_internal/
constant_time.rs

1//! Constant-time operations to prevent timing attacks
2
3use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
4
5/// Constant-time comparison of two byte slices
6///
7/// Returns true if the slices are equal, false otherwise.
8/// This function runs in constant time regardless of the input values.
9pub fn ct_eq<A, B>(a: A, b: B) -> bool
10where
11    A: AsRef<[u8]>,
12    B: AsRef<[u8]>,
13{
14    let a = a.as_ref();
15    let b = b.as_ref();
16
17    if a.len() != b.len() {
18        return false;
19    }
20
21    a.ct_eq(b).into()
22}
23
24/// Constant-time selection of a byte
25///
26/// Returns `a` if `condition` is false, `b` if `condition` is true.
27/// This function runs in constant time regardless of the input values.
28pub fn ct_select<T>(a: T, b: T, condition: bool) -> T
29where
30    T: ConditionallySelectable,
31{
32    let choice = Choice::from(condition as u8);
33    T::conditional_select(&a, &b, choice)
34}
35
36/// Constant-time conditional assignment
37///
38/// Sets `dst` to `src` if `condition` is true, otherwise leaves `dst` unchanged.
39/// This function runs in constant time regardless of the input values.
40pub fn ct_assign(dst: &mut [u8], src: &[u8], condition: bool) {
41    assert_eq!(dst.len(), src.len());
42
43    let choice = Choice::from(condition as u8);
44
45    for i in 0..dst.len() {
46        dst[i] = u8::conditional_select(&dst[i], &src[i], choice);
47    }
48}
49
50/// Trait for types that can be compared in constant time
51pub trait ConstantTimeEquals {
52    /// Compare two values in constant time
53    fn ct_equals(&self, other: &Self) -> bool;
54}
55
56/// Implement ConstantTimeEquals for all types that implement AsRef<[u8]>
57impl<T: AsRef<[u8]>> ConstantTimeEquals for T {
58    fn ct_equals(&self, other: &Self) -> bool {
59        ct_eq(self.as_ref(), other.as_ref())
60    }
61}
62
63/// Constant-time equality check that returns a Choice (0 or 1)
64pub fn ct_eq_choice<A, B>(a: A, b: B) -> Choice
65where
66    A: AsRef<[u8]>,
67    B: AsRef<[u8]>,
68{
69    let a = a.as_ref();
70    let b = b.as_ref();
71
72    if a.len() != b.len() {
73        return Choice::from(0);
74    }
75
76    a.ct_eq(b)
77}
78
79/// Apply a constant-time bitwise AND operation between two arrays
80pub fn ct_and<const N: usize>(a: &[u8; N], b: &[u8; N]) -> [u8; N] {
81    let mut result = [0u8; N];
82    for i in 0..N {
83        result[i] = a[i] & b[i];
84    }
85    result
86}
87
88/// Apply a constant-time bitwise OR operation between two arrays
89pub fn ct_or<const N: usize>(a: &[u8; N], b: &[u8; N]) -> [u8; N] {
90    let mut result = [0u8; N];
91    for i in 0..N {
92        result[i] = a[i] | b[i];
93    }
94    result
95}
96
97/// Apply a constant-time bitwise XOR operation between two arrays
98pub fn ct_xor<const N: usize>(a: &[u8; N], b: &[u8; N]) -> [u8; N] {
99    let mut result = [0u8; N];
100    for i in 0..N {
101        result[i] = a[i] ^ b[i];
102    }
103    result
104}
105
106/// Generic constant-time conditional operation on byte arrays
107///
108/// Applies the function `op` to elements of `a` and `b` based on the condition.
109/// This is useful for implementing constant-time bit operations.
110pub fn ct_op<const N: usize, F>(a: &[u8; N], b: &[u8; N], condition: bool, op: F) -> [u8; N]
111where
112    F: Fn(u8, u8) -> u8,
113{
114    let choice = Choice::from(condition as u8);
115    let mut result = [0u8; N];
116
117    for i in 0..N {
118        // If condition is true, apply op(a[i], b[i]), otherwise keep a[i]
119        let operated = op(a[i], b[i]);
120        result[i] = u8::conditional_select(&a[i], &operated, choice);
121    }
122
123    result
124}
125
126/// Constant-time mask generation for a boolean condition
127///
128/// Returns an all-1s mask if condition is true, all-0s if false
129pub fn ct_mask(condition: bool) -> u8 {
130    0u8.wrapping_sub(condition as u8)
131}