108 lines
3.4 KiB
Rust
108 lines
3.4 KiB
Rust
//! RSA-style group Z_N^* with a 2048-bit modulus.
|
|
//!
|
|
//! TRUSTED-SETUP STAND-IN. NOT FOR PRODUCTION.
|
|
//! Whoever knows the factors of N can compute x^(2^T) in two short exponentiations
|
|
//! (reduce 2^T mod phi(N)) and skip the delay entirely. In this prototype the factors are
|
|
//! derived from a PUBLIC seed string, so the trapdoor is public by construction. The group
|
|
//! exists here only to measure squaring speed and to exercise the Wesolowski code against a
|
|
//! second group. The production group is the class group in `classgroup.rs`.
|
|
//!
|
|
//! Soundness note for the record: Wesolowski over Z_N^* needs the low-order assumption, which
|
|
//! fails for -1 (order 2). Real deployments work in QR_N or in Z_N^*/{+-1}. We map inputs into
|
|
//! QR_N by squaring and do not canonicalise signs, which is enough for timing.
|
|
|
|
use crate::group::Group;
|
|
use crate::hash::{bytes_to_int, int_to_bytes, sha256};
|
|
use rug::integer::Order;
|
|
use rug::rand::RandState;
|
|
use rug::Integer;
|
|
|
|
pub const MODULUS_BITS: u32 = 2048;
|
|
|
|
pub struct RsaGroup {
|
|
pub n: Integer,
|
|
}
|
|
|
|
impl RsaGroup {
|
|
/// Deterministic modulus from a public seed. See the module note: the factors are public.
|
|
pub fn from_public_seed(seed: &str) -> RsaGroup {
|
|
let mut rs = RandState::new_mersenne_twister();
|
|
let s = sha256(&[b"igneum-vdf-rsa-standin", seed.as_bytes()]);
|
|
rs.seed(&Integer::from_digits(&s, Order::MsfBe));
|
|
let half = MODULUS_BITS / 2;
|
|
let gen_prime = |rs: &mut RandState| -> Integer {
|
|
let mut p = Integer::from(Integer::random_bits(half, rs));
|
|
p.set_bit(half - 1, true);
|
|
p.set_bit(half - 2, true);
|
|
p.set_bit(0, true);
|
|
p.next_prime_mut();
|
|
p
|
|
};
|
|
let p = gen_prime(&mut rs);
|
|
let mut q = gen_prime(&mut rs);
|
|
while q == p {
|
|
q = gen_prime(&mut rs);
|
|
}
|
|
let n = Integer::from(&p * &q);
|
|
assert_eq!(n.significant_bits(), MODULUS_BITS);
|
|
RsaGroup { n }
|
|
}
|
|
|
|
/// Hash to an element of QR_N.
|
|
pub fn hash_to_elem(&self, data: &[u8]) -> Integer {
|
|
let mut acc = Vec::new();
|
|
let mut i: u32 = 0;
|
|
while acc.len() * 8 < MODULUS_BITS as usize {
|
|
acc.extend_from_slice(&sha256(&[b"igneum-vdf-rsa-x", data, &i.to_be_bytes()]));
|
|
i += 1;
|
|
}
|
|
let mut x = bytes_to_int(&acc);
|
|
x %= &self.n;
|
|
x.square_mut();
|
|
x %= &self.n;
|
|
x
|
|
}
|
|
}
|
|
|
|
impl Group for RsaGroup {
|
|
type Elem = Integer;
|
|
|
|
fn name(&self) -> String {
|
|
format!("RSA-{} stand-in (trusted setup, public trapdoor)", MODULUS_BITS)
|
|
}
|
|
|
|
fn identity(&self) -> Integer {
|
|
Integer::from(1)
|
|
}
|
|
|
|
fn square(&self, x: &mut Integer) {
|
|
x.square_mut();
|
|
*x %= &self.n;
|
|
}
|
|
|
|
fn mul(&self, a: &Integer, b: &Integer) -> Integer {
|
|
let mut r = Integer::from(a * b);
|
|
r %= &self.n;
|
|
r
|
|
}
|
|
|
|
fn serialize(&self, x: &Integer) -> Vec<u8> {
|
|
int_to_bytes(x, (MODULUS_BITS / 8) as usize)
|
|
}
|
|
|
|
fn deserialize(&self, b: &[u8]) -> Option<Integer> {
|
|
if b.len() != (MODULUS_BITS / 8) as usize {
|
|
return None;
|
|
}
|
|
let x = bytes_to_int(b);
|
|
if self.is_valid(&x) {
|
|
Some(x)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn is_valid(&self, x: &Integer) -> bool {
|
|
x.cmp0() == std::cmp::Ordering::Greater && *x < self.n
|
|
}
|
|
}
|