libcrux_secrets/
int.rs

1use crate::traits::*;
2
3#[cfg(feature = "check-secret-independence")]
4mod classify_secret;
5// If the feature "check-secret-independence" is set, we use secret integers
6#[cfg(feature = "check-secret-independence")]
7mod secret_integers;
8#[cfg(feature = "check-secret-independence")]
9pub use secret_integers::*;
10
11// If the feature "check-secret-independence" is not set, we use public integers
12#[cfg(not(feature = "check-secret-independence"))]
13mod classify_public;
14#[cfg(not(feature = "check-secret-independence"))]
15mod public_integers;
16#[cfg(not(feature = "check-secret-independence"))]
17pub use public_integers::*;
18
19// A macro defining const constructors for secret/public integers
20macro_rules! impl_new {
21    ($name:ident, $t:ty, $st:ty) => {
22        #[allow(non_snake_case)]
23        #[inline(always)]
24        pub const fn $name(v: $t) -> $st {
25            secret(v)
26        }
27    };
28}
29impl_new!(U8, u8, U8);
30impl_new!(U16, u16, U16);
31impl_new!(U32, u32, U32);
32impl_new!(U64, u64, U64);
33
34#[cfg(not(eurydice))]
35impl_new!(U128, u128, U128);
36
37impl_new!(I8, i8, I8);
38impl_new!(I16, i16, I16);
39impl_new!(I32, i32, I32);
40impl_new!(I64, i64, I64);
41
42#[cfg(not(eurydice))]
43impl_new!(I128, i128, I128);
44
45/// A trait defining cast operations for secret/public integers
46pub trait CastOps {
47    fn as_u8(self) -> U8;
48    fn as_i8(self) -> I8;
49    fn as_u16(self) -> U16;
50    fn as_i16(self) -> I16;
51    fn as_u32(self) -> U32;
52    fn as_i32(self) -> I32;
53    fn as_u64(self) -> U64;
54    fn as_i64(self) -> I64;
55
56    #[cfg(not(eurydice))]
57    fn as_u128(self) -> U128;
58    #[cfg(not(eurydice))]
59    fn as_i128(self) -> I128;
60}
61
62// Implementations of cast operations for all integers
63macro_rules! impl_cast_ops {
64    ($name:ident) => {
65        impl CastOps for $name {
66            #[inline(always)]
67            fn as_u8(self) -> U8 {
68                (self.declassify() as u8).classify()
69            }
70            #[inline(always)]
71            fn as_i8(self) -> I8 {
72                (self.declassify() as i8).classify()
73            }
74            #[inline(always)]
75            fn as_u16(self) -> U16 {
76                (self.declassify() as u16).classify()
77            }
78            #[inline(always)]
79            fn as_i16(self) -> I16 {
80                (self.declassify() as i16).classify()
81            }
82            #[inline(always)]
83            fn as_u32(self) -> U32 {
84                (self.declassify() as u32).classify()
85            }
86            #[inline(always)]
87            fn as_i32(self) -> I32 {
88                (self.declassify() as i32).classify()
89            }
90            #[inline(always)]
91            fn as_u64(self) -> U64 {
92                (self.declassify() as u64).classify()
93            }
94            #[inline(always)]
95            fn as_i64(self) -> I64 {
96                (self.declassify() as i64).classify()
97            }
98
99            #[cfg(not(eurydice))]
100            #[inline(always)]
101            fn as_u128(self) -> U128 {
102                (self.declassify() as u128).classify()
103            }
104            #[cfg(not(eurydice))]
105            #[inline(always)]
106            fn as_i128(self) -> I128 {
107                (self.declassify() as i128).classify()
108            }
109        }
110    };
111}
112impl_cast_ops!(U8);
113impl_cast_ops!(U16);
114impl_cast_ops!(U32);
115impl_cast_ops!(U64);
116
117#[cfg(not(eurydice))]
118impl_cast_ops!(U128);
119
120impl_cast_ops!(I8);
121impl_cast_ops!(I16);
122impl_cast_ops!(I32);
123impl_cast_ops!(I64);
124
125#[cfg(not(eurydice))]
126impl_cast_ops!(I128);
127
128/// Best effort constant time swapping of values.
129pub trait Swap {
130    /// Depending on `selector`, keep everything as is, or swap `self` and `other`.
131    ///
132    /// If `selector == 0`, the values are unchanged, otherwise swap.
133    fn cswap(&mut self, other: &mut Self, selector: U8);
134}
135
136/// Best effort constant time selection of values.
137pub trait Select {
138    /// Select `self` or `other`, depending on `selector`.
139    /// The selected value will be in `self`.
140    ///
141    /// If `selector != 0`, select `other`, otherwise
142    /// `self` is unchanged.
143    fn select(&mut self, other: &Self, selector: U8);
144}
145
146#[cfg(any(hax, not(target_arch = "aarch64")))]
147mod portable {
148    use super::{Select, Swap};
149    use crate::traits::Declassify;
150    use crate::U8;
151    #[cfg(feature = "check-secret-independence")]
152    use crate::{traits::*, U16, U32, U64};
153
154    // Don't inline this to avoid that the compiler optimizes this out.
155    #[inline(never)]
156    fn is_non_zero_32(selector: u8) -> u32 {
157        core::hint::black_box(
158            ((!(core::hint::black_box(selector) as u32)).wrapping_add(1) >> 31) & 1,
159        )
160    }
161
162    // Don't inline this to avoid that the compiler optimizes this out.
163    #[inline(never)]
164    fn is_non_zero_64(selector: u8) -> u64 {
165        core::hint::black_box(
166            ((!(core::hint::black_box(selector) as u64)).wrapping_add(1) >> 63) & 1,
167        )
168    }
169
170    /// This macro implements `Select` for public integer type
171    /// `&[$ty]` and its secret version `&[$secret_ty]`.
172    macro_rules! impl_select {
173        ($ty:ty, $secret_ty:ty, $is_non_zero:ident) => {
174            impl Select for [$ty] {
175                fn select(&mut self, other: &Self, selector: crate::U8) {
176                    let mask = ($is_non_zero(selector.declassify()) as $ty).wrapping_sub(1);
177                    for i in 0..self.len() {
178                        self[i] = (self[i] & mask) | (other[i] & !mask);
179                    }
180                }
181            }
182
183            #[cfg(feature = "check-secret-independence")]
184            impl Select for [$secret_ty] {
185                fn select(&mut self, other: &Self, selector: crate::U8) {
186                    let lhs = self.declassify_ref_mut();
187                    let rhs = other.declassify_ref();
188                    lhs.select(rhs, selector);
189                }
190            }
191        };
192    }
193
194    impl_select!(u8, U8, is_non_zero_32);
195    impl_select!(u16, U16, is_non_zero_32);
196    impl_select!(u32, U32, is_non_zero_32);
197    impl_select!(u64, U64, is_non_zero_64);
198
199    macro_rules! swap64 {
200        ($t:ty, $lhs:expr, $rhs:expr, $selector:expr) => {
201            let mask = core::hint::black_box(
202                ((((!($selector as u64)).wrapping_add(1) >> 63) & 1) as $t).wrapping_sub(1),
203            );
204            for i in 0..$lhs.len() {
205                let dummy = !mask & ($lhs[i] ^ $rhs[i]);
206                $lhs[i] ^= dummy;
207                $rhs[i] ^= dummy;
208            }
209        };
210    }
211
212    macro_rules! swap32 {
213        ($t:ty, $lhs:expr, $rhs:expr, $selector:expr) => {
214            let mask = core::hint::black_box(
215                ((((!(core::hint::black_box($selector) as u32)).wrapping_add(1) >> 31) & 1) as $t)
216                    .wrapping_sub(1),
217            );
218            for i in 0..$lhs.len() {
219                let dummy = !mask & ($lhs[i] ^ $rhs[i]);
220                $lhs[i] ^= dummy;
221                $rhs[i] ^= dummy;
222            }
223        };
224    }
225
226    /// This macro implements `Swap` for public integer type
227    /// `&[$ty]` and its secret version `&[$secret_ty]`.
228    macro_rules! impl_swap {
229        ($ty:ty, $secret_ty:ty, $swap:ident) => {
230            impl Swap for [$ty] {
231                #[inline]
232                fn cswap(&mut self, other: &mut Self, selector: U8) {
233                    debug_assert_eq!(self.len(), other.len());
234                    $swap!($ty, self, other, selector.declassify());
235                }
236            }
237
238            #[cfg(feature = "check-secret-independence")]
239            impl Swap for [$secret_ty] {
240                #[inline]
241                fn cswap(&mut self, other: &mut Self, selector: U8) {
242                    debug_assert_eq!(self.len(), other.len());
243                    let lhs = self.declassify_ref_mut();
244                    let rhs = other.declassify_ref_mut();
245                    $swap!($ty, lhs, rhs, selector.declassify());
246                }
247            }
248        };
249    }
250
251    impl_swap!(u8, U8, swap32);
252    impl_swap!(u16, U16, swap32);
253    impl_swap!(u32, U32, swap32);
254    impl_swap!(u64, U64, swap64);
255}
256
257#[cfg(all(not(hax), target_arch = "aarch64"))]
258mod aarch64 {
259    use core::arch::asm;
260
261    use super::*;
262
263    macro_rules! select64 {
264        ($lhs:expr, $rhs:expr, $selector:expr) => {
265            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
266            #[allow(unsafe_code)]
267            unsafe {
268                asm! {
269                    "cmp {cond:w}, 0",
270                    "csel {lhs:x}, {rhs:x}, {lhs:x}, NE",
271                    cond = in(reg) $selector,
272                    lhs = inlateout(reg) *$lhs,
273                    rhs = in(reg) *$rhs,
274                    options(nostack),
275                };
276            }
277        };
278    }
279
280    macro_rules! select32 {
281        ($lhs:expr, $rhs:expr, $selector:expr) => {
282            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
283            #[allow(unsafe_code)]
284            unsafe {
285                asm! {
286                    "cmp {cond:w}, 0",
287                    "csel {lhs:w}, {rhs:w}, {lhs:w}, NE",
288                    cond = in(reg) $selector,
289                    lhs = inlateout(reg) *$lhs,
290                    rhs = in(reg) *$rhs,
291                    options(nostack),
292                };
293            }
294        };
295    }
296
297    /// This macro implements `Select` for public integer type `$ty`
298    /// and its secret version `$secret_ty`. `$select` should be one
299    /// of `select32` and `select64`, determining the used register
300    /// width.
301    macro_rules! impl_select {
302        ($ty:ty, $secret_ty:ty, $select: ident) => {
303            impl Select for $ty {
304                #[inline]
305                fn select(&mut self, other: &Self, selector: U8) {
306                    $select!(self, other, selector.declassify());
307                }
308            }
309
310            #[cfg(feature = "check-secret-independence")]
311            impl Select for $secret_ty {
312                #[inline]
313                fn select(&mut self, other: &Self, selector: U8) {
314                    let lhs = self.declassify_ref_mut();
315                    let rhs = other.declassify_ref();
316                    lhs.select(rhs, selector);
317                }
318            }
319        };
320    }
321
322    impl<T: Select> Select for [T] {
323        #[inline]
324        fn select(&mut self, other: &Self, selector: U8) {
325            debug_assert_eq!(self.len(), other.len());
326            for i in 0..self.len() {
327                (&mut self[i]).select(&other[i], selector);
328            }
329        }
330    }
331
332    impl_select!(u8, U8, select32);
333    impl_select!(u16, U16, select32);
334    impl_select!(u32, U32, select32);
335    impl_select!(u64, U64, select64);
336
337    macro_rules! swap64 {
338        ($lhs:expr, $rhs:expr, $selector:expr) => {
339            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
340            #[allow(unsafe_code)]
341            unsafe {
342                asm! {
343                    "cmp {cond:w}, 0",
344                    "csel {tmp}, {b:x}, {a:x}, NE",
345                    "csel {b:x}, {a:x}, {b:x}, NE",
346                    "mov {a:x}, {tmp}",
347                    cond = in(reg) $selector,
348                    a = inout(reg) *$lhs,
349                    b = inout(reg) *$rhs,
350                    tmp = out(reg) _,
351                    options(nostack),
352                };
353            }
354        };
355    }
356
357    macro_rules! swap32 {
358        ($lhs:expr, $rhs:expr, $selector:expr) => {
359            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
360            #[allow(unsafe_code)]
361            unsafe {
362                asm! {
363                    "cmp {cond:w}, 0",
364                    "csel {tmp:w}, {b:w}, {a:w}, NE",
365                    "csel {b:w}, {a:w}, {b:w}, NE",
366                    "mov {a:w}, {tmp:w}",
367                    cond = in(reg) $selector,
368                    a = inout(reg) *$lhs,
369                    b = inout(reg) *$rhs,
370                    tmp = out(reg) _,
371                    options(nostack),
372                };
373            }
374        };
375    }
376
377    /// This macro implements `Swap` for public integer type
378    /// `$ty` and its secret version `$secret_ty`. `$swap` should be one
379    /// of `swap32` and `swap64`, determining the used register
380    /// width.
381    macro_rules! impl_swap {
382        ($ty:ty, $secret_ty:ty, $swap:ident) => {
383            impl Swap for $ty {
384                #[inline]
385                fn cswap(&mut self, other: &mut Self, selector: U8) {
386                    $swap!(self, other, selector.declassify());
387                }
388            }
389
390            #[cfg(feature = "check-secret-independence")]
391            impl Swap for $secret_ty {
392                #[inline]
393                fn cswap(&mut self, other: &mut Self, selector: U8) {
394                    let lhs = self.declassify_ref_mut();
395                    let rhs = other.declassify_ref_mut();
396                    lhs.cswap(rhs, selector);
397                }
398            }
399        };
400    }
401
402    impl_swap!(u8, U8, swap32);
403    impl_swap!(u16, U16, swap32);
404    impl_swap!(u32, U32, swap32);
405    impl_swap!(u64, U64, swap64);
406
407    impl<T: Swap> Swap for [T] {
408        #[inline]
409        fn cswap(&mut self, other: &mut Self, selector: U8) {
410            for i in 0..self.len() {
411                (&mut self[i]).cswap(&mut other[i], selector);
412            }
413        }
414    }
415}
416
417#[cfg(test)]
418mod select {
419    extern crate std;
420    use core::fmt::Debug;
421
422    use super::*;
423    use rand::{rng, Fill, Rng, RngCore};
424
425    fn test<T: Classify + Default + Copy + PartialEq + Eq + Debug>(rng: &mut impl RngCore)
426    where
427        [T]: Fill + Select,
428    {
429        let selector: u8 = rng.random::<u8>() & 1;
430        // XXX: Setting `selector` as follows will break the selection in release mode on `aarch64`.
431        // let selector = if selector { 0 } else { rng.next_u32() as u8 };
432        let mut lhs = [T::default(); 256];
433        rng.fill(&mut lhs);
434        let mut rhs = [T::default(); 256];
435        rng.fill(&mut rhs);
436
437        let expected = if selector == 0 {
438            lhs.clone()
439        } else {
440            rhs.clone()
441        };
442
443        lhs.select(&rhs, selector.classify());
444
445        assert_eq!(
446            lhs, expected,
447            "\nother: {:?}\nselector: {}\n",
448            rhs, selector
449        );
450    }
451
452    #[test]
453    fn correctness() {
454        let mut rng = rng();
455        const ITERATIONS: usize = 1_000;
456
457        for _ in 0..ITERATIONS {
458            test::<u8>(&mut rng);
459            test::<u16>(&mut rng);
460            test::<u32>(&mut rng);
461            test::<u64>(&mut rng);
462        }
463    }
464
465    #[test]
466    #[cfg(feature = "check-secret-independence")]
467    fn correctness_secret() {
468        macro_rules! secret_test {
469            ($ty:ty, $rng:expr) => {{
470                let selector: u8 = $rng.random::<u8>() & 1;
471                // XXX: Setting `selector` as follows will break the selection in release mode on `aarch64`.
472                // let selector = if selector { 0 } else { rng.next_u32() as u8 };
473                let mut lhs = [<$ty>::default(); 256];
474                $rng.fill(&mut lhs);
475                let mut rhs = [<$ty>::default(); 256];
476                $rng.fill(&mut rhs);
477
478                let expected = if selector == 0 {
479                    lhs.clone()
480                } else {
481                    rhs.clone()
482                };
483
484                let mut lhs = lhs.classify();
485                let rhs = rhs.classify();
486
487                lhs.select(&rhs, selector.classify());
488
489                assert_eq!(
490                    lhs.declassify(),
491                    expected,
492                    "\nother: {:?}\nselector: {}\n",
493                    rhs.declassify(),
494                    selector
495                );
496            }};
497        }
498        let mut rng = rng();
499        const ITERATIONS: usize = 1_000;
500
501        for _ in 0..ITERATIONS {
502            secret_test!(u8, rng);
503            secret_test!(u16, rng);
504            secret_test!(u32, rng);
505            secret_test!(u64, rng);
506        }
507    }
508}
509
510#[cfg(test)]
511mod swap {
512    extern crate std;
513    use core::fmt::Debug;
514
515    use super::*;
516    use rand::{rng, Fill, Rng, RngCore};
517
518    /// Test swap on public integers.
519    fn test<T: Default + Copy + PartialEq + Eq + Debug>(rng: &mut impl RngCore)
520    where
521        [T]: Fill + Swap,
522    {
523        let selector = rng.random::<u8>() & 1;
524        // XXX: Setting `selector` as follows will break the swap in release mode on `aarch64`.
525        // let selector = if selector { 0 } else { rng.next_u32() as u8 };
526        let mut lhs = [T::default(); 256];
527        rng.fill(&mut lhs);
528        let mut rhs = [T::default(); 256];
529        rng.fill(&mut rhs);
530
531        let (expected_lhs, expected_rhs) = if selector == 0 {
532            (lhs.clone(), rhs.clone())
533        } else {
534            (rhs.clone(), lhs.clone())
535        };
536
537        lhs.cswap(&mut rhs, selector.classify());
538
539        assert_eq!(lhs, expected_lhs, "\nlhs / selector: {}\n", selector);
540        assert_eq!(rhs, expected_rhs, "\nrhs / selector: {}\n", selector);
541    }
542
543    #[test]
544    fn correctness() {
545        let mut rng = rng();
546        const ITERATIONS: usize = 1_000;
547
548        for _ in 0..ITERATIONS {
549            test::<u8>(&mut rng);
550            test::<u16>(&mut rng);
551            test::<u32>(&mut rng);
552            test::<u64>(&mut rng);
553        }
554    }
555
556    /// Test swap on secret integers.
557    #[test]
558    #[cfg(feature = "check-secret-independence")]
559    fn correctness_secret() {
560        macro_rules! secret_test {
561            ($ty:ty, $rng:expr) => {{
562                let selector = $rng.random::<u8>() & 1;
563                // XXX: Setting `selector` as follows will break the swap in release mode on `aarch64`.
564                // let selector = if selector { 0 } else { $rng.next_u32() as u8 };
565                let mut lhs = [<$ty>::default(); 256];
566                $rng.fill(&mut lhs);
567                let mut rhs = [<$ty>::default(); 256];
568                $rng.fill(&mut rhs);
569
570                let (expected_lhs, expected_rhs) = if selector == 0 {
571                    (lhs.clone(), rhs.clone())
572                } else {
573                    (rhs.clone(), lhs.clone())
574                };
575
576                let mut lhs = lhs.classify();
577                let mut rhs = rhs.classify();
578                lhs.cswap(&mut rhs, selector.classify());
579
580                assert_eq!(
581                    lhs.declassify(),
582                    expected_lhs,
583                    "\nlhs / selector: {}\n",
584                    selector
585                );
586                assert_eq!(
587                    rhs.declassify(),
588                    expected_rhs,
589                    "\nrhs / selector: {}\n",
590                    selector
591                );
592            }};
593        }
594        let mut rng = rng();
595        const ITERATIONS: usize = 1_000;
596
597        for _ in 0..ITERATIONS {
598            secret_test!(u8, rng);
599            secret_test!(u16, rng);
600            secret_test!(u32, rng);
601            secret_test!(u64, rng);
602        }
603    }
604}