pub const FSPRG_RECOMMENDED_SECPAR: u32 = 1536;
pub const FSPRG_RECOMMENDED_SEEDLEN: usize = 96 / 8;
pub fn mskinbytes(secpar: u32) -> usize {
assert!(is_valid_secpar(secpar));
2 + 2 * (secpar as usize / 2) / 8 }
pub fn mpkinbytes(secpar: u32) -> usize {
assert!(is_valid_secpar(secpar));
2 + secpar as usize / 8 }
pub fn stateinbytes(secpar: u32) -> usize {
assert!(is_valid_secpar(secpar));
2 + 2 * secpar as usize / 8 + 8 }
fn is_valid_secpar(secpar: u32) -> bool {
secpar % 16 == 0 && secpar >= 16 && secpar <= 16384
}
#[cfg(feature = "fss")]
mod inner {
use num_bigint::BigUint;
use num_traits::{One, Zero};
use rand::rngs::OsRng;
use rand::RngCore;
use sha2::{Digest, Sha256};
use zeroize::Zeroizing;
use super::*;
const RND_GEN_P: u32 = 0x01;
const RND_GEN_Q: u32 = 0x02;
const RND_GEN_X: u32 = 0x03;
fn mpi_export(buf: &mut [u8], x: &BigUint) {
let bytes = x.to_bytes_be();
assert!(bytes.len() <= buf.len());
let pad = buf.len() - bytes.len();
buf[..pad].fill(0);
buf[pad..].copy_from_slice(&bytes);
}
fn mpi_import(buf: &[u8]) -> BigUint {
BigUint::from_bytes_be(buf)
}
fn uint64_export(buf: &mut [u8], x: u64) {
assert!(buf.len() == 8);
buf.copy_from_slice(&x.to_be_bytes());
}
fn uint64_import(buf: &[u8]) -> u64 {
assert!(buf.len() == 8);
u64::from_be_bytes(buf.try_into().unwrap())
}
fn det_randomize(buf: &mut [u8], seed: &[u8], idx: u32) {
let mut remaining = buf.len();
let mut offset = 0;
let mut ctr: u32 = 0;
let mut base = Sha256::new();
base.update(seed);
base.update(idx.to_be_bytes());
while remaining > 0 {
let mut h = base.clone();
h.update(ctr.to_be_bytes());
let digest = h.finalize();
let cpylen = remaining.min(32);
buf[offset..offset + cpylen].copy_from_slice(&digest[..cpylen]);
offset += cpylen;
remaining -= cpylen;
ctr += 1;
}
}
fn store_secpar(buf: &mut [u8], secpar: u16) {
let val = secpar / 16 - 1;
buf[0] = (val >> 8) as u8;
buf[1] = val as u8;
}
fn read_secpar(buf: &[u8]) -> u16 {
let val = (buf[0] as u16) << 8 | buf[1] as u16;
16 * (val + 1)
}
fn genprime3mod4(bits: u32, seed: &[u8], idx: u32) -> BigUint {
use num_prime::nt_funcs::is_prime;
let buflen = bits as usize / 8;
assert!(bits % 8 == 0 && buflen > 0);
let mut buf = Zeroizing::new(vec![0u8; buflen]);
det_randomize(&mut buf, seed, idx);
buf[0] |= 0xc0; buf[buflen - 1] |= 0x03;
let mut p = mpi_import(&buf);
while !is_prime(&p, None).probably() {
p += 4u32;
}
p
}
fn gensquare(n: &BigUint, seed: &[u8], idx: u32, secpar: u32) -> BigUint {
let buflen = secpar as usize / 8;
let mut buf = Zeroizing::new(vec![0u8; buflen]);
det_randomize(&mut buf, seed, idx);
buf[0] &= 0x7f; let x = mpi_import(&buf);
assert!(x < *n);
x.modpow(&BigUint::from(2u32), n)
}
fn twopowmodphi(m: u64, p: &BigUint) -> BigUint {
let one = BigUint::one();
let phi = p - &one;
let mut nbits = 0u32;
while nbits < 64 && (1u64 << nbits) <= m {
nbits += 1;
}
let mut r = BigUint::one();
let mut n = nbits;
while n > 0 {
n -= 1;
r = (&r * &r) % φ
if m & (1u64 << n) != 0 {
r = &r + &r;
if r >= phi {
r -= φ
}
}
}
r
}
fn crt_decompose(x: &BigUint, p: &BigUint, q: &BigUint) -> (BigUint, BigUint) {
(x % p, x % q)
}
fn crt_compose(
xp: &BigUint,
xq: &BigUint,
p: &BigUint,
q: &BigUint,
) -> BigUint {
let a = if xq >= xp {
(xq - xp) % q
} else {
q - ((xp - xq) % q)
};
let u = mod_inverse(p, q).expect("p and q are coprime");
let a = (&a * &u) % q;
p * &a + xp
}
fn mod_inverse(a: &BigUint, m: &BigUint) -> Option<BigUint> {
use num_bigint::BigInt;
let a = BigInt::from(a.clone());
let m_int = BigInt::from(m.clone());
let mut old_r = a;
let mut r = m_int.clone();
let mut old_s = BigInt::one();
let mut s = BigInt::zero();
while !r.is_zero() {
let quotient = &old_r / &r;
let temp_r = r.clone();
r = &old_r - "ient * &r;
old_r = temp_r;
let temp_s = s.clone();
s = &old_s - "ient * &s;
old_s = temp_s;
}
if old_r != BigInt::one() {
return None;
}
let result = ((old_s % &m_int) + &m_int) % &m_int;
Some(result.to_biguint().unwrap())
}
pub fn gen_mk(
seed: Option<&[u8]>,
secpar: u32,
) -> (Zeroizing<Vec<u8>>, Vec<u8>) {
assert!(is_valid_secpar(secpar));
let owned_seed;
let seed = match seed {
Some(s) => s,
None => {
owned_seed = {
let mut buf = Zeroizing::new(vec![0u8; FSPRG_RECOMMENDED_SEEDLEN]);
OsRng.fill_bytes(&mut buf);
buf
};
&owned_seed
}
};
let secpar16 = secpar as u16;
let half = (secpar / 2) as usize;
let p = genprime3mod4(secpar / 2, seed, RND_GEN_P);
let q = genprime3mod4(secpar / 2, seed, RND_GEN_Q);
let msk_len = mskinbytes(secpar);
let mut msk = Zeroizing::new(vec![0u8; msk_len]);
store_secpar(&mut msk[0..2], secpar16);
mpi_export(&mut msk[2..2 + half / 8], &p);
mpi_export(&mut msk[2 + half / 8..2 + 2 * half / 8], &q);
let n = &p * &q;
let mpk_len = mpkinbytes(secpar);
let mut mpk = vec![0u8; mpk_len];
store_secpar(&mut mpk[0..2], secpar16);
mpi_export(&mut mpk[2..2 + secpar as usize / 8], &n);
(msk, mpk)
}
pub fn gen_state0(mpk: &[u8], seed: &[u8]) -> Zeroizing<Vec<u8>> {
let secpar = read_secpar(mpk) as u32;
let n_len = secpar as usize / 8;
let n = mpi_import(&mpk[2..2 + n_len]);
let x = gensquare(&n, seed, RND_GEN_X, secpar);
let state_len = stateinbytes(secpar);
let mut state = Zeroizing::new(vec![0u8; state_len]);
state[..2 + n_len].copy_from_slice(&mpk[..2 + n_len]);
mpi_export(&mut state[2 + n_len..2 + 2 * n_len], &x);
state
}
pub fn evolve(state: &mut [u8]) {
let secpar = read_secpar(state) as usize;
let n_len = secpar / 8;
let n = mpi_import(&state[2..2 + n_len]);
let x = mpi_import(&state[2 + n_len..2 + 2 * n_len]);
let epoch = uint64_import(&state[2 + 2 * n_len..2 + 2 * n_len + 8]);
let x_new = x.modpow(&BigUint::from(2u32), &n);
let epoch_new = epoch + 1;
mpi_export(&mut state[2 + n_len..2 + 2 * n_len], &x_new);
uint64_export(
&mut state[2 + 2 * n_len..2 + 2 * n_len + 8],
epoch_new,
);
}
pub fn get_epoch(state: &[u8]) -> u64 {
let secpar = read_secpar(state) as usize;
uint64_import(&state[2 + 2 * secpar / 8..2 + 2 * secpar / 8 + 8])
}
pub fn seek(
state: &mut Vec<u8>,
epoch: u64,
msk: &[u8],
seed: &[u8],
) {
let secpar = read_secpar(msk) as u32;
let half = (secpar / 2) as usize;
let n_len = secpar as usize / 8;
let p = mpi_import(&msk[2..2 + half / 8]);
let q = mpi_import(&msk[2 + half / 8..2 + 2 * half / 8]);
let n = &p * &q;
let x = gensquare(&n, seed, RND_GEN_X, secpar);
let (xp, xq) = crt_decompose(&x, &p, &q);
let kp = twopowmodphi(epoch, &p);
let kq = twopowmodphi(epoch, &q);
let xp = xp.modpow(&kp, &p);
let xq = xq.modpow(&kq, &q);
let xm = crt_compose(&xp, &xq, &p, &q);
let state_len = stateinbytes(secpar);
state.resize(state_len, 0);
store_secpar(&mut state[0..2], secpar as u16);
mpi_export(&mut state[2..2 + n_len], &n);
mpi_export(&mut state[2 + n_len..2 + 2 * n_len], &xm);
uint64_export(
&mut state[2 + 2 * n_len..2 + 2 * n_len + 8],
epoch,
);
}
pub fn get_key(state: &[u8], keylen: usize, idx: u32) -> Zeroizing<Vec<u8>> {
let secpar = read_secpar(state) as usize;
let seed_len = 2 * secpar / 8 + 8;
let seed = &state[2..2 + seed_len];
let mut key = Zeroizing::new(vec![0u8; keylen]);
det_randomize(&mut key, seed, idx);
key
}
}
#[cfg(feature = "fss")]
pub use inner::*;