Skip to main content

ctutils/traits/
ct_neg.rs

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