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.
83 lines
3.3 KiB
Rust
83 lines
3.3 KiB
Rust
/// Squaring of large numbers.
|
|
pub trait Square<Output> {
|
|
fn square(&self) -> Output;
|
|
}
|
|
|
|
macro_rules! square_impls {
|
|
($name: ident, $bigger: ident, $size: expr) => {
|
|
impl Square<$bigger> for $name {
|
|
fn square(&self) -> $bigger {
|
|
let mut w = [0; $size/32];
|
|
let t = $size / 64;
|
|
|
|
for i in 0..t {
|
|
let x128 = self.value[i] as u128;
|
|
let mut uvb = (w[2*i] as u128) + (x128 * x128);
|
|
w[2*i] = uvb & 0xFFFFFFFFFFFFFFFF;
|
|
let mut c = uvb >> 64;
|
|
for j in (i+1)..t {
|
|
let xj128 = self.value[j] as u128;
|
|
let xi128 = self.value[i] as u128;
|
|
// this first product is safely 128 bits or less,
|
|
// because the input arguments are both 64 bits.
|
|
let xij128 = xj128 * xi128;
|
|
// this next bit may overflow, but will do so by exactly
|
|
// one bit.
|
|
let twoxij128 = xij128 << 1;
|
|
let carried_shl = (xij128 & (1 << 127)) != 0;
|
|
// this next bit may *also* overflow, but should also do
|
|
// so by no more than one bit.
|
|
let (new,carry1) = twoxij128.overflowing_add(c);
|
|
// ditto ...
|
|
let wij = w[i+j];
|
|
let (uvb2,carry2) = new.overflowing_add(wij as u128);
|
|
// for the value we're going to save for this digit, we
|
|
// only care about the low bits, so we can forget about
|
|
// the carry stuff.
|
|
w[i+j] = uvb2 & 0xFFFFFFFFFFFFFFFF;
|
|
// for c, though, we do care about the carries, above.
|
|
// Fortunately, they were both by only one bit, so we
|
|
// should be able to just back-fix them.
|
|
c = uvb2 >> 64;
|
|
if carried_shl { c += 1 << 64; }
|
|
if carry1 { c += 1 << 64; }
|
|
if carry2 { c += 1 << 64; }
|
|
}
|
|
w[i+t] = c;
|
|
}
|
|
let mut res = $bigger::zero();
|
|
for i in 0..w.len() { res.value[i] = w[i] as u64; }
|
|
res
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
macro_rules! generate_square_tests {
|
|
($name: ident, $lname: ident, $dbl: ident) => {
|
|
#[test]
|
|
fn $lname() {
|
|
generate_square_tests!(body $name, $lname, $dbl);
|
|
}
|
|
};
|
|
(ignore $name: ident, $lname: ident, $dbl: ident) => {
|
|
#[test]
|
|
#[ignore]
|
|
fn $lname() {
|
|
generate_square_tests!(body $name, $lname, $dbl);
|
|
}
|
|
};
|
|
(body $name: ident, $lname: ident, $dbl: ident) => {
|
|
let fname = build_test_path("square", stringify!($name));
|
|
run_test(fname.to_string(), 2, |case| {
|
|
let (neg0, abytes) = case.get("a").unwrap();
|
|
let (neg1, rbytes) = case.get("r").unwrap();
|
|
assert!(!neg0 && !neg1);
|
|
|
|
let a = $name::from_bytes(abytes);
|
|
let r = $dbl::from_bytes(rbytes);
|
|
assert_eq!(r, a.square());
|
|
});
|
|
};
|
|
} |