//! Equivalence tests between `num-bigint` and `crypto-bigint` use crypto_bigint::{ modular::runtime_mod::{DynResidue, DynResidueParams}, Encoding, Limb, NonZero, Word, U256, }; use num_bigint::BigUint; use num_integer::Integer; use num_traits::identities::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::()) -> 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::()) { 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 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 wrapping_sqrt(a in uint()) { let a_bi = to_biguint(&a); let expected = to_uint(a_bi.sqrt()); let actual = a.wrapping_sqrt(); 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::()) { 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); } }