use super::functions::*;
#[derive(Debug, Clone)]
pub struct SchnorrParams {
pub p: u64,
pub q: u64,
pub g: u64,
}
impl SchnorrParams {
pub fn commit(&self, r: u64) -> u64 {
mod_exp(self.g, r % self.q, self.p)
}
pub fn respond(&self, r: u64, challenge: u64, secret_x: u64) -> u64 {
let r = r % self.q;
let e_x = (challenge as u128 * secret_x as u128 % self.q as u128) as u64;
(r + e_x) % self.q
}
pub fn verify(&self, transcript: &SchnorrTranscript, public_y: u64) -> bool {
let lhs = mod_exp(self.g, transcript.response, self.p);
let ye = mod_exp(public_y, transcript.challenge, self.p);
let rhs = (transcript.commitment as u128 * ye as u128 % self.p as u128) as u64;
lhs == rhs
}
pub fn prove(&self, secret_x: u64, r: u64, challenge: u64) -> SchnorrTranscript {
let commitment = self.commit(r);
let response = self.respond(r, challenge, secret_x);
SchnorrTranscript {
commitment,
challenge,
response,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum MPCSecurityModel {
SemiHonest,
Malicious,
Covert,
}
#[derive(Debug, Clone)]
pub struct ShamirSS {
pub p: u64,
pub t: usize,
pub n: usize,
}
impl ShamirSS {
pub fn split(&self, secret: u64, coeffs: &[u64]) -> Vec<(u64, u64)> {
assert_eq!(
coeffs.len(),
self.t - 1,
"Need exactly t-1 random coefficients"
);
(1..=(self.n as u64))
.map(|i| {
let mut val: u128 = secret as u128;
let mut ipow: u128 = i as u128;
for &c in coeffs {
val = (val + c as u128 * ipow) % self.p as u128;
ipow = ipow * i as u128 % self.p as u128;
}
(i, val as u64)
})
.collect()
}
pub fn reconstruct(&self, shares: &[(u64, u64)]) -> u64 {
assert!(shares.len() >= self.t, "Need at least t shares");
let shares = &shares[..self.t];
let p = self.p;
let mut secret: i128 = 0;
for (j, &(xj, yj)) in shares.iter().enumerate() {
let mut num: i128 = 1;
let mut den: i128 = 1;
for (k, &(xk, _)) in shares.iter().enumerate() {
if k == j {
continue;
}
num = num * (-(xk as i128)) % p as i128;
den = den * (xj as i128 - xk as i128) % p as i128;
}
let den_inv =
mod_inv(((den % p as i128 + p as i128) % p as i128) as u64, p).unwrap_or(0) as i128;
let lagrange = num * den_inv % p as i128;
secret = (secret + yj as i128 * lagrange) % p as i128;
}
((secret % p as i128 + p as i128) % p as i128) as u64
}
}
#[derive(Debug, Clone)]
pub struct ToyOT {
pub p: u64,
pub g: u64,
}
impl ToyOT {
pub fn sender_setup(&self, a: u64) -> u64 {
mod_exp(self.g, a, self.p)
}
pub fn receiver_choose(&self, c: u64, b: u8, k: u64) -> (u64, u64) {
let gk = mod_exp(self.g, k, self.p);
let gk_inv = mod_inv(gk, self.p).unwrap_or(1);
let other = (c as u128 * gk_inv as u128 % self.p as u128) as u64;
if b == 0 {
(gk, other)
} else {
(other, gk)
}
}
pub fn receiver_key(&self, c: u64, b: u8, k: u64) -> u64 {
let _ = b;
mod_exp(c, k, self.p)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ObliviousTransfer {
pub variant: OTVariant,
pub security_parameter: usize,
}
#[allow(dead_code)]
impl ObliviousTransfer {
pub fn new(variant: OTVariant, sec: usize) -> Self {
ObliviousTransfer {
variant,
security_parameter: sec,
}
}
pub fn is_fundamental(&self) -> bool {
matches!(self.variant, OTVariant::OneOutOfTwo)
}
pub fn n_messages(&self) -> usize {
match &self.variant {
OTVariant::OneOutOfTwo => 2,
OTVariant::OneOutOfN(n) => *n,
OTVariant::RandomOT => 2,
}
}
}
#[derive(Debug, Clone)]
pub struct PedersenCommitment {
pub p: u64,
pub q: u64,
pub g: u64,
pub h: u64,
}
impl PedersenCommitment {
pub fn commit(&self, m: u64, r: u64) -> u64 {
let gm = mod_exp(self.g, m % self.q, self.p);
let hr = mod_exp(self.h, r % self.q, self.p);
(gm as u128 * hr as u128 % self.p as u128) as u64
}
pub fn verify(&self, c: u64, m: u64, r: u64) -> bool {
self.commit(m, r) == c
}
pub fn add(&self, c1: u64, c2: u64) -> u64 {
(c1 as u128 * c2 as u128 % self.p as u128) as u64
}
pub fn batch_commit(&self, pairs: &[(u64, u64)]) -> Vec<u64> {
pairs.iter().map(|&(m, r)| self.commit(m, r)).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ZKProofSystem {
pub name: String,
pub is_interactive: bool,
pub soundness_error: f64,
pub completeness_error: f64,
pub proof_size_bytes: Option<usize>,
}
#[allow(dead_code)]
impl ZKProofSystem {
pub fn new(name: &str, interactive: bool) -> Self {
ZKProofSystem {
name: name.to_string(),
is_interactive: interactive,
soundness_error: 0.5,
completeness_error: 0.0,
proof_size_bytes: None,
}
}
pub fn schnorr() -> Self {
let mut s = ZKProofSystem::new("Schnorr", true);
s.soundness_error = 1.0 / 2.0_f64.powi(128);
s.proof_size_bytes = Some(64);
s
}
pub fn groth16() -> Self {
let mut s = ZKProofSystem::new("Groth16", false);
s.soundness_error = 1.0 / 2.0_f64.powi(128);
s.proof_size_bytes = Some(128);
s
}
pub fn plonk() -> Self {
let mut s = ZKProofSystem::new("PLONK", false);
s.soundness_error = 1.0 / 2.0_f64.powi(128);
s.proof_size_bytes = Some(640);
s
}
pub fn is_non_interactive(&self) -> bool {
!self.is_interactive
}
pub fn is_succinct(&self) -> bool {
matches!(self.proof_size_bytes, Some(n) if n < 1000)
}
}
#[derive(Debug, Clone)]
pub struct ShamirSecretSharingExtended {
pub p: u64,
pub t: usize,
pub n: usize,
}
impl ShamirSecretSharingExtended {
pub fn share(&self, secret: u64, coeffs: &[u64]) -> Vec<(u64, u64)> {
assert_eq!(coeffs.len(), self.t - 1, "Need t-1 coefficients");
(1..=self.n as u64)
.map(|i| {
let mut val: u128 = secret as u128 % self.p as u128;
let mut ipow: u128 = i as u128;
for &c in coeffs {
val = (val + c as u128 % self.p as u128 * ipow) % self.p as u128;
ipow = ipow * i as u128 % self.p as u128;
}
(i, val as u64)
})
.collect()
}
pub fn reconstruct(&self, shares: &[(u64, u64)]) -> u64 {
assert!(shares.len() >= self.t);
let shares = &shares[..self.t];
let p = self.p;
let mut acc: i128 = 0;
for (j, &(xj, yj)) in shares.iter().enumerate() {
let mut num: i128 = 1;
let mut den: i128 = 1;
for (k, &(xk, _)) in shares.iter().enumerate() {
if k == j {
continue;
}
num = num * (p as i128 - xk as i128) % p as i128;
den = den * ((xj as i128 - xk as i128).rem_euclid(p as i128)) % p as i128;
}
let den_pos = den.rem_euclid(p as i128) as u64;
let inv = mod_inv(den_pos, p).unwrap_or(0) as i128;
let lagrange = num % p as i128 * inv % p as i128;
acc = (acc + yj as i128 * lagrange % p as i128).rem_euclid(p as i128);
}
acc as u64
}
}
#[derive(Debug, Clone)]
pub struct GarbledGate {
pub table: [[u64; 2]; 2],
pub labels_a: [u64; 2],
pub labels_b: [u64; 2],
pub labels_out: [u64; 2],
}
impl GarbledGate {
pub fn garble_and(labels_a: [u64; 2], labels_b: [u64; 2], labels_out: [u64; 2]) -> Self {
let mut table = [[0u64; 2]; 2];
for a in 0usize..2 {
for b in 0usize..2 {
let out_bit = a & b;
table[a][b] = toy_encrypt(labels_a[a], labels_b[b], labels_out[out_bit]);
}
}
GarbledGate {
table,
labels_a,
labels_b,
labels_out,
}
}
pub fn garble_or(labels_a: [u64; 2], labels_b: [u64; 2], labels_out: [u64; 2]) -> Self {
let mut table = [[0u64; 2]; 2];
for a in 0usize..2 {
for b in 0usize..2 {
let out_bit = a | b;
table[a][b] = toy_encrypt(labels_a[a], labels_b[b], labels_out[out_bit]);
}
}
GarbledGate {
table,
labels_a,
labels_b,
labels_out,
}
}
pub fn evaluate(&self, label_a: u64, label_b: u64) -> Option<u64> {
for a in 0usize..2 {
for b in 0usize..2 {
if self.labels_a[a] == label_a && self.labels_b[b] == label_b {
let out = toy_encrypt(label_a, label_b, self.table[a][b]);
return Some(out);
}
}
}
None
}
pub fn is_output_one(&self, output_label: u64) -> bool {
output_label == self.labels_out[1]
}
}
#[derive(Debug, Clone)]
pub struct PedersenParams {
pub p: u64,
pub q: u64,
pub g: u64,
pub h: u64,
}
impl PedersenParams {
pub fn commit(&self, m: u64, r: u64) -> u64 {
let gm = mod_exp(self.g, m % self.q, self.p);
let hr = mod_exp(self.h, r % self.q, self.p);
(gm as u128 * hr as u128 % self.p as u128) as u64
}
pub fn verify(&self, c: u64, m: u64, r: u64) -> bool {
self.commit(m, r) == c
}
pub fn add_commitments(&self, c1: u64, c2: u64) -> u64 {
(c1 as u128 * c2 as u128 % self.p as u128) as u64
}
}
#[derive(Debug, Clone)]
pub struct MpcShare {
pub party: u8,
pub share: bool,
}
impl MpcShare {
pub fn xor_gate(a: &MpcShare, b: &MpcShare) -> MpcShare {
assert_eq!(a.party, b.party);
MpcShare {
party: a.party,
share: a.share ^ b.share,
}
}
pub fn reconstruct(s0: &MpcShare, s1: &MpcShare) -> bool {
s0.share ^ s1.share
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum OTVariant {
OneOutOfTwo,
OneOutOfN(usize),
RandomOT,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CommitmentScheme {
pub name: String,
pub is_perfectly_hiding: bool,
pub is_computationally_binding: bool,
pub is_homomorphic: bool,
}
#[allow(dead_code)]
impl CommitmentScheme {
pub fn new(name: &str) -> Self {
CommitmentScheme {
name: name.to_string(),
is_perfectly_hiding: false,
is_computationally_binding: false,
is_homomorphic: false,
}
}
pub fn pedersen() -> Self {
CommitmentScheme {
name: "Pedersen".to_string(),
is_perfectly_hiding: true,
is_computationally_binding: true,
is_homomorphic: true,
}
}
pub fn sha256_hash() -> Self {
CommitmentScheme {
name: "SHA256-hash".to_string(),
is_perfectly_hiding: false,
is_computationally_binding: true,
is_homomorphic: false,
}
}
pub fn satisfies_binding_hiding_tradeoff(&self) -> bool {
!self.is_perfectly_hiding || !self.is_computationally_binding
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MPCProtocol {
pub name: String,
pub n_parties: usize,
pub threshold_corruption: usize,
pub security_model: MPCSecurityModel,
}
#[allow(dead_code)]
impl MPCProtocol {
pub fn new(name: &str, n: usize, t: usize, model: MPCSecurityModel) -> Self {
MPCProtocol {
name: name.to_string(),
n_parties: n,
threshold_corruption: t,
security_model: model,
}
}
pub fn bgw(n: usize, t: usize) -> Self {
MPCProtocol::new("BGW", n, t, MPCSecurityModel::Malicious)
}
pub fn is_secure_against_majority_corruption(&self) -> bool {
self.threshold_corruption * 2 < self.n_parties
}
pub fn is_optimal_corruption_threshold(&self) -> bool {
match self.security_model {
MPCSecurityModel::Malicious => self.threshold_corruption * 3 < self.n_parties,
MPCSecurityModel::SemiHonest => self.threshold_corruption * 2 < self.n_parties,
MPCSecurityModel::Covert => self.threshold_corruption * 2 < self.n_parties,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SecretSharing {
pub threshold: usize,
pub n_shares: usize,
pub field_size_bits: usize,
}
#[allow(dead_code)]
impl SecretSharing {
pub fn new(t: usize, n: usize, field_bits: usize) -> Self {
assert!(t <= n);
SecretSharing {
threshold: t,
n_shares: n,
field_size_bits: field_bits,
}
}
pub fn shamir_2_of_3() -> Self {
SecretSharing::new(2, 3, 256)
}
pub fn is_perfect(&self) -> bool {
true
}
pub fn min_shares_needed(&self) -> usize {
self.threshold
}
pub fn share_size_bits(&self) -> usize {
self.field_size_bits
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SchnorrTranscript {
pub commitment: u64,
pub challenge: u64,
pub response: u64,
}
#[derive(Debug, Clone)]
pub struct PaillierHomomorphic {
pub n: u64,
pub n_sq: u128,
pub g: u64,
pub lambda: u64,
pub mu: u64,
}
impl PaillierHomomorphic {
pub fn encrypt(&self, m: u64, r: u64) -> u128 {
let n_sq = self.n_sq;
let gm = {
let base = (self.g as u128).pow(1) % n_sq;
let mut result: u128 = 1;
let mut exp = m;
let mut b = self.g as u128 % n_sq;
while exp > 0 {
if exp & 1 == 1 {
result = result * b % n_sq;
}
exp >>= 1;
b = b * b % n_sq;
}
let _ = base;
result
};
let rn = {
let mut result: u128 = 1;
let mut exp = self.n;
let mut b = r as u128 % n_sq;
while exp > 0 {
if exp & 1 == 1 {
result = result * b % n_sq;
}
exp >>= 1;
b = b * b % n_sq;
}
result
};
gm * rn % n_sq
}
pub fn add_ciphertexts(&self, c1: u128, c2: u128) -> u128 {
c1 * c2 % self.n_sq
}
fn l_func(&self, x: u128) -> u64 {
((x - 1) / self.n as u128) as u64
}
pub fn decrypt(&self, c: u128) -> u64 {
let n_sq = self.n_sq;
let mut result: u128 = 1;
let mut exp = self.lambda;
let mut b = c % n_sq;
while exp > 0 {
if exp & 1 == 1 {
result = result * b % n_sq;
}
exp >>= 1;
b = b * b % n_sq;
}
let lval = self.l_func(result);
(lval as u128 * self.mu as u128 % self.n as u128) as u64
}
}
#[derive(Debug, Clone)]
pub struct BlindSignatureScheme {
pub n: u64,
pub e: u64,
pub d: u64,
}
impl BlindSignatureScheme {
pub fn blind(&self, m: u64, r: u64) -> u64 {
let re = mod_exp(r, self.e, self.n);
(re as u128 * m as u128 % self.n as u128) as u64
}
pub fn sign_blinded(&self, blinded: u64) -> u64 {
mod_exp(blinded, self.d, self.n)
}
pub fn unblind(&self, s_prime: u64, r: u64) -> u64 {
let r_inv = mod_inv(r, self.n).unwrap_or(1);
(s_prime as u128 * r_inv as u128 % self.n as u128) as u64
}
pub fn verify(&self, m: u64, s: u64) -> bool {
mod_exp(s, self.e, self.n) == m % self.n
}
}