Add support for Barrett reduction, which should be a faster way to do mods.

This commit is contained in:
2018-03-10 17:20:33 -08:00
parent f3d4b102c0
commit 4b90550225
4 changed files with 249 additions and 39 deletions

View File

@@ -1,10 +1,16 @@
macro_rules! construct_unsigned { macro_rules! construct_unsigned {
($type: ident, $modname: ident, $count: expr) => { ($type: ident, $barrett: ident, $modname: ident, $count: expr) => {
#[derive(Clone)] #[derive(Clone)]
pub struct $type { pub struct $type {
contents: [u64; $count] contents: [u64; $count]
} }
pub struct $barrett {
k: usize,
progenitor: $type,
contents: [u64; $count + 1]
}
impl PartialEq for $type { impl PartialEq for $type {
fn eq(&self, other: &$type) -> bool { fn eq(&self, other: &$type) -> bool {
for i in 0..$count { for i in 0..$count {
@@ -26,21 +32,15 @@ macro_rules! construct_unsigned {
} }
} }
impl $type { impl Debug for $barrett {
/// 0! fn fmt(&self, f: &mut Formatter) -> Result<(),Error> {
pub fn zero() -> $type { f.write_str("BarrettMu{{ ")?;
$type { contents: [0; $count] } f.write_fmt(format_args!("k = {}, ", self.k))?;
f.write_fmt(format_args!("progen = {:?}, ",self.progenitor))?;
f.write_str("contents: ")?;
f.debug_list().entries(self.contents.iter()).finish()?;
f.write_str(" }}")
} }
/// The maximum possible value we can hold.
pub fn max() -> $type {
$type { contents: [0xFFFFFFFFFFFFFFFF; $count] }
}
from_to!($type, $count, u8, from_u8, to_u8);
from_to!($type, $count, u16, from_u16, to_u16);
from_to!($type, $count, u32, from_u32, to_u32);
from_to!($type, $count, u64, from_u64, to_u64);
} }
impl PartialOrd for $type { impl PartialOrd for $type {
@@ -226,6 +226,38 @@ macro_rules! construct_unsigned {
} }
impl CryptoNum for $type { impl CryptoNum for $type {
type BarrettMu = $barrett;
fn zero() -> $type {
$type { contents: [0; $count] }
}
fn max_value() -> $type {
$type { contents: [0xFFFFFFFFFFFFFFFF; $count] }
}
fn is_zero(&self) -> bool {
for x in self.contents.iter() {
if *x != 0 {
return false;
}
}
true
}
fn is_odd(&self) -> bool {
(self.contents[0] & 1) == 1
}
fn is_even(&self) -> bool {
(self.contents[0] & 1) == 0
}
from_to!($type, $count, u8, from_u8, to_u8);
from_to!($type, $count, u16, from_u16, to_u16);
from_to!($type, $count, u32, from_u32, to_u32);
from_to!($type, $count, u64, from_u64, to_u64);
fn divmod(&self, a: &$type, q: &mut $type, r: &mut $type) { fn divmod(&self, a: &$type, q: &mut $type, r: &mut $type) {
generic_div(&self.contents, &a.contents, generic_div(&self.contents, &a.contents,
&mut q.contents, &mut r.contents); &mut q.contents, &mut r.contents);
@@ -266,6 +298,109 @@ macro_rules! construct_unsigned {
assert!(i == $count); assert!(i == $count);
res res
} }
fn barrett_mu(&self) -> Option<$barrett> {
// Step #0: Don't divide by 0.
if self.is_zero() {
return None
}
// Step #1: Compute k.
let mut k = $count;
while self.contents[k - 1] == 0 { k -= 1 };
// Step #2: The algorithm below only works if x has at most 2k
// digits, so if k*2 < count, abort this whole process.
if (k * 2) < $count {
return None
}
// Step #2: Compute floor(b^2k / m), where m is this value.
let mut widebody_b2k = [0; ($count * 2) + 1];
let mut widebody_self = [0; ($count * 2) + 1];
let mut quotient = [0; ($count * 2) + 1];
let mut remainder = [0; ($count * 2) + 1];
widebody_b2k[$count * 2] = 1;
for i in 0..k {
widebody_self[i] = self.contents[i];
}
generic_div(&widebody_b2k, &widebody_self,
&mut quotient, &mut remainder);
let mut result = [0; $count + 1];
for (idx, val) in quotient.iter().enumerate() {
if idx < ($count + 1) {
result[idx] = *val;
} else {
if quotient[idx] != 0 {
return None;
}
}
}
Some($barrett{k: k, progenitor: self.clone(), contents: result})
}
fn fastmod(&self, mu: &$barrett) -> $type {
// This algorithm is from our friends at the Handbook of
// Applied Cryptography, Chapter 14, Algorithm 14.42.
// Step #0:
// Expand x so that it has the same size as the Barrett
// value.
let mut x = [0; $count + 1];
for i in 0..$count {
x[i] = self.contents[i];
}
// Step #1:
// q1 <- floor(x / b^(k-1))
let mut q1 = x.clone();
generic_shr(&mut q1, &x, 64 * (mu.k - 1));
// q2 <- q1 * mu
let q2 = expanding_mul(&q1, &mu.contents);
// q3 <- floor(q2 / b^(k+1))
let mut q3big = q2.clone();
generic_shr(&mut q3big, &q2, 64 * (mu.k + 1));
let mut q3 = [0; $count + 1];
for (idx, val) in q3big.iter().enumerate() {
if idx <= $count {
q3[idx] = *val;
} else {
assert_eq!(*val, 0);
}
}
// Step #2:
// r1 <- x mod b^(k+1)
let mut r1 = x.clone();
for i in mu.k..($count+1) {
r1[i] = 0;
}
// r2 <- q3 * m mod b^(k+1)
let mut moddedm = [0; $count + 1];
for i in 0..mu.k {
moddedm[i] = mu.progenitor.contents[i];
}
let mut r2 = q3.clone();
generic_mul(&mut r2, &q3, &moddedm);
// r <- r1 - r2
let mut r = r1.clone();
generic_sub(&mut r, &r2);
let is_negative = !ge(&r1, &r2);
// Step #3:
// if r < 0 then r <- r + b^(k + 1)
if is_negative {
let mut bk1 = [0; $count + 1];
bk1[mu.k] = 1;
generic_add(&mut r, &bk1);
}
// Step #4:
// while r >= m do: r <- r - m.
while ge(&r, &moddedm) {
generic_sub(&mut r, &moddedm);
}
// Step #5:
// return r
let mut retval = [0; $count];
for i in 0..$count {
retval[i] = r[i];
}
assert_eq!(r[$count], 0);
$type{ contents: retval }
}
} }
#[cfg(test)] #[cfg(test)]
@@ -306,8 +441,8 @@ macro_rules! construct_unsigned {
fn test_max() { fn test_max() {
assert_eq!($type::from_u64(u64::max_value()).to_u64(), assert_eq!($type::from_u64(u64::max_value()).to_u64(),
u64::max_value()); u64::max_value());
assert_eq!($type::max().to_u64(), u64::max_value()); assert_eq!($type::max_value().to_u64(), u64::max_value());
assert_eq!($type::max() + $type::from_u8(1), $type::zero()); assert_eq!($type::max_value() + $type::from_u8(1), $type::zero());
} }
quickcheck! { quickcheck! {
@@ -352,10 +487,10 @@ macro_rules! construct_unsigned {
(x & $type::zero()) == $type::zero() (x & $type::zero()) == $type::zero()
} }
fn or_annulment(x: $type) -> bool { fn or_annulment(x: $type) -> bool {
(x | $type::max()) == $type::max() (x | $type::max_value()) == $type::max_value()
} }
fn and_identity(x: $type) -> bool { fn and_identity(x: $type) -> bool {
(&x & $type::max()) == x (&x & $type::max_value()) == x
} }
fn or_identity(x: $type) -> bool { fn or_identity(x: $type) -> bool {
(&x | $type::zero()) == x (&x | $type::zero()) == x
@@ -370,7 +505,7 @@ macro_rules! construct_unsigned {
(&x & &x) == x (&x & &x) == x
} }
fn or_complement(x: $type) -> bool { fn or_complement(x: $type) -> bool {
(&x | !&x) == $type::max() (&x | !&x) == $type::max_value()
} }
fn and_commutative(x: $type, y: $type) -> bool { fn and_commutative(x: $type, y: $type) -> bool {
(&x & &y) == (&y & &x) (&x & &y) == (&y & &x)
@@ -484,14 +619,14 @@ macro_rules! construct_unsigned {
quickcheck! { quickcheck! {
fn shift_mask_equivr(x: $type, in_shift: usize) -> bool { fn shift_mask_equivr(x: $type, in_shift: usize) -> bool {
let shift = in_shift % ($count * 64); let shift = in_shift % ($count * 64);
let mask = $type::max() << shift; let mask = $type::max_value() << shift;
let masked_x = &x & mask; let masked_x = &x & mask;
let shift_maskr = (x >> shift) << shift; let shift_maskr = (x >> shift) << shift;
shift_maskr == masked_x shift_maskr == masked_x
} }
fn shift_mask_equivl(x: $type, in_shift: usize) -> bool { fn shift_mask_equivl(x: $type, in_shift: usize) -> bool {
let shift = in_shift % ($count * 64); let shift = in_shift % ($count * 64);
let mask = $type::max() >> shift; let mask = $type::max_value() >> shift;
let masked_x = &x & mask; let masked_x = &x & mask;
let shift_maskl = (x << shift) >> shift; let shift_maskl = (x << shift) >> shift;
shift_maskl == masked_x shift_maskl == masked_x
@@ -537,9 +672,10 @@ macro_rules! construct_unsigned {
$type::from_u64(0)); $type::from_u64(0));
let mut buffer = [0; $count]; let mut buffer = [0; $count];
buffer[1] = 1; buffer[1] = 1;
assert_eq!($type{ contents: buffer.clone() } - $type::from_u64(1), assert_eq!($type{contents:buffer.clone()} - $type::from_u64(1),
$type::from_u64(0xFFFFFFFFFFFFFFFF)); $type::from_u64(0xFFFFFFFFFFFFFFFF));
assert_eq!($type::zero() - $type::from_u8(1), $type::max()); assert_eq!($type::zero() - $type::from_u8(1),
$type::max_value());
} }
quickcheck! { quickcheck! {
@@ -681,23 +817,32 @@ macro_rules! construct_unsigned {
a == b a == b
} }
} }
quickcheck! {
fn fastmod_works(a: $type, b: $type) -> bool {
assert!(b != $type::zero());
match b.barrett_mu() {
None =>
true,
Some(barrett) => {
a.fastmod(&barrett) == (&a % &b)
}
}
}
}
} }
}; };
} }
macro_rules! from_to { macro_rules! from_to {
($type: ident, $count: expr, $base: ty, $from: ident, $to: ident) => { ($type: ident, $count: expr, $base: ty, $from: ident, $to: ident) => {
/// Convert the given base type into this type. This is always safe. fn $from(x: $base) -> $type {
pub fn $from(x: $base) -> $type {
let mut res = $type { contents: [0; $count] }; let mut res = $type { contents: [0; $count] };
res.contents[0] = x as u64; res.contents[0] = x as u64;
res res
} }
/// Convert this back into a base type. This is the equivalent of fn $to(&self) -> $base {
/// masking off the relevant number of bits, and should work just
/// like the `as` keyword with normal word types.
pub fn $to(&self) -> $base {
self.contents[0] as $base self.contents[0] as $base
} }
}; };

View File

@@ -21,7 +21,7 @@ fn le(a: &[u64], b: &[u64]) -> bool {
generic_cmp(a, b) != Ordering::Greater generic_cmp(a, b) != Ordering::Greater
} }
fn ge(a: &[u64], b: &[u64]) -> bool { pub fn ge(a: &[u64], b: &[u64]) -> bool {
generic_cmp(a, b) != Ordering::Less generic_cmp(a, b) != Ordering::Less
} }
@@ -183,6 +183,32 @@ pub fn generic_mul(a: &mut [u64], orig: &[u64], b: &[u64]) {
} }
} }
#[inline(always)]
pub fn expanding_mul(a: &[u64], b: &[u64]) -> Vec<u64> {
assert!(a.len() == b.len());
// The maximum size of an n x n digit multiplication is 2n digits, so
// here's our output array.
let mut result = Vec::with_capacity(a.len() * 2);
result.resize(a.len() * 2, 0);
for (base_idx, digit) in b.iter().enumerate() {
let mut myrow = Vec::with_capacity(a.len() * 2);
let mut carry = 0;
myrow.resize(a.len() * 2, 0);
for (col_idx, digit2) in a.iter().enumerate() {
let left = *digit2 as u128;
let right = *digit as u128;
let combo = (left * right) + carry;
myrow[base_idx + col_idx] = combo as u64;
carry = combo >> 64;
}
generic_add(&mut result, &myrow);
}
result
}
#[inline(always)] #[inline(always)]
pub fn generic_div(inx: &[u64], iny: &[u64], pub fn generic_div(inx: &[u64], iny: &[u64],
outq: &mut [u64], outr: &mut [u64]) outq: &mut [u64], outr: &mut [u64])

View File

@@ -14,11 +14,11 @@ use std::cmp::Ordering;
use std::fmt::{Debug,Error,Formatter}; use std::fmt::{Debug,Error,Formatter};
use std::ops::*; use std::ops::*;
construct_unsigned!(U512, u512, 8); construct_unsigned!(U512, BarretMu512, u512, 8);
construct_unsigned!(U1024, u1024, 16); construct_unsigned!(U1024, BarretMu1024, u1024, 16);
construct_unsigned!(U2048, u2048, 32); construct_unsigned!(U2048, BarretMu2048, u2048, 32);
construct_unsigned!(U3072, u3072, 48); construct_unsigned!(U3072, BarretMu3072, u3072, 48);
construct_unsigned!(U4096, u4096, 64); construct_unsigned!(U4096, BarretMu4096, u4096, 64);
construct_unsigned!(U7680, u7680, 120); construct_unsigned!(U7680, BarretMu7680, u7680, 120);
construct_unsigned!(U8192, u8192, 128); construct_unsigned!(U8192, BarretMu8192, u8192, 128);
construct_unsigned!(U15360, u15360, 240); construct_unsigned!(U15360, BarretMu15360, u15360, 240);

View File

@@ -1,4 +1,38 @@
pub trait CryptoNum { pub trait CryptoNum {
/// A related type that can hold the constant required for Barrett
/// reduction.
type BarrettMu;
/// Generate the zero value for this type.
fn zero() -> Self;
/// Generate the maximum possible value for this type.
fn max_value() -> Self;
/// Test if the number is zero.
fn is_zero(&self) -> bool;
/// Test if the number is odd (a.k.a., the low bit is set)
fn is_odd(&self) -> bool;
/// Test if the number is even (a.k.a., the low bit is clear)
fn is_even(&self) -> bool;
/// Translate a `u8` to this type. This must be safe.
fn from_u8(x: u8) -> Self;
/// Convert this back into a `u8`. This is the equivalent of masking off
/// the lowest 8 bits and then casting to a `u8`.
fn to_u8(&self) -> u8;
/// Translate a `u16` to this type. This must be safe.
fn from_u16(x: u16) -> Self;
/// Convert this back into a `u16`. This is the equivalent of masking off
/// the lowest 16 bits and then casting to a `u16`.
fn to_u16(&self) -> u16;
/// Translate a `u32` to this type. This must be safe.
fn from_u32(x: u32) -> Self;
/// Convert this back into a `u32`. This is the equivalent of masking off
/// the lowest 32 bits and then casting to a `u32`.
fn to_u32(&self) -> u32;
/// Translate a `u64` to this type. This must be safe.
fn from_u64(x: u64) -> Self;
/// Convert this back into a `u64`. This is the equivalent of masking off
/// the lowest 64 bits and then casting to a `u64`.
fn to_u64(&self) -> u64;
/// Simultaneously compute the quotient and remainder of this number and /// Simultaneously compute the quotient and remainder of this number and
/// the given divisor. /// the given divisor.
fn divmod(&self, a: &Self, q: &mut Self, r: &mut Self); fn divmod(&self, a: &Self, q: &mut Self, r: &mut Self);
@@ -9,4 +43,9 @@ pub trait CryptoNum {
/// must be greater than or equal to the size of the number, and must be /// must be greater than or equal to the size of the number, and must be
/// a multiple of 8 bytes long. Unused bytes should be ignored. /// a multiple of 8 bytes long. Unused bytes should be ignored.
fn from_bytes(&[u8]) -> Self; fn from_bytes(&[u8]) -> Self;
/// Compute the Barett constant mu, using this as a modulus, which we can
/// use later to perform faster mod operations.
fn barrett_mu(&self) -> Option<Self::BarrettMu>;
/// Faster modulo through the use of the Barrett constant, above.
fn fastmod(&self, &Self::BarrettMu) -> Self;
} }