|
| 1 | +//! arithmetic mod q |
| 2 | +
|
| 3 | +use crate::{ |
| 4 | + const_time::i32_mod_u14, |
| 5 | + params::{NtruCommon, NtruLRPrime}, |
| 6 | +}; |
| 7 | +use core::marker::PhantomData; |
| 8 | +use core::ops::Deref; |
| 9 | + |
| 10 | +/// always represented as `-F::Q12...F::Q12` |
| 11 | +#[derive(Copy, Clone)] |
| 12 | +pub struct Inner<Params> { |
| 13 | + inner: i16, |
| 14 | + marker: PhantomData<Params>, |
| 15 | +} |
| 16 | + |
| 17 | +impl<P> Default for Inner<P> { |
| 18 | + fn default() -> Self { |
| 19 | + Self { |
| 20 | + inner: 0, |
| 21 | + marker: PhantomData, |
| 22 | + } |
| 23 | + } |
| 24 | +} |
| 25 | +/// we need this type for the following reason, there is an expressivity |
| 26 | +/// problem, that is `FqInner<T>` implements only `Clone` and `Copy` if |
| 27 | +/// `T: Clone + Copy`. In this case, we do not require `T: Clone + Copy` |
| 28 | +/// So to bypass this we can: |
| 29 | +/// A- manually implment Clone + Copy |
| 30 | +/// B - Add Clone+Copy a trait bounds for T |
| 31 | +/// C - This trick which is saying that we use static reference to T which is always Clone + Copy |
| 32 | +/// D - Use third party crates like derivatives. |
| 33 | +pub type Fq<Params> = Inner<&'static Params>; |
| 34 | + |
| 35 | +/// the benefit is from outside, anyone can access the inner value as number, |
| 36 | +/// but no one can modify it without refreezing |
| 37 | +impl<Params> Deref for Fq<Params> { |
| 38 | + type Target = i16; |
| 39 | + |
| 40 | + fn deref(&self) -> &Self::Target { |
| 41 | + &self.inner |
| 42 | + } |
| 43 | +} |
| 44 | +// TODO should we have `T: Clone + Copy` or should we specify |
| 45 | +// trait bounds for the derive (either by manual |
| 46 | +// implementation or via derivative) |
| 47 | +impl<Params: NtruCommon> Fq<Params> { |
| 48 | + const Q12: u16 = ((Params::Q - 1) / 2); |
| 49 | + pub(super) fn new_i32(n: i32) -> Self { |
| 50 | + debug_assert!(n < Self::Q12 as i32); |
| 51 | + debug_assert!(n > -(Self::Q12 as i32)); |
| 52 | + Fq { |
| 53 | + inner: n as i16, |
| 54 | + marker: PhantomData, |
| 55 | + } |
| 56 | + } |
| 57 | + pub(super) fn new_i16(n: i16) -> Self { |
| 58 | + debug_assert!(n < Self::Q12 as i16); |
| 59 | + debug_assert!(n > -(Self::Q12 as i16)); |
| 60 | + Fq { |
| 61 | + inner: n, |
| 62 | + marker: PhantomData, |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + pub(super) fn new_i8(n: i8) -> Self { |
| 67 | + let n = n as i16; |
| 68 | + debug_assert!(n < Self::Q12 as i16); |
| 69 | + debug_assert!(n > -(Self::Q12 as i16)); |
| 70 | + Fq { |
| 71 | + inner: n, |
| 72 | + marker: PhantomData, |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + /// x must not be close to top int32 |
| 77 | + #[must_use] |
| 78 | + pub const fn freeze(x: i32) -> Self { |
| 79 | + debug_assert!(x <= i32::MAX - Self::Q12 as i32); |
| 80 | + Fq { |
| 81 | + inner: i32_mod_u14(x + Self::Q12 as i32, Params::Q).wrapping_sub(Self::Q12) as i16, |
| 82 | + marker: PhantomData, |
| 83 | + } |
| 84 | + } |
| 85 | + /// caclucates the multiplicative inverse of a1 |
| 86 | + /// a1 must not be zero |
| 87 | + #[must_use] |
| 88 | + pub const fn recip(a1: Self) -> Self { |
| 89 | + debug_assert!(a1.inner != 0); |
| 90 | + let mut i = 1; |
| 91 | + let mut ai = a1; |
| 92 | + while i < Params::Q - 2 { |
| 93 | + // we have to use `a1.0` instead of deref to maintian |
| 94 | + // the const status of the function |
| 95 | + ai = Fq::freeze(a1.inner as i32 * ai.inner as i32); |
| 96 | + i += 1; |
| 97 | + } |
| 98 | + ai |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +///TODO tests for both funtions |
| 103 | +impl<Params: NtruLRPrime + NtruCommon> Fq<Params> { |
| 104 | + #[must_use] |
| 105 | + pub const fn top(self) -> i8 { |
| 106 | + ((Params::TAU1 * (self.inner + Params::TAU0) as i32 + 16384) >> 15) as i8 |
| 107 | + } |
| 108 | + #[must_use] |
| 109 | + pub const fn right(t: i8) -> Self { |
| 110 | + Fq::freeze(Params::TAU3 * t as i32 - Params::TAU2) |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +#[cfg(test)] |
| 115 | +mod test { |
| 116 | + use super::Fq; |
| 117 | + use crate::params::*; |
| 118 | + use rayon::prelude::*; |
| 119 | + use std::io::{stdout, Write}; |
| 120 | + |
| 121 | + fn naive_freeze(x: i32, q: u16) -> i16 { |
| 122 | + let res = (x % (q as i32)) as i16; |
| 123 | + if res > ((q as i16 - 1) / 2) { |
| 124 | + return res - q as i16; |
| 125 | + } |
| 126 | + if res < -((q as i16 - 1) / 2) { |
| 127 | + return res + q as i16; |
| 128 | + } |
| 129 | + res |
| 130 | + } |
| 131 | + #[test] |
| 132 | + #[ignore = "Expected to take ~ 1 hour to finish on single core"] |
| 133 | + fn test_fq_freezer() { |
| 134 | + // if i is close to i32::Max we overflow and crash |
| 135 | + // we also need to chunk things a bit |
| 136 | + (i32::MIN..i32::MAX - S1277::Q as i32) |
| 137 | + .into_par_iter() |
| 138 | + .chunks(0xffffff) |
| 139 | + .for_each(|chunk| { |
| 140 | + print!("."); |
| 141 | + stdout().flush().unwrap(); |
| 142 | + for i in chunk { |
| 143 | + // all viable Q values from section 3.4 of NTRU NIST submission |
| 144 | + assert_eq!(*Fq::<S653>::freeze(i), naive_freeze(i, S653::Q)); |
| 145 | + assert_eq!(*Fq::<S761>::freeze(i), naive_freeze(i, S761::Q)); |
| 146 | + assert_eq!(*Fq::<S857>::freeze(i), naive_freeze(i, S857::Q)); |
| 147 | + assert_eq!(*Fq::<S953>::freeze(i), naive_freeze(i, S953::Q)); |
| 148 | + assert_eq!(*Fq::<S1013>::freeze(i), naive_freeze(i, S1013::Q)); |
| 149 | + assert_eq!(*Fq::<S1277>::freeze(i), naive_freeze(i, S1277::Q)); |
| 150 | + } |
| 151 | + }) |
| 152 | + } |
| 153 | + #[test] |
| 154 | + fn test_f_s653_recip() { |
| 155 | + // note that zero has no recip, so we skip zero |
| 156 | + for i in (-(Fq::<S653>::Q12 as i32)..0).chain(1..Fq::<S653>::Q12 as i32) { |
| 157 | + assert_eq!( |
| 158 | + *Fq::<S653>::freeze(i * *Fq::<S653>::recip(Fq::<S653>::freeze(i)) as i32), |
| 159 | + 1 |
| 160 | + ) |
| 161 | + } |
| 162 | + } |
| 163 | + #[test] |
| 164 | + fn test_f_s761_recip() { |
| 165 | + // note that zero has no recip, so we skip zero |
| 166 | + for i in (-(Fq::<S761>::Q12 as i32)..0).chain(1..Fq::<S761>::Q12 as i32) { |
| 167 | + assert_eq!( |
| 168 | + *Fq::<S761>::freeze(i * *Fq::<S761>::recip(Fq::<S761>::freeze(i)) as i32), |
| 169 | + 1 |
| 170 | + ) |
| 171 | + } |
| 172 | + } |
| 173 | + #[test] |
| 174 | + fn test_f_s857_recip() { |
| 175 | + // note that zero has no recip, so we skip zero |
| 176 | + for i in (-(Fq::<S857>::Q12 as i32)..0).chain(1..Fq::<S857>::Q12 as i32) { |
| 177 | + assert_eq!( |
| 178 | + *Fq::<S857>::freeze(i * *Fq::<S857>::recip(Fq::<S857>::freeze(i)) as i32), |
| 179 | + 1 |
| 180 | + ) |
| 181 | + } |
| 182 | + } |
| 183 | + #[test] |
| 184 | + fn test_f_s953_recip() { |
| 185 | + // note that zero has no recip, so we skip zero |
| 186 | + for i in (-(Fq::<S953>::Q12 as i32)..0).chain(1..Fq::<S953>::Q12 as i32) { |
| 187 | + assert_eq!( |
| 188 | + *Fq::<S953>::freeze(i * *Fq::<S953>::recip(Fq::<S953>::freeze(i)) as i32), |
| 189 | + 1 |
| 190 | + ) |
| 191 | + } |
| 192 | + } |
| 193 | + #[test] |
| 194 | + fn test_f_s1013_recip() { |
| 195 | + // note that zero has no recip, so we skip zero |
| 196 | + for i in (-(Fq::<S1013>::Q12 as i32)..0).chain(1..Fq::<S1013>::Q12 as i32) { |
| 197 | + assert_eq!( |
| 198 | + *Fq::<S1013>::freeze(i * *Fq::<S1013>::recip(Fq::<S1013>::freeze(i)) as i32), |
| 199 | + 1 |
| 200 | + ) |
| 201 | + } |
| 202 | + } |
| 203 | + #[test] |
| 204 | + fn test_f_s1277_recip() { |
| 205 | + // note that zero has no recip, so we skip zero |
| 206 | + for i in (-(Fq::<S1277>::Q12 as i32)..0).chain(1..Fq::<S1277>::Q12 as i32) { |
| 207 | + assert_eq!( |
| 208 | + *Fq::<S1277>::freeze(i * *Fq::<S1277>::recip(Fq::<S1277>::freeze(i)) as i32), |
| 209 | + 1 |
| 210 | + ) |
| 211 | + } |
| 212 | + } |
| 213 | +} |
0 commit comments