Skip to main content

ctutils/traits/
ct_neg.rs

1use crate::{Choice, CtAssign, CtSelect};
2use core::num::{
3    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32,
4    NonZeroU64, NonZeroU128,
5};
6
7/// Constant-time conditional negation: negates a value when `choice` is [`Choice::TRUE`].
8pub trait CtNeg: Sized {
9    /// Conditionally negate `self`, returning `-self` if `choice` is [`Choice::TRUE`], or `self`
10    /// otherwise.
11    #[must_use]
12    fn ct_neg(&self, choice: Choice) -> Self;
13
14    /// Conditionally negate `self` in-place, replacing it with `-self` if `choice` is
15    /// [`Choice::TRUE`].
16    fn ct_neg_assign(&mut self, choice: Choice) {
17        *self = self.ct_neg(choice);
18    }
19}
20
21// Impl `CtNeg` for a signed integer (`i*`) type which impls `CtSelect`
22macro_rules! impl_signed_ct_neg {
23    ( $($int:ty),+ ) => {
24        $(
25            impl CtNeg for $int {
26                #[inline]
27                fn ct_neg(&self, choice: Choice) -> Self {
28                    self.ct_select(&-*self, choice)
29                }
30
31                #[inline]
32                fn ct_neg_assign(&mut self, choice: Choice) {
33                    self.ct_assign(&-*self, choice)
34                }
35            }
36        )+
37    };
38}
39
40// Impl `CtNeg` for an unsigned integer (`u*`) type which impls `CtSelect`
41macro_rules! impl_unsigned_ct_neg {
42    ( $($uint:ty),+ ) => {
43        $(
44            impl CtNeg for $uint {
45                #[inline]
46                fn ct_neg(&self, choice: Choice) -> Self {
47                    self.ct_select(&self.wrapping_neg(), choice)
48                }
49
50                #[inline]
51                fn ct_neg_assign(&mut self, choice: Choice) {
52                    self.ct_assign(&self.wrapping_neg(), choice)
53                }
54            }
55        )+
56    };
57}
58
59impl_signed_ct_neg!(
60    i8,
61    i16,
62    i32,
63    i64,
64    i128,
65    NonZeroI8,
66    NonZeroI16,
67    NonZeroI32,
68    NonZeroI64,
69    NonZeroI128
70);
71impl_unsigned_ct_neg!(u8, u16, u32, u64, u128);
72
73/// Unfortunately `NonZeroU*` doesn't support `wrapping_neg` for some reason (but `NonZeroI*` does),
74/// even though the wrapping negation of any non-zero integer should also be non-zero.
75///
76/// So we need a special case just for `NonZeroU*`, at least for now.
77macro_rules! impl_ct_neg_for_unsigned_nonzero {
78    ( $($nzuint:ident),+ ) => {
79        $(
80            impl CtNeg for $nzuint {
81                #[inline]
82                fn ct_neg(&self, choice: Choice) -> Self {
83                    // TODO(tarcieri): use `NonZero::wrapping_neg` if it becomes available
84                    let n = self.get().ct_select(&self.get().wrapping_neg(), choice);
85                    $nzuint::new(n).expect("should be non-zero")
86                }
87            }
88        )+
89    };
90}
91
92impl_ct_neg_for_unsigned_nonzero!(NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128);
93
94#[cfg(test)]
95mod tests {
96    /// Test `CtNeg` impl on `i*`
97    macro_rules! signed_ct_neg_tests {
98         ( $($int:ident),+ ) => {
99             $(
100                mod $int {
101                    use crate::{Choice, CtNeg};
102
103                    #[test]
104                    fn ct_neg() {
105                        let n: $int = 42;
106                        assert_eq!(n, n.ct_neg(Choice::FALSE));
107                        assert_eq!(-n, n.ct_neg(Choice::TRUE));
108                    }
109
110                    #[test]
111                    fn ct_neg_assign() {
112                        let n: $int = 42;
113                        let mut x = n;
114                        x.ct_neg_assign(Choice::FALSE);
115                        assert_eq!(n, x);
116
117                        x.ct_neg_assign(Choice::TRUE);
118                        assert_eq!(-n, x);
119                    }
120                }
121             )+
122        };
123    }
124
125    /// Test `CtNeg` impl on `u*`
126    macro_rules! unsigned_ct_neg_tests {
127         ( $($uint:ident),+ ) => {
128             $(
129                mod $uint {
130                    use crate::{Choice, CtNeg};
131
132                    #[test]
133                    fn u32_ct_neg() {
134                        let n: $uint = 42;
135                        assert_eq!(n, n.ct_neg(Choice::FALSE));
136                        assert_eq!(<$uint>::MAX - n + 1, n.ct_neg(Choice::TRUE));
137                    }
138
139                    #[test]
140                    fn u32_ct_neg_assign() {
141                        let n: $uint = 42;
142                        let mut x = n;
143                        x.ct_neg_assign(Choice::FALSE);
144                        assert_eq!(n, x);
145
146                        x.ct_neg_assign(Choice::TRUE);
147                        assert_eq!(<$uint>::MAX - n + 1, x);
148                    }
149                }
150             )+
151        };
152    }
153
154    signed_ct_neg_tests!(i8, i16, i32, i64, i128);
155    unsigned_ct_neg_tests!(u8, u16, u32, u64, u128);
156}