#![allow(dead_code)]
use crate::tss::bigint::BigUintDec;
use purecrypto::bignum::{BoxedMontModulus, BoxedUint, inv_mod_boxed};
use purecrypto::rng::RngCore;
use std::cmp::Ordering;
pub(crate) fn u64(v: u64) -> BoxedUint {
BoxedUint::from_u64(v)
}
pub(crate) fn one() -> BoxedUint {
BoxedUint::from_u64(1)
}
pub(crate) fn is_one(n: &BoxedUint) -> bool {
*n == one()
}
pub(crate) fn to_be(n: &BoxedUint) -> Vec<u8> {
if n.is_zero() {
return Vec::new();
}
let byte_len = n.bit_len().div_ceil(8);
let b = n.to_be_bytes(byte_len);
let start = b.iter().position(|&x| x != 0).unwrap_or(b.len());
b[start..].to_vec()
}
pub(crate) fn from_be(b: &[u8]) -> BoxedUint {
if b.is_empty() {
return BoxedUint::zero(1);
}
BoxedUint::from_be_bytes(b)
}
pub(crate) fn from_dec(d: &BigUintDec) -> BoxedUint {
from_be(d.as_be_bytes())
}
pub(crate) fn to_dec(n: &BoxedUint) -> BigUintDec {
BigUintDec::from_be_bytes(&to_be(n))
}
pub(crate) fn cmp(a: &BoxedUint, b: &BoxedUint) -> Ordering {
if a == b {
Ordering::Equal
} else if a.lt(b) {
Ordering::Less
} else {
Ordering::Greater
}
}
pub(crate) fn ge(a: &BoxedUint, b: &BoxedUint) -> bool {
cmp(a, b) != Ordering::Less
}
pub(crate) fn gt(a: &BoxedUint, b: &BoxedUint) -> bool {
cmp(a, b) == Ordering::Greater
}
fn low_u64(n: &BoxedUint) -> u64 {
n.as_limbs().first().copied().unwrap_or(0)
}
pub(crate) fn to_u64(n: &BoxedUint) -> u64 {
low_u64(n)
}
pub(crate) fn bit(n: &BoxedUint, i: usize) -> u8 {
let limb = i / 64;
n.as_limbs()
.get(limb)
.map_or(0, |&l| ((l >> (i % 64)) & 1) as u8)
}
pub(crate) fn from_u128(v: u128) -> BoxedUint {
from_be(&v.to_be_bytes())
}
pub(crate) fn mod_small(n: &BoxedUint, m: u64) -> u64 {
let (_, r) = n.divrem(&u64(m));
low_u64(&r)
}
pub(crate) fn add(a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
a.add(b)
}
pub(crate) fn mul(a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
a.mul(b)
}
pub(crate) fn sub(a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
debug_assert!(ge(a, b), "bn::sub underflow");
a.sub(b)
}
pub(crate) fn rem(a: &BoxedUint, m: &BoxedUint) -> BoxedUint {
a.reduce(m)
}
pub(crate) fn divrem(a: &BoxedUint, b: &BoxedUint) -> (BoxedUint, BoxedUint) {
a.divrem(b)
}
pub(crate) fn sqrt(n: &BoxedUint) -> BoxedUint {
if n.bit_len() <= 1 {
return n.clone(); }
let mut x = n.clone();
loop {
let (q, _) = divrem(n, &x);
let y = add(&x, &q).shr_bits(1); if !y.lt(&x) {
break;
}
x = y;
}
x
}
pub(crate) fn secp256k1_order() -> BoxedUint {
from_be(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36,
0x41, 0x41,
])
}
pub(crate) fn mod_inv(a: &BoxedUint, m: &BoxedUint) -> Option<BoxedUint> {
inv_mod_boxed(&rem(a, m), m)
}
pub(crate) fn gcd(a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
let mut a = a.clone();
let mut b = b.clone();
if a.is_zero() {
return b;
}
if b.is_zero() {
return a;
}
let shift = {
let mut s = 0;
while !a.is_odd() && !b.is_odd() {
a = a.shr_bits(1);
b = b.shr_bits(1);
s += 1;
}
s
};
while !a.is_odd() {
a = a.shr_bits(1);
}
loop {
while !b.is_odd() {
b = b.shr_bits(1);
}
if gt(&a, &b) {
std::mem::swap(&mut a, &mut b);
}
b = b.sub(&a);
if b.is_zero() {
break;
}
}
let mut g = a;
for _ in 0..shift {
g = g.add(&g);
}
g
}
pub(crate) struct Modulus {
m: BoxedUint,
mont: BoxedMontModulus,
}
impl Modulus {
pub(crate) fn new(m: &BoxedUint) -> Self {
Modulus {
m: m.clone(),
mont: BoxedMontModulus::new(m),
}
}
pub(crate) fn modulus(&self) -> &BoxedUint {
&self.m
}
pub(crate) fn reduce(&self, a: &BoxedUint) -> BoxedUint {
if a.lt(&self.m) {
a.clone()
} else {
a.reduce(&self.m)
}
}
pub(crate) fn mul(&self, a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
self.mont.mul_mod(&self.reduce(a), &self.reduce(b))
}
pub(crate) fn add(&self, a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
self.mont.add_mod(&self.reduce(a), &self.reduce(b))
}
pub(crate) fn sub(&self, a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
self.mont.sub_mod(&self.reduce(a), &self.reduce(b))
}
pub(crate) fn pow(&self, base: &BoxedUint, exp: &BoxedUint) -> BoxedUint {
self.mont.pow_public(&self.reduce(base), exp)
}
pub(crate) fn pow_pub(&self, base: &BoxedUint, exp: &BoxedUint) -> BoxedUint {
self.mont.pow_public(&self.reduce(base), exp)
}
pub(crate) fn pow_secret(&self, base: &BoxedUint, exp: &BoxedUint) -> BoxedUint {
self.mont.pow(&self.reduce(base), exp)
}
pub(crate) fn inv(&self, a: &BoxedUint) -> Option<BoxedUint> {
inv_mod_boxed(&self.reduce(a), &self.m)
}
}
const SMALL_PRIMES: &[u64] = &[
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
];
pub(crate) fn is_probable_prime<R: RngCore>(n: &BoxedUint, rng: &mut R, rounds: usize) -> bool {
if n.is_zero() || is_one(n) {
return false;
}
let two = u64(2);
if *n == two {
return true;
}
if !n.is_odd() {
return false;
}
for &p in SMALL_PRIMES {
if mod_small(n, p) == 0 {
return *n == u64(p);
}
}
let n_minus_1 = sub(n, &one());
let mut d = n_minus_1.clone();
let mut s = 0usize;
while !d.is_odd() {
d = d.shr_bits(1);
s += 1;
}
let modn = Modulus::new(n);
'witness: for _ in 0..rounds {
let a = rand_range(&two, &sub(n, &two), rng);
let mut x = modn.pow_pub(&a, &d);
if is_one(&x) || x == n_minus_1 {
continue;
}
for _ in 0..s.saturating_sub(1) {
x = modn.mul(&x, &x);
if x == n_minus_1 {
continue 'witness;
}
if is_one(&x) {
return false;
}
}
return false;
}
true
}
fn pow2(i: usize) -> BoxedUint {
let mut buf = vec![0u8; i / 8 + 1];
buf[0] = 1u8 << (i % 8);
from_be(&buf)
}
fn set_second_top_bit(q: BoxedUint, bits: usize) -> BoxedUint {
if bit(&q, bits - 2) == 0 {
add(&q, &pow2(bits - 2))
} else {
q
}
}
pub(crate) fn generate_safe_prime<R: RngCore>(bits: usize, rng: &mut R) -> BoxedUint {
assert!(bits >= 4);
let rounds = 20;
loop {
let mut q = set_second_top_bit(rand_bits(bits - 1, rng), bits - 1);
if mod_small(&q, 3) != 2 {
continue;
}
if !q.is_odd() {
q = add(&q, &one());
}
let p = add(&add(&q, &q), &one());
if is_probable_prime(&q, rng, rounds) && is_probable_prime(&p, rng, rounds) {
return p;
}
}
}
pub(crate) fn generate_germain<R: RngCore>(bits: usize, rng: &mut R) -> (BoxedUint, BoxedUint) {
assert!(bits >= 4);
let rounds = 20;
loop {
let mut q = set_second_top_bit(rand_bits(bits - 1, rng), bits - 1);
if mod_small(&q, 3) != 2 {
continue;
}
if !q.is_odd() {
q = add(&q, &one());
}
let p = add(&add(&q, &q), &one());
if is_probable_prime(&q, rng, rounds) && is_probable_prime(&p, rng, rounds) {
return (q, p);
}
}
}
pub(crate) fn rand_bits<R: RngCore>(bits: usize, rng: &mut R) -> BoxedUint {
assert!(bits >= 2);
let nbytes = bits.div_ceil(8);
let mut buf = vec![0u8; nbytes];
rng.fill_bytes(&mut buf);
let excess = nbytes * 8 - bits;
buf[0] &= 0xffu8 >> excess;
buf[0] |= 0x80u8 >> excess;
let last = nbytes - 1;
buf[last] |= 1;
from_be(&buf)
}
pub(crate) fn rand_below<R: RngCore>(n: &BoxedUint, rng: &mut R) -> BoxedUint {
assert!(!n.is_zero());
let bits = n.bit_len();
let nbytes = bits.div_ceil(8);
let excess = nbytes * 8 - bits;
loop {
let mut buf = vec![0u8; nbytes];
rng.fill_bytes(&mut buf);
buf[0] &= 0xffu8 >> excess;
let c = from_be(&buf);
if c.lt(n) {
return c;
}
}
}
pub(crate) fn rand_positive_below<R: RngCore>(n: &BoxedUint, rng: &mut R) -> BoxedUint {
loop {
let x = rand_below(n, rng);
if !x.is_zero() {
return x;
}
}
}
pub(crate) fn rand_range<R: RngCore>(lo: &BoxedUint, hi: &BoxedUint, rng: &mut R) -> BoxedUint {
let span = add(&sub(hi, lo), &one());
add(lo, &rand_below(&span, rng))
}
pub(crate) fn rand_unit<R: RngCore>(m: &BoxedUint, rng: &mut R) -> BoxedUint {
loop {
let x = rand_below(m, rng);
if !x.is_zero() && is_one(&gcd(&x, m)) {
return x;
}
}
}
pub(crate) fn jacobi(a: &BoxedUint, n: &BoxedUint) -> i32 {
debug_assert!(n.is_odd());
let mut a = rem(a, n);
let mut n = n.clone();
let mut result = 1i32;
while !a.is_zero() {
while !a.is_odd() {
a = a.shr_bits(1);
let r = mod_small(&n, 8);
if r == 3 || r == 5 {
result = -result;
}
}
std::mem::swap(&mut a, &mut n);
if mod_small(&a, 4) == 3 && mod_small(&n, 4) == 3 {
result = -result;
}
a = rem(&a, &n);
}
if is_one(&n) { result } else { 0 }
}
#[cfg(test)]
mod tests {
use super::*;
use purecrypto::rng::OsRng;
#[test]
fn be_roundtrip_and_dec() {
let n = from_be(&[0x01, 0x00, 0x00]); assert_eq!(to_be(&n), vec![0x01, 0x00, 0x00]);
assert_eq!(to_dec(&n).as_be_bytes(), &[0x01, 0x00, 0x00]);
assert_eq!(to_be(&BoxedUint::zero(1)), Vec::<u8>::new());
}
#[test]
fn gcd_known() {
assert_eq!(to_be(&gcd(&u64(48), &u64(36))), vec![12]);
assert_eq!(to_be(&gcd(&u64(17), &u64(5))), vec![1]);
assert_eq!(to_be(&gcd(&u64(0), &u64(9))), vec![9]);
}
#[test]
fn modular_ops() {
let m = Modulus::new(&u64(97));
assert_eq!(to_be(&m.mul(&u64(20), &u64(20))), vec![12]); assert_eq!(to_be(&m.pow(&u64(5), &u64(3))), vec![125 % 97]); let inv = m.inv(&u64(5)).unwrap();
assert_eq!(to_be(&m.mul(&u64(5), &inv)), vec![1]);
}
#[test]
fn sqrt_known() {
assert_eq!(to_be(&sqrt(&u64(0))), Vec::<u8>::new());
assert_eq!(to_be(&sqrt(&u64(1))), vec![1]);
assert_eq!(to_be(&sqrt(&u64(15))), vec![3]);
assert_eq!(to_be(&sqrt(&u64(16))), vec![4]);
assert_eq!(to_be(&sqrt(&u64(17))), vec![4]);
assert_eq!(to_be(&sqrt(&u64(1_000_000))), vec![0x03, 0xe8]); let big = mul(&u64(1_234_567), &u64(1_234_567));
assert_eq!(to_be(&sqrt(&add(&big, &u64(5)))), to_be(&u64(1_234_567)));
}
#[test]
fn jacobi_known() {
assert_eq!(jacobi(&u64(2), &u64(15)), 1);
assert_eq!(jacobi(&u64(7), &u64(15)), -1);
assert_eq!(jacobi(&u64(3), &u64(15)), 0);
}
#[test]
fn primality() {
let mut rng = OsRng;
assert!(is_probable_prime(&u64(2), &mut rng, 10));
assert!(is_probable_prime(&u64(97), &mut rng, 10));
assert!(is_probable_prime(&u64(7919), &mut rng, 10));
assert!(!is_probable_prime(&u64(91), &mut rng, 10)); assert!(!is_probable_prime(&u64(1), &mut rng, 10));
}
#[test]
fn safe_prime_small() {
let mut rng = OsRng;
let p = generate_safe_prime(20, &mut rng);
assert!(is_probable_prime(&p, &mut rng, 20));
let q = sub(&p, &one());
let q = q.shr_bits(1); assert!(is_probable_prime(&q, &mut rng, 20), "(p-1)/2 must be prime");
assert_eq!(p.bit_len(), 20);
}
}