use super::functions::*;
pub struct DiffieHellmanSim {
pub p: u64,
pub g: u64,
pub alice_priv: u64,
pub bob_priv: u64,
}
impl DiffieHellmanSim {
pub fn new(p: u64, g: u64, alice_priv: u64, bob_priv: u64) -> Self {
DiffieHellmanSim {
p,
g,
alice_priv,
bob_priv,
}
}
pub fn alice_public(&self) -> u64 {
mod_exp(self.g, self.alice_priv, self.p)
}
pub fn bob_public(&self) -> u64 {
mod_exp(self.g, self.bob_priv, self.p)
}
pub fn alice_shared_secret(&self) -> u64 {
mod_exp(self.bob_public(), self.alice_priv, self.p)
}
pub fn bob_shared_secret(&self) -> u64 {
mod_exp(self.alice_public(), self.bob_priv, self.p)
}
pub fn secrets_match(&self) -> bool {
self.alice_shared_secret() == self.bob_shared_secret()
}
}
pub struct ModularArithmetic {
pub modulus: u64,
}
impl ModularArithmetic {
pub fn new(modulus: u64) -> Self {
ModularArithmetic { modulus }
}
pub fn add(&self, a: u64, b: u64) -> u64 {
(a + b) % self.modulus
}
pub fn sub(&self, a: u64, b: u64) -> u64 {
(a + self.modulus - b % self.modulus) % self.modulus
}
pub fn mul(&self, a: u64, b: u64) -> u64 {
((a as u128 * b as u128) % self.modulus as u128) as u64
}
pub fn pow(&self, a: u64, e: u64) -> u64 {
mod_exp(a, e, self.modulus)
}
pub fn inv(&self, a: u64) -> Option<u64> {
mod_inverse(a, self.modulus)
}
pub fn legendre(&self, a: u64) -> i64 {
if a % self.modulus == 0 {
return 0;
}
let exp = (self.modulus - 1) / 2;
let result = self.pow(a % self.modulus, exp);
if result == 1 {
1
} else {
-1
}
}
}
#[derive(Debug, Clone)]
pub struct HashChain {
pub chain: Vec<u64>,
}
impl HashChain {
pub fn new(genesis: u64) -> Self {
HashChain {
chain: vec![genesis],
}
}
fn link_hash(prev: u64, data: u64) -> u64 {
let mut buf = [0u8; 16];
buf[..8].copy_from_slice(&prev.to_le_bytes());
buf[8..].copy_from_slice(&data.to_le_bytes());
simple_hash(&buf)
}
pub fn append(&mut self, data: u64) {
let prev = *self
.chain
.last()
.expect("chain is non-empty: initialized with genesis value");
self.chain.push(Self::link_hash(prev, data));
}
pub fn tip(&self) -> u64 {
*self
.chain
.last()
.expect("chain is non-empty: initialized with genesis value")
}
pub fn verify(&self, data: &[u64]) -> bool {
if data.len() + 1 != self.chain.len() {
return false;
}
for (i, &d) in data.iter().enumerate() {
let expected = Self::link_hash(self.chain[i], d);
if self.chain[i + 1] != expected {
return false;
}
}
true
}
}
pub struct ToyDiffieHellman {
pub p: u64,
pub g: u64,
}
impl ToyDiffieHellman {
pub fn public_key(&self, private: u64) -> u64 {
mod_exp(self.g, private, self.p)
}
pub fn shared_secret(&self, their_public: u64, my_private: u64) -> u64 {
mod_exp(their_public, my_private, self.p)
}
}
#[allow(dead_code)]
pub struct ToySchnorr {
pub p: u64,
pub q: u64,
pub g: u64,
}
#[allow(dead_code)]
impl ToySchnorr {
pub fn new(p: u64, q: u64, g: u64) -> Self {
ToySchnorr { p, q, g }
}
pub fn public_key(&self, x: u64) -> u64 {
mod_exp(self.g, x, self.p)
}
pub fn sign(&self, x: u64, k: u64, h: u64) -> (u64, u64) {
let r = mod_exp(self.g, k, self.p) % self.q;
let s = (k.wrapping_add(x.wrapping_mul(r).wrapping_add(h))) % self.q;
(r, s)
}
pub fn verify(&self, pk: u64, h: u64, r: u64, s: u64) -> bool {
let gs = mod_exp(self.g, s, self.p);
let pkh = mod_exp(pk, h, self.p);
let lhs = ((gs as u128 * pkh as u128) % self.p as u128) as u64;
lhs % self.q == r
}
}
pub struct ToyRsa {
pub n: u64,
pub e: u64,
pub d: u64,
}
impl ToyRsa {
pub fn generate(p: u64, q: u64) -> Option<Self> {
let n = p.checked_mul(q)?;
let pm1 = p - 1;
let qm1 = q - 1;
let g = {
let (gcd, _, _) = extended_gcd(pm1 as i64, qm1 as i64);
gcd.unsigned_abs()
};
let lambda_n = pm1 / g * qm1;
let candidates = [65537u64, 17, 3];
for &e in &candidates {
if e >= lambda_n {
continue;
}
if let Some(d) = mod_inverse(e, lambda_n) {
return Some(ToyRsa { n, e, d });
}
}
None
}
pub fn encrypt(&self, m: u64) -> u64 {
mod_exp(m, self.e, self.n)
}
pub fn decrypt(&self, c: u64) -> u64 {
mod_exp(c, self.d, self.n)
}
}
#[allow(dead_code)]
pub struct ToyPolyCommit {
pub tau: u64,
pub q: u64,
}
#[allow(dead_code)]
impl ToyPolyCommit {
pub fn new(tau: u64, q: u64) -> Self {
ToyPolyCommit { tau, q }
}
pub fn commit(&self, coeffs: &[u64]) -> u64 {
let q = self.q as u128;
let tau = self.tau as u128;
let mut power = 1u128;
let mut result = 0u128;
for &c in coeffs {
result = (result + c as u128 * power) % q;
power = power * tau % q;
}
result as u64
}
pub fn evaluate(&self, coeffs: &[u64], z: u64) -> u64 {
let q = self.q as u128;
let z = z as u128;
let mut power = 1u128;
let mut result = 0u128;
for &c in coeffs {
result = (result + c as u128 * power) % q;
power = power * z % q;
}
result as u64
}
pub fn verify_opening(&self, commitment: u64, z: u64, v: u64, coeffs: &[u64]) -> bool {
let actual = self.evaluate(coeffs, z);
let claimed_commit = self.commit(coeffs);
actual == v && claimed_commit == commitment
}
}
#[allow(dead_code)]
pub struct ToyLwe {
pub n: usize,
pub q: u64,
pub error_bound: u64,
}
#[allow(dead_code)]
impl ToyLwe {
pub fn new(n: usize, q: u64, error_bound: u64) -> Self {
ToyLwe { n, q, error_bound }
}
pub fn inner_product(&self, a: &[u64], s: &[u64]) -> u64 {
assert_eq!(a.len(), s.len());
let q = self.q as u128;
a.iter()
.zip(s.iter())
.fold(0u128, |acc, (&ai, &si)| (acc + ai as u128 * si as u128) % q) as u64
}
pub fn encrypt_bit(&self, secret: &[u64], bit: u8, noise: u64, a: &[u64]) -> (Vec<u64>, u64) {
let b = (self.inner_product(a, secret) + noise % self.q) % self.q;
let v = (b + (bit as u64) * (self.q / 2)) % self.q;
(a.to_vec(), v)
}
pub fn decrypt_bit(&self, secret: &[u64], a: &[u64], v: u64) -> u8 {
let dot = self.inner_product(a, secret);
let diff = (v + self.q - dot) % self.q;
if diff < self.q / 4 || diff > 3 * self.q / 4 {
0
} else {
1
}
}
}
pub struct ShamirSecretShare {
pub p: u64,
pub k: usize,
pub n: usize,
}
impl ShamirSecretShare {
pub fn new(p: u64, k: usize, n: usize) -> Self {
ShamirSecretShare { p, k, n }
}
fn eval_poly(&self, coeffs: &[u64], x: u64) -> u64 {
let mut result = 0u64;
for &c in coeffs.iter().rev() {
result = (((result as u128 * x as u128) % self.p as u128 + c as u128) % self.p as u128)
as u64;
}
result
}
pub fn share(&self, secret: u64, seed: u64) -> Vec<(u64, u64)> {
let mut coeffs = vec![secret % self.p];
for i in 1..self.k {
let c = (seed
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(i as u64 * 0x6c62272e07bb0142))
% self.p;
coeffs.push(c);
}
(1..=self.n as u64)
.map(|x| (x, self.eval_poly(&coeffs, x)))
.collect()
}
pub fn reconstruct(&self, shares: &[(u64, u64)]) -> Option<u64> {
if shares.len() < self.k {
return None;
}
let shares = &shares[..self.k];
let p = self.p;
let mut secret = 0u64;
for (i, &(xi, yi)) in shares.iter().enumerate() {
let mut num: u128 = 1;
let mut den: u128 = 1;
for (j, &(xj, _)) in shares.iter().enumerate() {
if i != j {
num = num * ((p - xj % p) as u128) % p as u128;
let diff = ((xi as i128 - xj as i128).rem_euclid(p as i128)) as u64;
den = den * diff as u128 % p as u128;
}
}
let den_inv = mod_inverse(den as u64, p)?;
let li = (num as u64 * den_inv % p * yi % p) % p;
secret = (secret + li) % p;
}
Some(secret)
}
}
pub struct RsaKeyGen;
impl RsaKeyGen {
pub fn next_prime(start: u64) -> u64 {
let witnesses = [2u64, 3, 5, 7, 11, 13];
let mut candidate = if start % 2 == 0 { start + 1 } else { start };
loop {
if miller_rabin(candidate, &witnesses) {
return candidate;
}
candidate += 2;
}
}
pub fn generate_from_seed(seed: u64) -> Option<(ToyRsa, u64, u64)> {
let p = Self::next_prime(seed);
let q = Self::next_prime(p + 2);
let rsa = ToyRsa::generate(p, q)?;
Some((rsa, p, q))
}
}
#[allow(dead_code)]
pub struct RingZq {
pub n: usize,
pub q: u64,
}
#[allow(dead_code)]
impl RingZq {
pub fn new(n: usize, q: u64) -> Self {
RingZq { n, q }
}
pub fn reduce(&self, poly: &[u64]) -> Vec<u64> {
poly.iter().map(|&c| c % self.q).collect()
}
pub fn add(&self, a: &[u64], b: &[u64]) -> Vec<u64> {
assert_eq!(a.len(), self.n);
assert_eq!(b.len(), self.n);
a.iter()
.zip(b.iter())
.map(|(&ai, &bi)| (ai + bi) % self.q)
.collect()
}
pub fn sub(&self, a: &[u64], b: &[u64]) -> Vec<u64> {
assert_eq!(a.len(), self.n);
assert_eq!(b.len(), self.n);
a.iter()
.zip(b.iter())
.map(|(&ai, &bi)| (ai + self.q - bi % self.q) % self.q)
.collect()
}
pub fn mul(&self, a: &[u64], b: &[u64]) -> Vec<u64> {
assert_eq!(a.len(), self.n);
assert_eq!(b.len(), self.n);
let mut result = vec![0i128; self.n];
let q = self.q as i128;
for i in 0..self.n {
for j in 0..self.n {
let idx = i + j;
let coeff = a[i] as i128 * b[j] as i128 % q;
if idx < self.n {
result[idx] = (result[idx] + coeff) % q;
} else {
result[idx - self.n] = (result[idx - self.n] - coeff + q) % q;
}
}
}
result.iter().map(|&c| c as u64).collect()
}
pub fn linf_norm(&self, poly: &[u64]) -> u64 {
let half_q = self.q / 2;
poly.iter()
.map(|&c| {
let c = c % self.q;
if c <= half_q {
c
} else {
self.q - c
}
})
.max()
.unwrap_or(0)
}
}