mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-17 07:48:55 +00:00
371 lines
9.7 KiB
Rust
371 lines
9.7 KiB
Rust
//! Equivalence tests between `num-bigint` and `crypto-bigint`
|
|
|
|
use crypto_bigint::{
|
|
modular::runtime_mod::{DynResidue, DynResidueParams},
|
|
CtChoice, Encoding, Limb, NonZero, Word, U256,
|
|
};
|
|
use num_bigint::BigUint;
|
|
use num_integer::Integer;
|
|
use num_traits::identities::{One, Zero};
|
|
use proptest::prelude::*;
|
|
use std::mem;
|
|
|
|
/// Example prime number (NIST P-256 curve order)
|
|
const P: U256 =
|
|
U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
|
|
|
|
fn to_biguint(uint: &U256) -> BigUint {
|
|
BigUint::from_bytes_le(uint.to_le_bytes().as_ref())
|
|
}
|
|
|
|
fn to_uint(big_uint: BigUint) -> U256 {
|
|
let mut input = [0u8; U256::BYTES];
|
|
let encoded = big_uint.to_bytes_le();
|
|
let l = encoded.len().min(U256::BYTES);
|
|
input[..l].copy_from_slice(&encoded[..l]);
|
|
|
|
U256::from_le_slice(&input)
|
|
}
|
|
|
|
prop_compose! {
|
|
fn uint()(bytes in any::<[u8; 32]>()) -> U256 {
|
|
U256::from_le_slice(&bytes)
|
|
}
|
|
}
|
|
prop_compose! {
|
|
fn uint_mod_p(p: U256)(a in uint()) -> U256 {
|
|
a.wrapping_rem(&p)
|
|
}
|
|
}
|
|
prop_compose! {
|
|
fn nonzero_limb()(x in any::<Word>()) -> Limb {
|
|
if x == 0 { Limb::from(1u32) } else {Limb::from(x)}
|
|
}
|
|
}
|
|
|
|
proptest! {
|
|
#[test]
|
|
fn roundtrip(a in uint()) {
|
|
assert_eq!(a, to_uint(to_biguint(&a)));
|
|
}
|
|
|
|
#[test]
|
|
fn shl_vartime(a in uint(), shift in any::<u8>()) {
|
|
let a_bi = to_biguint(&a);
|
|
|
|
let expected = to_uint(a_bi << shift);
|
|
let actual = a.shl_vartime(shift as usize);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn shl(a in uint(), shift in any::<u16>()) {
|
|
let a_bi = to_biguint(&a);
|
|
|
|
// Add a 50% probability of overflow.
|
|
let shift = (shift as usize) % (U256::BITS * 2);
|
|
|
|
let expected = to_uint((a_bi << shift) & ((BigUint::one() << U256::BITS) - BigUint::one()));
|
|
let actual = a.shl(shift);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn shr(a in uint(), shift in any::<u16>()) {
|
|
let a_bi = to_biguint(&a);
|
|
|
|
// Add a 50% probability of overflow.
|
|
let shift = (shift as usize) % (U256::BITS * 2);
|
|
|
|
let expected = to_uint(a_bi >> shift);
|
|
let actual = a.shr(shift);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_add(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
let expected = to_uint(a_bi + b_bi);
|
|
let actual = a.wrapping_add(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn add_mod_nist_p256(a in uint_mod_p(P), b in uint_mod_p(P)) {
|
|
assert!(a < P);
|
|
assert!(b < P);
|
|
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
let p_bi = to_biguint(&P);
|
|
|
|
let expected = to_uint((a_bi + b_bi) % p_bi);
|
|
let actual = a.add_mod(&b, &P);
|
|
|
|
assert!(expected < P);
|
|
assert!(actual < P);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn sub_mod_nist_p256(mut a in uint_mod_p(P), mut b in uint_mod_p(P)) {
|
|
if b > a {
|
|
mem::swap(&mut a, &mut b);
|
|
}
|
|
|
|
assert!(a < P);
|
|
assert!(b < P);
|
|
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
let p_bi = to_biguint(&P);
|
|
|
|
let expected = to_uint((a_bi - b_bi) % p_bi);
|
|
let actual = a.sub_mod(&b, &P);
|
|
|
|
assert!(expected < P);
|
|
assert!(actual < P);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_sub(mut a in uint(), mut b in uint()) {
|
|
if b > a {
|
|
mem::swap(&mut a, &mut b);
|
|
}
|
|
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
let expected = to_uint(a_bi - b_bi);
|
|
let actual = a.wrapping_sub(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_mul(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
let expected = to_uint(a_bi * b_bi);
|
|
let actual = a.wrapping_mul(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_div(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
if !b_bi.is_zero() {
|
|
let expected = to_uint(a_bi / b_bi);
|
|
let actual = a.wrapping_div(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn div_rem_limb(a in uint(), b in nonzero_limb()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&U256::from(b));
|
|
|
|
let (expected_quo, expected_rem) = a_bi.div_rem(&b_bi);
|
|
let (actual_quo, actual_rem) = a.div_rem_limb(NonZero::new(b).unwrap());
|
|
assert_eq!(to_uint(expected_quo), actual_quo);
|
|
assert_eq!(to_uint(expected_rem), U256::from(actual_rem));
|
|
}
|
|
|
|
#[test]
|
|
fn div_rem_limb_min_max(a in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
|
|
for b in [Limb::from(1u32), Limb::MAX] {
|
|
let b_bi = to_biguint(&U256::from(b));
|
|
let (expected_quo, expected_rem) = a_bi.div_rem(&b_bi);
|
|
let (actual_quo, actual_rem) = a.div_rem_limb(NonZero::new(b).unwrap());
|
|
assert_eq!(to_uint(expected_quo), actual_quo);
|
|
assert_eq!(to_uint(expected_rem), U256::from(actual_rem));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_rem(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
if !b_bi.is_zero() {
|
|
let expected = to_uint(a_bi % b_bi);
|
|
let actual = a.wrapping_rem(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn inv_mod2k(a in uint(), k in any::<usize>()) {
|
|
let a = a | U256::ONE; // make odd
|
|
let k = k % (U256::BITS + 1);
|
|
let a_bi = to_biguint(&a);
|
|
let m_bi = BigUint::one() << k;
|
|
|
|
let actual = a.inv_mod2k(k);
|
|
let actual_vartime = a.inv_mod2k_vartime(k);
|
|
assert_eq!(actual, actual_vartime);
|
|
|
|
if k == 0 {
|
|
assert_eq!(actual, U256::ZERO);
|
|
}
|
|
else {
|
|
let inv_bi = to_biguint(&actual);
|
|
let res = (inv_bi * a_bi) % m_bi;
|
|
assert_eq!(res, BigUint::one());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn inv_mod(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
let expected_is_some = if a_bi.gcd(&b_bi) == BigUint::one() { CtChoice::TRUE } else { CtChoice::FALSE };
|
|
let (actual, actual_is_some) = a.inv_mod(&b);
|
|
|
|
assert_eq!(bool::from(expected_is_some), bool::from(actual_is_some));
|
|
|
|
if actual_is_some.into() {
|
|
let inv_bi = to_biguint(&actual);
|
|
let res = (inv_bi * a_bi) % b_bi;
|
|
assert_eq!(res, BigUint::one());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_sqrt(a in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let expected = to_uint(a_bi.sqrt());
|
|
let actual = a.wrapping_sqrt_vartime();
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_or(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
if !b_bi.is_zero() {
|
|
let expected = to_uint(a_bi | b_bi);
|
|
let actual = a.wrapping_or(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_and(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
|
|
if !b_bi.is_zero() {
|
|
let expected = to_uint(a_bi & b_bi);
|
|
let actual = a.wrapping_and(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wrapping_xor(a in uint(), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
if !b_bi.is_zero() {
|
|
let expected = to_uint(a_bi ^ b_bi);
|
|
let actual = a.wrapping_xor(&b);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn encoding(a in uint()) {
|
|
assert_eq!(a, U256::from_be_bytes(a.to_be_bytes()));
|
|
assert_eq!(a, U256::from_le_bytes(a.to_le_bytes()));
|
|
}
|
|
|
|
#[test]
|
|
fn encoding_reverse(a in uint()) {
|
|
let mut bytes = a.to_be_bytes();
|
|
bytes.reverse();
|
|
assert_eq!(a, U256::from_le_bytes(bytes));
|
|
|
|
let mut bytes = a.to_le_bytes();
|
|
bytes.reverse();
|
|
assert_eq!(a, U256::from_be_bytes(bytes));
|
|
}
|
|
|
|
#[test]
|
|
fn residue_pow(a in uint_mod_p(P), b in uint()) {
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b);
|
|
let p_bi = to_biguint(&P);
|
|
|
|
let expected = to_uint(a_bi.modpow(&b_bi, &p_bi));
|
|
|
|
let params = DynResidueParams::new(&P);
|
|
let a_m = DynResidue::new(&a, params);
|
|
let actual = a_m.pow(&b).retrieve();
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn residue_pow_bounded_exp(a in uint_mod_p(P), b in uint(), exponent_bits in any::<u8>()) {
|
|
|
|
let b_masked = b & (U256::ONE << exponent_bits.into()).wrapping_sub(&U256::ONE);
|
|
|
|
let a_bi = to_biguint(&a);
|
|
let b_bi = to_biguint(&b_masked);
|
|
let p_bi = to_biguint(&P);
|
|
|
|
let expected = to_uint(a_bi.modpow(&b_bi, &p_bi));
|
|
|
|
let params = DynResidueParams::new(&P);
|
|
let a_m = DynResidue::new(&a, params);
|
|
let actual = a_m.pow_bounded_exp(&b, exponent_bits.into()).retrieve();
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn residue_div_by_2(a in uint_mod_p(P)) {
|
|
let a_bi = to_biguint(&a);
|
|
let p_bi = to_biguint(&P);
|
|
let two = BigUint::from(2u32);
|
|
|
|
let expected = if a_bi.is_even() {
|
|
&a_bi / two
|
|
}
|
|
else {
|
|
(&a_bi + &p_bi) / two
|
|
};
|
|
let expected = to_uint(expected);
|
|
|
|
let params = DynResidueParams::new(&P);
|
|
let a_m = DynResidue::new(&a, params);
|
|
let actual = a_m.div_by_2().retrieve();
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|