dcrypt_internal/
constant_time.rs1use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, Not};
7
8use crate::zeroing::Zeroize;
9
10#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
12pub struct Choice(u8);
13
14impl Choice {
15 #[inline(always)]
17 pub const fn unwrap_u8(self) -> u8 {
18 self.0
19 }
20}
21
22impl From<u8> for Choice {
23 #[inline(always)]
24 fn from(value: u8) -> Self {
25 Self(value & 1)
26 }
27}
28
29impl From<bool> for Choice {
30 #[inline(always)]
31 fn from(value: bool) -> Self {
32 Self(value as u8)
33 }
34}
35
36impl From<Choice> for bool {
37 #[inline(always)]
38 fn from(value: Choice) -> Self {
39 value.0 == 1
40 }
41}
42
43impl From<Choice> for u8 {
44 #[inline(always)]
45 fn from(value: Choice) -> Self {
46 value.0
47 }
48}
49
50impl Zeroize for Choice {
51 #[inline(never)]
52 fn zeroize(&mut self) {
53 self.0.zeroize();
54 }
55}
56
57impl Not for Choice {
58 type Output = Self;
59
60 #[inline(always)]
61 fn not(self) -> Self::Output {
62 Self(self.0 ^ 1)
63 }
64}
65
66impl BitAnd for Choice {
67 type Output = Self;
68
69 #[inline(always)]
70 fn bitand(self, rhs: Self) -> Self::Output {
71 Self(self.0 & rhs.0)
72 }
73}
74
75impl BitAndAssign for Choice {
76 #[inline(always)]
77 fn bitand_assign(&mut self, rhs: Self) {
78 self.0 &= rhs.0;
79 }
80}
81
82impl BitOr for Choice {
83 type Output = Self;
84
85 #[inline(always)]
86 fn bitor(self, rhs: Self) -> Self::Output {
87 Self(self.0 | rhs.0)
88 }
89}
90
91impl BitOrAssign for Choice {
92 #[inline(always)]
93 fn bitor_assign(&mut self, rhs: Self) {
94 self.0 |= rhs.0;
95 }
96}
97
98impl BitXor for Choice {
99 type Output = Self;
100
101 #[inline(always)]
102 fn bitxor(self, rhs: Self) -> Self::Output {
103 Self(self.0 ^ rhs.0)
104 }
105}
106
107pub trait ConditionallySelectable: Copy {
109 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self;
110}
111
112macro_rules! impl_conditionally_selectable_integer {
113 ($($ty:ty),+ $(,)?) => {$ (
114 impl ConditionallySelectable for $ty {
115 #[inline(always)]
116 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
117 let mask = (0 as $ty).wrapping_sub(choice.unwrap_u8() as $ty);
118 a ^ (mask & (a ^ b))
119 }
120 }
121 )+ };
122}
123
124impl_conditionally_selectable_integer!(u8, u16, u32, u64, u128, usize);
125
126impl ConditionallySelectable for Choice {
127 #[inline(always)]
128 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
129 Self(u8::conditional_select(&a.0, &b.0, choice))
130 }
131}
132
133pub trait ConstantTimeEq {
135 fn ct_eq(&self, other: &Self) -> Choice;
136}
137
138macro_rules! impl_constant_time_eq_integer {
139 ($($ty:ty),+ $(,)?) => {$ (
140 impl ConstantTimeEq for $ty {
141 #[inline(always)]
142 fn ct_eq(&self, other: &Self) -> Choice {
143 let difference = self ^ other;
144 let nonzero = difference | difference.wrapping_neg();
145 Choice::from(((nonzero >> (<$ty>::BITS - 1)) as u8) ^ 1)
146 }
147 }
148 )+ };
149}
150
151impl_constant_time_eq_integer!(u8, u16, u32, u64, u128, usize);
152
153impl ConstantTimeEq for Choice {
154 #[inline(always)]
155 fn ct_eq(&self, other: &Self) -> Choice {
156 self.0.ct_eq(&other.0)
157 }
158}
159
160impl<T: ConstantTimeEq, const N: usize> ConstantTimeEq for [T; N] {
161 fn ct_eq(&self, other: &Self) -> Choice {
162 let mut equal = Choice::from(1u8);
163 for index in 0..N {
164 equal &= self[index].ct_eq(&other[index]);
165 }
166 equal
167 }
168}
169
170impl<T: ConstantTimeEq> ConstantTimeEq for [T] {
171 fn ct_eq(&self, other: &Self) -> Choice {
172 if self.len() != other.len() {
173 return Choice::from(0u8);
174 }
175 let mut equal = Choice::from(1u8);
176 for (left, right) in self.iter().zip(other) {
177 equal &= left.ct_eq(right);
178 }
179 equal
180 }
181}
182
183#[derive(Clone, Copy, Debug)]
185pub struct CtOption<T> {
186 value: T,
187 is_some: Choice,
188}
189
190impl<T> CtOption<T> {
191 pub const fn new(value: T, is_some: Choice) -> Self {
192 Self { value, is_some }
193 }
194
195 pub const fn is_some(&self) -> Choice {
196 self.is_some
197 }
198
199 pub fn is_none(&self) -> Choice {
200 !self.is_some
201 }
202
203 pub fn unwrap(self) -> T {
204 assert!(
205 bool::from(self.is_some),
206 "called CtOption::unwrap on an invalid value"
207 );
208 self.value
209 }
210
211 pub fn unwrap_or(self, default: T) -> T
212 where
213 T: ConditionallySelectable,
214 {
215 T::conditional_select(&default, &self.value, self.is_some)
216 }
217
218 pub fn unwrap_or_else<F>(self, default: F) -> T
219 where
220 T: ConditionallySelectable,
221 F: FnOnce() -> T,
222 {
223 self.unwrap_or(default())
224 }
225
226 pub fn and_then<U, F>(self, function: F) -> CtOption<U>
227 where
228 F: FnOnce(T) -> CtOption<U>,
229 {
230 let next = function(self.value);
231 CtOption::new(next.value, self.is_some & next.is_some)
232 }
233
234 pub fn map<U, F>(self, function: F) -> CtOption<U>
235 where
236 F: FnOnce(T) -> U,
237 {
238 CtOption::new(function(self.value), self.is_some)
239 }
240
241 pub fn into_option(self) -> Option<T> {
242 self.into()
243 }
244
245 pub fn or_else<F>(self, function: F) -> Self
246 where
247 T: ConditionallySelectable,
248 F: FnOnce() -> Self,
249 {
250 let alternative = function();
251 Self::new(
252 T::conditional_select(&alternative.value, &self.value, self.is_some),
253 self.is_some | alternative.is_some,
254 )
255 }
256}
257
258impl<T> From<CtOption<T>> for Option<T> {
259 fn from(value: CtOption<T>) -> Self {
260 if bool::from(value.is_some) {
261 Some(value.value)
262 } else {
263 None
264 }
265 }
266}
267
268pub fn ct_eq<A, B>(a: A, b: B) -> bool
275where
276 A: AsRef<[u8]>,
277 B: AsRef<[u8]>,
278{
279 let a = a.as_ref();
280 let b = b.as_ref();
281
282 if a.len() != b.len() {
283 return false;
284 }
285
286 bool::from(a.ct_eq(b))
287}