Files
cryptonum/old/unsigned/barrett.rs
Adam Wick fa872c951a Start experimenting with full generation of all of the numeric types.
Previously, we used a little bit of generation to drive a lot of Rust
macros. This works, but it's a little confusing to read and write. In
addition, we used a lot of implementations with variable timings based
on their input, which isn't great for crypto. This is the start of an
attempt to just generate all of the relevant Rust code directly, and to
use timing-channel resistant implementations for most of the routines.
2019-07-15 17:39:06 -07:00

173 lines
6.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
macro_rules! barrett_impl {
($bar: ident, $name: ident, $name64: ident, $dbl: ident, $dbl64: ident) => {
#[derive(Clone,PartialEq)]
pub struct $bar {
pub(crate) k: usize,
pub(crate) m: $name64,
pub(crate) mu: $name64
}
impl $bar {
/// Generate a new Barrett number from the given input. This operation
/// is relatively slow, so you should only do it if you plan to use
/// reduce() multiple times with the ame number.
pub fn new(m: $name) -> $bar {
// Step #1: Figure out k
let mut k = 0;
for i in 0..m.value.len() {
if m.value[i] != 0 {
k = i;
}
}
k += 1;
// Step #2: Compute b
let mut b = $dbl64::zero();
b.value[2*k] = 1;
// Step #3: Divide b by m.
let bigm = $dbl64::from(&m);
let quot = b / &bigm;
let resm = $name64::from(&m);
let mu = $name64::from(&quot);
// Done!
$bar { k: k, m: resm, mu: mu }
}
/// Generate a new Barrett number from its component parts. You
/// probably don't want to use it; it's mostly here for debugging
/// purposes, so that tests won't have to compute this all the
/// time.
pub fn from_components(k: usize, m: $name64, mu: $name64) -> $bar {
$bar { k: k, m: m, mu: mu }
}
// Reduce the input by this value; in other words, perform a mod
// operation.
#[inline(always)]
pub fn reduce(&self, x: &$dbl) -> $name {
// 1. q1←⌊x/bk1⌋, q2←q1 · μ, q3←⌊q2/bk+1⌋.
let q1: $name64 = $name64::from(x >> ((self.k - 1) * 64));
let q2 = q1 * &self.mu;
let q3: $name64 = $name64::from(q2 >> ((self.k + 1) * 64));
// 2. r1←x mod bk+1, r2←q3 · m mod bk+1, r←r1 r2.
let mut r: $dbl64 = $dbl64::from(x);
r.mask(self.k + 1);
let mut r2: $dbl64 = $dbl64::from(q3 * &self.m);
r2.mask(self.k + 1);
let went_negative = &r < &r2;
r -= &r2;
// 3. If r<0 then r←r+bk+1.
if went_negative {
let mut bk1 = $dbl64::zero();
bk1.value[self.k+1] = 1;
// this may overflow, and we should probably be OK with it.
r += &bk1;
}
// 4. While r≥m do: r←rm.
let m2 = $dbl64::from(&self.m);
while &r > &m2 {
r -= &m2;
}
// Done!
$name::from(&r)
}
}
impl $name {
/// Generate a Barrett number from this number.
pub fn generate_barrett(&self) -> $bar {
$bar::new(self.clone())
}
#[cfg(test)]
pub(crate) fn new_barrett(k: usize, m: $name64, mu: $name64) -> $bar {
$bar{ k: k, m: m, mu: mu }
}
}
impl fmt::Debug for $bar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($name))
.field("k", &self.k)
.field("m", &self.m)
.field("mu", &self.mu)
.finish()
}
}
};
}
#[cfg(test)]
macro_rules! generate_barrett_gen_tests {
($name: ident, $lname: ident, $bname: ident) => {
#[test]
fn $lname() {
generate_barrett_gen_tests!(body $name, $lname, $bname);
}
};
(ignore $name: ident, $lname: ident, $bname: ident) => {
#[test]
#[ignore]
fn $lname() {
generate_barrett_gen_tests!(body $name, $lname, $bname);
}
};
(body $name: ident, $lname: ident, $bname: ident) => {
let fname = build_test_path("barrett_gen", stringify!($name));
run_test(fname.to_string(), 3, |case| {
let (neg0, mbytes) = case.get("m").unwrap();
let (neg1, kbytes) = case.get("k").unwrap();
let (neg2, ubytes) = case.get("u").unwrap();
assert!(!neg0 && !neg1 && !neg2);
let m = $name::from_bytes(mbytes);
let kbig = $name::from_bytes(kbytes);
let mu = $bname::from_bytes(ubytes);
//
let mbig = $bname::from(&m);
let k = usize::from(&kbig);
//
let bar = m.generate_barrett();
assert_eq!(k, bar.k);
assert_eq!(mbig, bar.m);
assert_eq!(mu, bar.mu);
});
};
}
#[cfg(test)]
macro_rules! generate_barrett_red_tests {
($name: ident, $lname: ident, $bname: ident, $dbl: ident) => {
#[test]
fn $lname() {
generate_barrett_red_tests!(body $name, $lname, $bname, $dbl);
}
};
(ignore $name: ident, $lname: ident, $bname: ident, $dbl: ident) => {
#[test]
#[ignore]
fn $lname() {
generate_barrett_red_tests!(body $name, $lname, $bname, $dbl);
}
};
(body $name: ident, $lname: ident, $bname: ident, $dbl: ident) => {
let fname = build_test_path("barrett_reduce", stringify!($name));
run_test(fname.to_string(), 5, |case| {
let (neg0, mbytes) = case.get("m").unwrap();
let (neg1, kbytes) = case.get("k").unwrap();
let (neg2, ubytes) = case.get("u").unwrap();
let (neg3, xbytes) = case.get("x").unwrap();
let (neg4, rbytes) = case.get("r").unwrap();
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4);
let m = $name::from_bytes(mbytes);
let kbig = $name::from_bytes(kbytes);
let k = usize::from(&kbig);
let mu = $bname::from_bytes(ubytes);
let bar = $name::new_barrett(usize::from(k), $bname::from(m), mu);
let x = $dbl::from_bytes(xbytes);
let r = $name::from_bytes(rbytes);
//
assert_eq!(r, bar.reduce(&x));
});
};
}