mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-03-05 00:07:33 +08:00
526 lines
16 KiB
Rust
526 lines
16 KiB
Rust
pub mod rdkgith;
|
|
pub mod utils;
|
|
pub mod pke;
|
|
pub mod ve;
|
|
pub mod seed_tree;
|
|
|
|
use std::convert::TryFrom;
|
|
use std::convert::TryInto;
|
|
|
|
use ed448_goldilocks_plus::CompressedEdwardsY;
|
|
use ed448_goldilocks_plus::EdwardsPoint;
|
|
use ed448_goldilocks_plus::Scalar;
|
|
use rand::rngs::OsRng;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub use crate::rdkgith::*;
|
|
pub use crate::utils::*;
|
|
pub use crate::pke::*;
|
|
pub use crate::ve::*;
|
|
pub use crate::seed_tree::*;
|
|
|
|
uniffi::include_scaffolding!("lib");
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct VerencCiphertext {
|
|
pub c1: Vec<u8>,
|
|
pub c2: Vec<u8>,
|
|
pub i: u64,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct VerencShare {
|
|
pub s1: Vec<u8>,
|
|
pub s2: Vec<u8>,
|
|
pub i: u64,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct VerencProofAndBlindingKey {
|
|
pub blinding_key: Vec<u8>,
|
|
pub blinding_pubkey: Vec<u8>,
|
|
pub decryption_key: Vec<u8>,
|
|
pub encryption_key: Vec<u8>,
|
|
pub statement: Vec<u8>,
|
|
pub challenge: Vec<u8>,
|
|
pub polycom: Vec<Vec<u8>>,
|
|
pub ctexts: Vec<VerencCiphertext>,
|
|
pub shares_rands: Vec<VerencShare>,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct VerencDecrypt {
|
|
pub blinding_pubkey: Vec<u8>,
|
|
pub decryption_key: Vec<u8>,
|
|
pub statement: Vec<u8>,
|
|
pub ciphertexts: CompressedCiphertext,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct VerencProof {
|
|
pub blinding_pubkey: Vec<u8>,
|
|
pub encryption_key: Vec<u8>,
|
|
pub statement: Vec<u8>,
|
|
pub challenge: Vec<u8>,
|
|
pub polycom: Vec<Vec<u8>>,
|
|
pub ctexts: Vec<VerencCiphertext>,
|
|
pub shares_rands: Vec<VerencShare>,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct CompressedCiphertext {
|
|
pub ctexts: Vec<VerencCiphertext>,
|
|
pub aux: Vec<Vec<u8>>,
|
|
}
|
|
|
|
pub fn new_verenc_proof(data: Vec<u8>) -> VerencProofAndBlindingKey {
|
|
if data.len() != 56 {
|
|
return VerencProofAndBlindingKey{
|
|
blinding_key: vec![],
|
|
blinding_pubkey: vec![],
|
|
decryption_key: vec![],
|
|
encryption_key: vec![],
|
|
statement: vec![],
|
|
challenge: vec![],
|
|
polycom: vec![],
|
|
ctexts: vec![],
|
|
shares_rands: vec![],
|
|
};
|
|
}
|
|
|
|
let blind = Scalar::random(&mut OsRng);
|
|
let params = CurveParams::init(EdwardsPoint::GENERATOR * blind);
|
|
let pke = Elgamal::setup(¶ms);
|
|
let (N, t, n) = RVE_PARAMS[0];
|
|
let vparams = RDkgithParams{ N, t, n };
|
|
let mut ve = RDkgith::setup(¶ms, &vparams, pke.clone());
|
|
|
|
let dk = ve.kgen();
|
|
let (stm, wit) = ve.igen(&data.try_into().unwrap());
|
|
let pi = ve.prove(&stm, &wit);
|
|
|
|
return VerencProofAndBlindingKey {
|
|
blinding_key: blind.to_bytes().to_vec(),
|
|
blinding_pubkey: (EdwardsPoint::GENERATOR * blind).compress().to_bytes().to_vec(),
|
|
decryption_key: dk.to_bytes().to_vec(),
|
|
encryption_key: (EdwardsPoint::GENERATOR * dk).compress().to_bytes().to_vec(),
|
|
statement: stm.compress().to_bytes().to_vec(),
|
|
challenge: pi.challenge,
|
|
polycom: pi.polycom.iter().map(|p| p.compress().to_bytes().to_vec()).collect(),
|
|
ctexts: pi.ctexts.iter().map(|c| VerencCiphertext {
|
|
c1: c.0.c1.compress().to_bytes().to_vec(),
|
|
c2: c.0.c2.to_bytes().to_vec(),
|
|
i: c.1 as u64,
|
|
}).collect(),
|
|
shares_rands: pi.shares_rands.iter().map(|s| VerencShare {
|
|
s1: s.0.to_bytes().to_vec(),
|
|
s2: s.1.to_bytes().to_vec(),
|
|
i: s.2 as u64,
|
|
}).collect(),
|
|
};
|
|
}
|
|
|
|
pub fn new_verenc_proof_encrypt_only(data: Vec<u8>, encryption_key_bytes: Vec<u8>) -> VerencProofAndBlindingKey {
|
|
if data.len() != 56 {
|
|
return VerencProofAndBlindingKey{
|
|
blinding_key: vec![],
|
|
blinding_pubkey: vec![],
|
|
decryption_key: vec![],
|
|
encryption_key: vec![],
|
|
statement: vec![],
|
|
challenge: vec![],
|
|
polycom: vec![],
|
|
ctexts: vec![],
|
|
shares_rands: vec![],
|
|
};
|
|
}
|
|
|
|
let encryption_key = point_from_bytes(encryption_key_bytes.clone());
|
|
if encryption_key.is_none() {
|
|
return VerencProofAndBlindingKey{
|
|
blinding_key: vec![],
|
|
blinding_pubkey: vec![],
|
|
decryption_key: vec![],
|
|
encryption_key: vec![],
|
|
statement: vec![],
|
|
challenge: vec![],
|
|
polycom: vec![],
|
|
ctexts: vec![],
|
|
shares_rands: vec![],
|
|
};
|
|
}
|
|
|
|
let blind = Scalar::random(&mut OsRng);
|
|
let params = CurveParams::init(EdwardsPoint::GENERATOR * blind);
|
|
let pke = Elgamal::setup(¶ms);
|
|
let (N, t, n) = RVE_PARAMS[0];
|
|
let vparams = RDkgithParams{ N, t, n };
|
|
let mut ve = RDkgith::setup(¶ms, &vparams, pke.clone());
|
|
|
|
ve.set_ek(PKEPublicKey{
|
|
ek: encryption_key.unwrap(),
|
|
});
|
|
let (stm, wit) = ve.igen(&data.try_into().unwrap());
|
|
let pi = ve.prove(&stm, &wit);
|
|
|
|
return VerencProofAndBlindingKey {
|
|
blinding_key: blind.to_bytes().to_vec(),
|
|
blinding_pubkey: (EdwardsPoint::GENERATOR * blind).compress().to_bytes().to_vec(),
|
|
decryption_key: vec![],
|
|
encryption_key: encryption_key_bytes.clone(),
|
|
statement: stm.compress().to_bytes().to_vec(),
|
|
challenge: pi.challenge,
|
|
polycom: pi.polycom.iter().map(|p| p.compress().to_bytes().to_vec()).collect(),
|
|
ctexts: pi.ctexts.iter().map(|c| VerencCiphertext {
|
|
c1: c.0.c1.compress().to_bytes().to_vec(),
|
|
c2: c.0.c2.to_bytes().to_vec(),
|
|
i: c.1 as u64,
|
|
}).collect(),
|
|
shares_rands: pi.shares_rands.iter().map(|s| VerencShare {
|
|
s1: s.0.to_bytes().to_vec(),
|
|
s2: s.1.to_bytes().to_vec(),
|
|
i: s.2 as u64,
|
|
}).collect(),
|
|
};
|
|
}
|
|
|
|
fn point_from_bytes(bytes: Vec<u8>) -> Option<EdwardsPoint> {
|
|
if bytes.len() != 57 {
|
|
return None;
|
|
}
|
|
|
|
let key_bytes: Result<[u8; 57], _> = bytes.try_into();
|
|
if key_bytes.is_err() {
|
|
return None;
|
|
}
|
|
|
|
let compressed_key = CompressedEdwardsY::try_from(key_bytes.unwrap());
|
|
if compressed_key.is_err() {
|
|
return None;
|
|
}
|
|
|
|
let key = compressed_key.unwrap().decompress();
|
|
if key.is_none().into() {
|
|
return None;
|
|
}
|
|
|
|
return Some(key.unwrap());
|
|
}
|
|
|
|
pub fn verenc_verify(proof: VerencProof) -> bool {
|
|
let blinding_key = point_from_bytes(proof.blinding_pubkey);
|
|
if blinding_key.is_none() {
|
|
return false;
|
|
}
|
|
|
|
let statement = point_from_bytes(proof.statement);
|
|
if statement.is_none() {
|
|
return false;
|
|
}
|
|
|
|
let encryption_key = point_from_bytes(proof.encryption_key);
|
|
if encryption_key.is_none() {
|
|
return false;
|
|
}
|
|
|
|
let mut polycom: Vec<EdwardsPoint> = Vec::new();
|
|
for p in proof.polycom {
|
|
let com = point_from_bytes(p);
|
|
if com.is_none() {
|
|
return false;
|
|
}
|
|
|
|
polycom.push(com.unwrap());
|
|
}
|
|
|
|
let mut ctexts: Vec<(PKECipherText, usize)> = Vec::new();
|
|
for c in proof.ctexts {
|
|
let c1 = point_from_bytes(c.c1);
|
|
if c1.is_none() {
|
|
return false;
|
|
}
|
|
|
|
if c.c2.len() != 56 {
|
|
return false;
|
|
}
|
|
|
|
let c2 = Scalar::from_bytes(&c.c2.try_into().unwrap());
|
|
ctexts.push((PKECipherText{c1: c1.unwrap(), c2: c2}, c.i as usize));
|
|
}
|
|
|
|
let mut shares: Vec<(Scalar, Scalar, usize)> = Vec::new();
|
|
for s in proof.shares_rands {
|
|
if s.s1.len() != 56 {
|
|
return false;
|
|
}
|
|
|
|
if s.s2.len() != 56 {
|
|
return false;
|
|
}
|
|
|
|
let s1 = Scalar::from_bytes(&s.s1.try_into().unwrap());
|
|
let s2 = Scalar::from_bytes(&s.s2.try_into().unwrap());
|
|
shares.push((s1, s2, s.i as usize));
|
|
}
|
|
|
|
let params = CurveParams::init(blinding_key.unwrap());
|
|
let pke = Elgamal::setup(¶ms);
|
|
let (N, t, n) = RVE_PARAMS[0];
|
|
let vparams = RDkgithParams{ N, t, n };
|
|
let mut ve = RDkgith::setup(¶ms, &vparams, pke.clone());
|
|
ve.set_ek(PKEPublicKey{
|
|
ek: encryption_key.unwrap(),
|
|
});
|
|
|
|
return ve.verify(&statement.unwrap(), &RDkgithProof{
|
|
challenge: proof.challenge,
|
|
polycom: polycom,
|
|
ctexts: ctexts,
|
|
shares_rands: shares,
|
|
});
|
|
}
|
|
|
|
|
|
pub fn verenc_compress(proof: VerencProof) -> CompressedCiphertext {
|
|
let blinding_key = point_from_bytes(proof.blinding_pubkey);
|
|
if blinding_key.is_none() {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
let statement = point_from_bytes(proof.statement);
|
|
if statement.is_none() {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
let encryption_key = point_from_bytes(proof.encryption_key);
|
|
if encryption_key.is_none() {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
let mut polycom: Vec<EdwardsPoint> = Vec::new();
|
|
for p in proof.polycom {
|
|
let com = point_from_bytes(p);
|
|
if com.is_none() {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
polycom.push(com.unwrap());
|
|
}
|
|
|
|
let mut ctexts: Vec<(PKECipherText, usize)> = Vec::new();
|
|
for c in proof.ctexts {
|
|
let c1 = point_from_bytes(c.c1);
|
|
if c1.is_none() {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
if c.c2.len() != 56 {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
let c2 = Scalar::from_bytes(&c.c2.try_into().unwrap());
|
|
ctexts.push((PKECipherText{c1: c1.unwrap(), c2: c2}, c.i as usize));
|
|
}
|
|
|
|
let mut shares: Vec<(Scalar, Scalar, usize)> = Vec::new();
|
|
for s in proof.shares_rands {
|
|
if s.s1.len() != 56 {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
if s.s2.len() != 56 {
|
|
return CompressedCiphertext{
|
|
ctexts: vec![],
|
|
aux: vec![],
|
|
};
|
|
}
|
|
|
|
let s1 = Scalar::from_bytes(&s.s1.try_into().unwrap());
|
|
let s2 = Scalar::from_bytes(&s.s2.try_into().unwrap());
|
|
shares.push((s1, s2, s.i as usize));
|
|
}
|
|
|
|
let params = CurveParams::init(blinding_key.unwrap());
|
|
let pke = Elgamal::setup(¶ms);
|
|
let (N, t, n) = RVE_PARAMS[0];
|
|
let vparams = RDkgithParams{ N, t, n };
|
|
let mut ve = RDkgith::setup(¶ms, &vparams, pke.clone());
|
|
ve.set_ek(PKEPublicKey{
|
|
ek: encryption_key.unwrap(),
|
|
});
|
|
let ve_ct = ve.compress(&statement.unwrap(), &RDkgithProof{
|
|
challenge: proof.challenge,
|
|
polycom: polycom,
|
|
ctexts: ctexts,
|
|
shares_rands: shares,
|
|
});
|
|
return CompressedCiphertext{
|
|
ctexts: ve_ct.ctexts.iter().map(|v| VerencCiphertext{
|
|
c1: v.c1.compress().to_bytes().to_vec(),
|
|
c2: v.c2.to_bytes().to_vec(),
|
|
i: 0,
|
|
}).collect(),
|
|
aux: ve_ct.aux.iter().map(|a| a.to_bytes().to_vec()).collect(),
|
|
};
|
|
}
|
|
|
|
pub fn verenc_recover(recovery: VerencDecrypt) -> Vec<u8> {
|
|
let blinding_key = point_from_bytes(recovery.blinding_pubkey);
|
|
if blinding_key.is_none() {
|
|
return vec![];
|
|
}
|
|
|
|
let statement = point_from_bytes(recovery.statement);
|
|
if statement.is_none() {
|
|
return vec![];
|
|
}
|
|
|
|
if recovery.decryption_key.len() != 56 {
|
|
return vec![];
|
|
}
|
|
|
|
let decryption_key = Scalar::from_bytes(&recovery.decryption_key.try_into().unwrap());
|
|
let mut ctexts: Vec<PKECipherText> = Vec::new();
|
|
let mut aux: Vec<Scalar> = Vec::new();
|
|
for c in recovery.ciphertexts.ctexts {
|
|
let c1 = point_from_bytes(c.c1);
|
|
if c1.is_none() {
|
|
return vec![];
|
|
}
|
|
|
|
if c.c2.len() != 56 {
|
|
return vec![];
|
|
}
|
|
|
|
let c2 = Scalar::from_bytes(&c.c2.try_into().unwrap());
|
|
ctexts.push(PKECipherText{c1: c1.unwrap(), c2: c2});
|
|
}
|
|
|
|
for c in recovery.ciphertexts.aux {
|
|
if c.len() != 56 {
|
|
return vec![];
|
|
}
|
|
|
|
let a = Scalar::from_bytes(&c.try_into().unwrap());
|
|
aux.push(a);
|
|
}
|
|
|
|
let ve_ct = RDkgithCipherText{
|
|
ctexts: ctexts,
|
|
aux: aux,
|
|
};
|
|
|
|
let params = CurveParams::init(blinding_key.unwrap());
|
|
let pke = Elgamal::setup(¶ms);
|
|
let (N, t, n) = RVE_PARAMS[0];
|
|
let vparams = RDkgithParams{ N, t, n };
|
|
let mut ve = RDkgith::setup(¶ms, &vparams, pke.clone());
|
|
let wit_recover = ve.recover(&statement.unwrap(), &decryption_key, &ve_ct);
|
|
return wit_recover.to_bytes().to_vec();
|
|
}
|
|
|
|
pub fn chunk_data_for_verenc(data: Vec<u8>) -> Vec<Vec<u8>> {
|
|
return encode_to_curve448_scalars(&data);
|
|
}
|
|
|
|
pub fn combine_chunked_data(chunks: Vec<Vec<u8>>) -> Vec<u8> {
|
|
return decode_from_curve448_scalars(&chunks);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use rand::RngCore;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_verenc() {
|
|
let data = vec![0, 'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0];
|
|
let proof = new_verenc_proof(data.clone());
|
|
let proofdata = proof.clone();
|
|
let pubproof = VerencProof { blinding_pubkey: proof.blinding_pubkey, encryption_key: proof.encryption_key, statement: proof.statement, challenge: proof.challenge, polycom: proof.polycom, ctexts: proof.ctexts, shares_rands: proof.shares_rands };
|
|
assert!(verenc_verify(pubproof.clone()));
|
|
let compressed = verenc_compress(pubproof);
|
|
let result = verenc_recover(VerencDecrypt{
|
|
blinding_pubkey: proofdata.blinding_pubkey,
|
|
decryption_key: proof.decryption_key,
|
|
statement: proofdata.statement,
|
|
ciphertexts: compressed,
|
|
});
|
|
|
|
assert!(data == result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_chunking() {
|
|
for i in 0..1000 {
|
|
let mut data: [u8; 1300] = [0u8;1300];
|
|
OsRng::fill_bytes(&mut OsRng, &mut data);
|
|
let chunks = chunk_data_for_verenc(data.to_vec());
|
|
for chunk in chunks.clone() {
|
|
let scalar_chunk = Scalar::from_bytes(&chunk.clone().try_into().unwrap());
|
|
assert!(scalar_chunk.to_bytes().to_vec() == chunk)
|
|
}
|
|
let result = combine_chunked_data(chunks);
|
|
let mut padded_data = data.to_vec();
|
|
while result.len() > padded_data.len() {
|
|
padded_data.push(0);
|
|
}
|
|
assert!(padded_data == result);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_full_verenc() {
|
|
let mut data: [u8; 128] = [0u8;128];
|
|
OsRng::fill_bytes(&mut OsRng, &mut data);
|
|
let chunks = chunk_data_for_verenc(data.to_vec());
|
|
for chunk in chunks.clone() {
|
|
let proof = new_verenc_proof(chunk.clone());
|
|
let proofdata = proof.clone();
|
|
let pubproof = VerencProof { blinding_pubkey: proof.blinding_pubkey, encryption_key: proof.encryption_key, statement: proof.statement, challenge: proof.challenge, polycom: proof.polycom, ctexts: proof.ctexts, shares_rands: proof.shares_rands };
|
|
assert!(verenc_verify(pubproof.clone()));
|
|
let compressed = verenc_compress(pubproof);
|
|
let result = verenc_recover(VerencDecrypt{
|
|
blinding_pubkey: proofdata.blinding_pubkey,
|
|
decryption_key: proof.decryption_key,
|
|
statement: proofdata.statement,
|
|
ciphertexts: compressed,
|
|
});
|
|
|
|
assert!(chunk == result);
|
|
}
|
|
let result = combine_chunked_data(chunks);
|
|
let mut padded_data = data.to_vec();
|
|
while result.len() > padded_data.len() {
|
|
padded_data.push(0);
|
|
}
|
|
assert!(padded_data != result);
|
|
}
|
|
} |