use crate::public_key::bigint::BigUint;
#[inline]
pub(crate) fn gf2m_add(a: &BigUint, b: &BigUint) -> BigUint {
let mut out = a.clone();
out.bitxor_assign(b);
out
}
pub(crate) fn gf2m_mul(a: &BigUint, b: &BigUint, poly: &BigUint, degree: usize) -> BigUint {
if a.is_zero() || b.is_zero() {
return BigUint::zero();
}
let mut acc = BigUint::zero();
let mut temp = a.clone();
let b_bits = b.bits();
for i in 0..b_bits {
if b.bit(i) {
acc.bitxor_assign(&temp);
}
temp.shl1();
}
gf2m_reduce(acc, poly, degree)
}
#[inline]
pub(crate) fn gf2m_sq(a: &BigUint, poly: &BigUint, degree: usize) -> BigUint {
gf2m_mul(a, a, poly, degree)
}
pub(crate) fn gf2m_inv(a: &BigUint, poly: &BigUint, degree: usize) -> Option<BigUint> {
if a.is_zero() {
return None;
}
let mut u = a.clone();
let mut v = poly.clone();
let mut b = BigUint::one();
let mut c = BigUint::zero();
while !u.is_one() {
let deg_u = u.bits(); let deg_v = v.bits();
if deg_u < deg_v {
core::mem::swap(&mut u, &mut v);
core::mem::swap(&mut b, &mut c);
}
let j = u.bits().saturating_sub(v.bits());
let mut sv = v.clone();
sv.shl_bits(j);
u.bitxor_assign(&sv);
let mut sc = c.clone();
sc.shl_bits(j);
b.bitxor_assign(&sc);
}
Some(gf2m_reduce(b, poly, degree))
}
pub(crate) fn gf2m_half_trace(c: &BigUint, poly: &BigUint, degree: usize) -> BigUint {
let mut t = c.clone(); let mut power = c.clone();
for _ in 0..(degree - 1) / 2 {
power = gf2m_sq(&gf2m_sq(&power, poly, degree), poly, degree);
t.bitxor_assign(&power);
}
t
}
fn gf2m_reduce(mut a: BigUint, poly: &BigUint, degree: usize) -> BigUint {
let mut current_bits = a.bits();
while current_bits > degree {
let i = current_bits - 1; let shift = i - degree;
let mut shifted = poly.clone();
shifted.shl_bits(shift);
a.bitxor_assign(&shifted);
current_bits = a.bits();
}
a
}
#[cfg(test)]
mod tests {
use super::*;
fn poly163() -> BigUint {
let mut p = BigUint::zero();
p.set_bit(163);
p.set_bit(7);
p.set_bit(6);
p.set_bit(3);
p.set_bit(0);
p
}
#[test]
fn gf2m_add_xor() {
let a = BigUint::from_u64(0b1010);
let b = BigUint::from_u64(0b1100);
let c = gf2m_add(&a, &b);
assert_eq!(c, BigUint::from_u64(0b0110));
}
#[test]
fn gf2m_add_self_is_zero() {
let a = BigUint::from_u64(0xDEAD_BEEF);
let c = gf2m_add(&a, &a);
assert!(c.is_zero(), "a XOR a must be zero");
}
#[test]
fn gf2m_mul_small() {
let poly = BigUint::from_u64(0b10011);
let a = BigUint::from_u64(0b0101);
let b = BigUint::from_u64(0b0011);
let c = gf2m_mul(&a, &b, &poly, 4);
assert_eq!(c, BigUint::from_u64(0b1111));
}
#[test]
fn gf2m_mul_reduce() {
let poly = BigUint::from_u64(0b10011);
let a = BigUint::from_u64(0b1000); let b = BigUint::from_u64(0b0010); let c = gf2m_mul(&a, &b, &poly, 4);
assert_eq!(c, BigUint::from_u64(0b0011), "x^3 * x = x + 1 in GF(2^4)");
}
#[test]
fn gf2m_sq_equals_mul_self() {
let poly = poly163();
let a = BigUint::from_u64(0xABCD_1234);
let sq = gf2m_sq(&a, &poly, 163);
let mul = gf2m_mul(&a, &a, &poly, 163);
assert_eq!(sq, mul, "sq must equal mul(a, a)");
}
#[test]
fn gf2m_inv_roundtrip_gf163() {
let poly = poly163();
let a = BigUint::from_u64(0x1234_5678_9ABC_DEF0);
let a_inv = gf2m_inv(&a, &poly, 163).expect("a is non-zero, must be invertible");
let product = gf2m_mul(&a, &a_inv, &poly, 163);
assert_eq!(
product,
BigUint::one(),
"a * a^{{-1}} must be 1 in GF(2^163)"
);
}
#[test]
fn gf2m_inv_of_one_is_one() {
let poly = poly163();
let one_inv = gf2m_inv(&BigUint::one(), &poly, 163).expect("1 is invertible");
assert_eq!(one_inv, BigUint::one());
}
#[test]
fn gf2m_inv_zero_returns_none() {
let poly = poly163();
assert!(gf2m_inv(&BigUint::zero(), &poly, 163).is_none());
}
#[test]
fn gf2m_half_trace_solves_quadratic() {
let poly = poly163();
let c = BigUint::from_u64(2); let z = gf2m_half_trace(&c, &poly, 163);
let z_sq = gf2m_sq(&z, &poly, 163);
let check = gf2m_add(&z_sq, &z);
assert_eq!(check, c, "HT(c)^2 + HT(c) must equal c");
}
#[test]
fn gf2m_mul_associative() {
let poly = poly163();
let a = BigUint::from_u64(0x1111_2222);
let b = BigUint::from_u64(0x3333_4444);
let c = BigUint::from_u64(0x5555_6666);
let ab_c = gf2m_mul(&gf2m_mul(&a, &b, &poly, 163), &c, &poly, 163);
let a_bc = gf2m_mul(&a, &gf2m_mul(&b, &c, &poly, 163), &poly, 163);
assert_eq!(ab_c, a_bc, "multiplication must be associative");
}
#[test]
fn gf2m_mul_distributive() {
let poly = poly163();
let a = BigUint::from_u64(0xABCD);
let b = BigUint::from_u64(0x1234);
let c = BigUint::from_u64(0x5678);
let a_bc = gf2m_mul(&a, &gf2m_add(&b, &c), &poly, 163);
let ab_ac = gf2m_add(&gf2m_mul(&a, &b, &poly, 163), &gf2m_mul(&a, &c, &poly, 163));
assert_eq!(a_bc, ab_ac, "multiplication must distribute over addition");
}
}