Skip to main content

test_foo_bar_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, U8};
150    #[cfg(feature = "check-secret-independence")]
151    use crate::{traits::*, U16, U32, U64};
152
153    // Don't inline this to avoid that the compiler optimizes this out.
154    #[inline(never)]
155    fn is_non_zero_32(selector: u8) -> u32 {
156        core::hint::black_box(
157            ((!(core::hint::black_box(selector) as u32)).wrapping_add(1) >> 31) & 1,
158        )
159    }
160
161    // Don't inline this to avoid that the compiler optimizes this out.
162    #[inline(never)]
163    fn is_non_zero_64(selector: u8) -> u64 {
164        core::hint::black_box(
165            ((!(core::hint::black_box(selector) as u64)).wrapping_add(1) >> 63) & 1,
166        )
167    }
168
169    /// This macro implements `Select` for public integer type
170    /// `&[$ty]` and its secret version `&[$secret_ty]`.
171    macro_rules! impl_select {
172        ($ty:ty, $secret_ty:ty, $is_non_zero:ident) => {
173            impl Select for [$ty] {
174                fn select(&mut self, other: &Self, selector: crate::U8) {
175                    let mask = ($is_non_zero(selector.declassify()) as $ty).wrapping_sub(1);
176                    for i in 0..self.len() {
177                        self[i] = (self[i] & mask) | (other[i] & !mask);
178                    }
179                }
180            }
181
182            #[cfg(feature = "check-secret-independence")]
183            impl Select for [$secret_ty] {
184                fn select(&mut self, other: &Self, selector: crate::U8) {
185                    let lhs = self.declassify_ref_mut();
186                    let rhs = other.declassify_ref();
187                    lhs.select(rhs, selector);
188                }
189            }
190        };
191    }
192
193    impl_select!(u8, U8, is_non_zero_32);
194    impl_select!(u16, U16, is_non_zero_32);
195    impl_select!(u32, U32, is_non_zero_32);
196    impl_select!(u64, U64, is_non_zero_64);
197
198    macro_rules! swap64 {
199        ($t:ty, $lhs:expr, $rhs:expr, $selector:expr) => {
200            let mask = core::hint::black_box(
201                ((((!($selector as u64)).wrapping_add(1) >> 63) & 1) as $t).wrapping_sub(1),
202            );
203            for i in 0..$lhs.len() {
204                let dummy = !mask & ($lhs[i] ^ $rhs[i]);
205                $lhs[i] ^= dummy;
206                $rhs[i] ^= dummy;
207            }
208        };
209    }
210
211    macro_rules! swap32 {
212        ($t:ty, $lhs:expr, $rhs:expr, $selector:expr) => {
213            let mask = core::hint::black_box(
214                ((((!(core::hint::black_box($selector) as u32)).wrapping_add(1) >> 31) & 1) as $t)
215                    .wrapping_sub(1),
216            );
217            for i in 0..$lhs.len() {
218                let dummy = !mask & ($lhs[i] ^ $rhs[i]);
219                $lhs[i] ^= dummy;
220                $rhs[i] ^= dummy;
221            }
222        };
223    }
224
225    /// This macro implements `Swap` for public integer type
226    /// `&[$ty]` and its secret version `&[$secret_ty]`.
227    macro_rules! impl_swap {
228        ($ty:ty, $secret_ty:ty, $swap:ident) => {
229            impl Swap for [$ty] {
230                #[inline]
231                fn cswap(&mut self, other: &mut Self, selector: U8) {
232                    debug_assert_eq!(self.len(), other.len());
233                    $swap!($ty, self, other, selector.declassify());
234                }
235            }
236
237            #[cfg(feature = "check-secret-independence")]
238            impl Swap for [$secret_ty] {
239                #[inline]
240                fn cswap(&mut self, other: &mut Self, selector: U8) {
241                    debug_assert_eq!(self.len(), other.len());
242                    let lhs = self.declassify_ref_mut();
243                    let rhs = other.declassify_ref_mut();
244                    $swap!($ty, lhs, rhs, selector.declassify());
245                }
246            }
247        };
248    }
249
250    impl_swap!(u8, U8, swap32);
251    impl_swap!(u16, U16, swap32);
252    impl_swap!(u32, U32, swap32);
253    impl_swap!(u64, U64, swap64);
254}
255
256#[cfg(all(not(hax), target_arch = "aarch64"))]
257mod aarch64 {
258    use core::arch::asm;
259
260    use super::*;
261
262    macro_rules! select64 {
263        ($lhs:expr, $rhs:expr, $selector:expr) => {
264            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
265            #[allow(unsafe_code)]
266            unsafe {
267                asm! {
268                    "cmp {cond:w}, 0",
269                    "csel {lhs:x}, {rhs:x}, {lhs:x}, NE",
270                    cond = in(reg) $selector,
271                    lhs = inlateout(reg) *$lhs,
272                    rhs = in(reg) *$rhs,
273                    options(nostack),
274                };
275            }
276        };
277    }
278
279    macro_rules! select32 {
280        ($lhs:expr, $rhs:expr, $selector:expr) => {
281            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
282            #[allow(unsafe_code)]
283            unsafe {
284                asm! {
285                    "cmp {cond:w}, 0",
286                    "csel {lhs:w}, {rhs:w}, {lhs:w}, NE",
287                    cond = in(reg) $selector,
288                    lhs = inlateout(reg) *$lhs,
289                    rhs = in(reg) *$rhs,
290                    options(nostack),
291                };
292            }
293        };
294    }
295
296    /// This macro implements `Select` for public integer type `$ty`
297    /// and its secret version `$secret_ty`. `$select` should be one
298    /// of `select32` and `select64`, determining the used register
299    /// width.
300    macro_rules! impl_select {
301        ($ty:ty, $secret_ty:ty, $select:ident) => {
302            impl Select for $ty {
303                #[inline]
304                fn select(&mut self, other: &Self, selector: U8) {
305                    $select!(self, other, selector.declassify());
306                }
307            }
308
309            #[cfg(feature = "check-secret-independence")]
310            impl Select for $secret_ty {
311                #[inline]
312                fn select(&mut self, other: &Self, selector: U8) {
313                    let lhs = self.declassify_ref_mut();
314                    let rhs = other.declassify_ref();
315                    lhs.select(rhs, selector);
316                }
317            }
318        };
319    }
320
321    impl<T: Select> Select for [T] {
322        #[inline]
323        fn select(&mut self, other: &Self, selector: U8) {
324            debug_assert_eq!(self.len(), other.len());
325            for i in 0..self.len() {
326                (&mut self[i]).select(&other[i], selector);
327            }
328        }
329    }
330
331    impl_select!(u8, U8, select32);
332    impl_select!(u16, U16, select32);
333    impl_select!(u32, U32, select32);
334    impl_select!(u64, U64, select64);
335
336    macro_rules! swap64 {
337        ($lhs:expr, $rhs:expr, $selector:expr) => {
338            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
339            #[allow(unsafe_code)]
340            unsafe {
341                asm! {
342                    "cmp {cond:w}, 0",
343                    "csel {tmp}, {b:x}, {a:x}, NE",
344                    "csel {b:x}, {a:x}, {b:x}, NE",
345                    "mov {a:x}, {tmp}",
346                    cond = in(reg) $selector,
347                    a = inout(reg) *$lhs,
348                    b = inout(reg) *$rhs,
349                    tmp = out(reg) _,
350                    options(nostack),
351                };
352            }
353        };
354    }
355
356    macro_rules! swap32 {
357        ($lhs:expr, $rhs:expr, $selector:expr) => {
358            // Using https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/CSEL--Conditional-Select-
359            #[allow(unsafe_code)]
360            unsafe {
361                asm! {
362                    "cmp {cond:w}, 0",
363                    "csel {tmp:w}, {b:w}, {a:w}, NE",
364                    "csel {b:w}, {a:w}, {b:w}, NE",
365                    "mov {a:w}, {tmp:w}",
366                    cond = in(reg) $selector,
367                    a = inout(reg) *$lhs,
368                    b = inout(reg) *$rhs,
369                    tmp = out(reg) _,
370                    options(nostack),
371                };
372            }
373        };
374    }
375
376    /// This macro implements `Swap` for public integer type
377    /// `$ty` and its secret version `$secret_ty`. `$swap` should be one
378    /// of `swap32` and `swap64`, determining the used register
379    /// width.
380    macro_rules! impl_swap {
381        ($ty:ty, $secret_ty:ty, $swap:ident) => {
382            impl Swap for $ty {
383                #[inline]
384                fn cswap(&mut self, other: &mut Self, selector: U8) {
385                    $swap!(self, other, selector.declassify());
386                }
387            }
388
389            #[cfg(feature = "check-secret-independence")]
390            impl Swap for $secret_ty {
391                #[inline]
392                fn cswap(&mut self, other: &mut Self, selector: U8) {
393                    let lhs = self.declassify_ref_mut();
394                    let rhs = other.declassify_ref_mut();
395                    lhs.cswap(rhs, selector);
396                }
397            }
398        };
399    }
400
401    impl_swap!(u8, U8, swap32);
402    impl_swap!(u16, U16, swap32);
403    impl_swap!(u32, U32, swap32);
404    impl_swap!(u64, U64, swap64);
405
406    impl<T: Swap> Swap for [T] {
407        #[inline]
408        fn cswap(&mut self, other: &mut Self, selector: U8) {
409            for i in 0..self.len() {
410                (&mut self[i]).cswap(&mut other[i], selector);
411            }
412        }
413    }
414}
415
416#[cfg(test)]
417mod select {
418    extern crate std;
419    use core::fmt::Debug;
420
421    use rand::{rng, Fill, Rng, RngExt};
422
423    use super::*;
424
425    fn test<T: Classify + Default + Copy + PartialEq + Eq + Debug + Fill>(rng: &mut impl Rng)
426    where
427        [T]: 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 rand::{rng, Fill, Rng, RngExt};
516
517    use super::*;
518
519    /// Test swap on public integers.
520    fn test<T: Default + Copy + PartialEq + Eq + Debug + Fill>(rng: &mut impl Rng)
521    where
522        [T]: Swap,
523    {
524        let selector = rng.random::<u8>() & 1;
525        // XXX: Setting `selector` as follows will break the swap in release mode on `aarch64`.
526        // let selector = if selector { 0 } else { rng.next_u32() as u8 };
527        let mut lhs = [T::default(); 256];
528        rng.fill(&mut lhs);
529        let mut rhs = [T::default(); 256];
530        rng.fill(&mut rhs);
531
532        let (expected_lhs, expected_rhs) = if selector == 0 {
533            (lhs.clone(), rhs.clone())
534        } else {
535            (rhs.clone(), lhs.clone())
536        };
537
538        lhs.cswap(&mut rhs, selector.classify());
539
540        assert_eq!(lhs, expected_lhs, "\nlhs / selector: {}\n", selector);
541        assert_eq!(rhs, expected_rhs, "\nrhs / selector: {}\n", selector);
542    }
543
544    #[test]
545    fn correctness() {
546        let mut rng = rng();
547        const ITERATIONS: usize = 1_000;
548
549        for _ in 0..ITERATIONS {
550            test::<u8>(&mut rng);
551            test::<u16>(&mut rng);
552            test::<u32>(&mut rng);
553            test::<u64>(&mut rng);
554        }
555    }
556
557    /// Test swap on secret integers.
558    #[test]
559    #[cfg(feature = "check-secret-independence")]
560    fn correctness_secret() {
561        macro_rules! secret_test {
562            ($ty:ty, $rng:expr) => {{
563                let selector = $rng.random::<u8>() & 1;
564                // XXX: Setting `selector` as follows will break the swap in release mode on `aarch64`.
565                // let selector = if selector { 0 } else { $rng.next_u32() as u8 };
566                let mut lhs = [<$ty>::default(); 256];
567                $rng.fill(&mut lhs);
568                let mut rhs = [<$ty>::default(); 256];
569                $rng.fill(&mut rhs);
570
571                let (expected_lhs, expected_rhs) = if selector == 0 {
572                    (lhs.clone(), rhs.clone())
573                } else {
574                    (rhs.clone(), lhs.clone())
575                };
576
577                let mut lhs = lhs.classify();
578                let mut rhs = rhs.classify();
579                lhs.cswap(&mut rhs, selector.classify());
580
581                assert_eq!(
582                    lhs.declassify(),
583                    expected_lhs,
584                    "\nlhs / selector: {}\n",
585                    selector
586                );
587                assert_eq!(
588                    rhs.declassify(),
589                    expected_rhs,
590                    "\nrhs / selector: {}\n",
591                    selector
592                );
593            }};
594        }
595        let mut rng = rng();
596        const ITERATIONS: usize = 1_000;
597
598        for _ in 0..ITERATIONS {
599            secret_test!(u8, rng);
600            secret_test!(u16, rng);
601            secret_test!(u32, rng);
602            secret_test!(u64, rng);
603        }
604    }
605}