Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
2,446 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
[package] | ||
name = "chain-vote" | ||
version = "0.1.0" | ||
authors = ["Vincent Hanquez <vincent.hanquez@iohk.io>"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
rand_core = "0.5" | ||
cryptoxide = "0.2" | ||
eccoxide = { git = "https://github.com/vincenthz/eccoxide", optional = true } | ||
zerocaf = { version = "0.2", optional = true } | ||
#eccoxide = { path = "/Users/tab/Work/crypto/eccoxide", optional = true } | ||
|
||
criterion = { version = "0.3", optional = true } | ||
rand_chacha = { version = "0.2", optional = true } | ||
|
||
[dev-dependencies] | ||
rand_chacha = "0.2" | ||
smoke = "^0.2.1" | ||
|
||
[features] | ||
default = ["p256k1"] | ||
p256k1 = ["eccoxide"] | ||
ed = ["zerocaf"] | ||
with-bench = ["criterion"] | ||
|
||
[[bench]] | ||
harness = false | ||
name = "shvzk" | ||
required-features = ["with-bench"] | ||
|
||
[[bench]] | ||
harness = false | ||
name = "gmul" | ||
required-features = ["with-bench"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use chain_vote::debug::gang; | ||
use criterion::{criterion_group, criterion_main, Criterion}; | ||
use rand_chacha::ChaCha20Rng; | ||
|
||
fn mul(c: &mut Criterion) { | ||
let mut g = gang::GroupElement::generator() * gang::Scalar::from_u64(100); | ||
c.bench_function("mul", |b| b.iter(|| &g + &g)); | ||
} | ||
|
||
criterion_group!(gmul, mul); | ||
criterion_main!(gmul); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
use chain_vote::debug::{gang, shvzk}; | ||
use chain_vote::*; | ||
use criterion::{criterion_group, criterion_main, Criterion}; | ||
use rand_chacha::ChaCha20Rng; | ||
use rand_core::SeedableRng; | ||
|
||
fn common(rng: &mut ChaCha20Rng) -> (EncryptingVoteKey, EncryptingVote) { | ||
let h = gang::GroupElement::random(rng); | ||
|
||
let mc1 = MemberCommunicationKey::new(rng); | ||
let mc = [mc1.to_public()]; | ||
|
||
let threshold = 1; | ||
|
||
let m1 = MemberState::new(rng, threshold, &h, &mc, 0); | ||
|
||
let participants = vec![m1.public_key()]; | ||
let ek = EncryptingVoteKey::from_participants(&participants); | ||
|
||
let vote_options = 2; | ||
let vote = Vote::new(vote_options, 0); | ||
|
||
let ev = EncryptingVote::prepare(rng, ek.as_raw(), &vote); | ||
(ek, ev) | ||
} | ||
|
||
fn generate(c: &mut Criterion) { | ||
let mut rng = ChaCha20Rng::from_seed([0u8; 32]); | ||
let (ek, ev) = common(&mut rng); | ||
c.bench_function("generate", |b| { | ||
b.iter(|| shvzk::prove(&mut rng, ek.as_raw(), ev.clone())) | ||
}); | ||
} | ||
|
||
fn verify(c: &mut Criterion) { | ||
let mut rng = ChaCha20Rng::from_seed([0u8; 32]); | ||
let (ek, ev) = common(&mut rng); | ||
let proof = shvzk::prove(&mut rng, ek.as_raw(), ev.clone()); | ||
c.bench_function("verify", |b| { | ||
b.iter(|| shvzk::verify(&ek.as_raw(), &ev.ciphertexts, &proof)) | ||
}); | ||
} | ||
|
||
criterion_group!(shvzk, generate, verify); | ||
criterion_main!(shvzk); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
use crate::gang::{GroupElement, Scalar}; | ||
use rand_core::{CryptoRng, RngCore}; | ||
use std::ops::{Add, Mul}; | ||
|
||
/// Pedersen commitment | ||
#[derive(Clone, PartialEq, Eq)] | ||
pub struct Commitment { | ||
c: GroupElement, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct CommitmentKey { | ||
pub h: GroupElement, | ||
} | ||
|
||
impl CommitmentKey { | ||
pub fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> Self { | ||
let h = GroupElement::random(rng); | ||
CommitmentKey { h } | ||
} | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
pub enum Validity { | ||
Valid, | ||
Invalid, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Open { | ||
m: Scalar, | ||
r: Scalar, | ||
} | ||
|
||
impl Commitment { | ||
pub fn new_open(ck: &CommitmentKey, o: &Open) -> Self { | ||
let c = GroupElement::generator() * &o.m + &ck.h * &o.r; | ||
Commitment { c } | ||
} | ||
|
||
pub fn new(ck: &CommitmentKey, m: &Scalar, r: &Scalar) -> Self { | ||
let c = GroupElement::generator() * m + &ck.h * r; | ||
Commitment { c } | ||
} | ||
|
||
pub fn verify(&self, ck: &CommitmentKey, o: &Open) -> Validity { | ||
let other = GroupElement::generator() * &o.m + &ck.h * &o.r; | ||
if self.c == other { | ||
Validity::Valid | ||
} else { | ||
Validity::Invalid | ||
} | ||
} | ||
pub fn to_bytes(&self) -> Vec<u8> { | ||
self.c.to_bytes().to_vec() | ||
} | ||
} | ||
|
||
impl<'a, 'b> Add<&'b Commitment> for &'a Commitment { | ||
type Output = Commitment; | ||
fn add(self, rhs: &'b Commitment) -> Self::Output { | ||
let c = &self.c + &rhs.c; | ||
Commitment { c } | ||
} | ||
} | ||
|
||
impl<'b> Add<&'b Commitment> for Commitment { | ||
type Output = Commitment; | ||
fn add(self, rhs: &'b Commitment) -> Self::Output { | ||
let c = &self.c + &rhs.c; | ||
Commitment { c } | ||
} | ||
} | ||
|
||
impl<'a, 'b> Mul<&'b Scalar> for &'a Commitment { | ||
type Output = Commitment; | ||
fn mul(self, rhs: &'b Scalar) -> Self::Output { | ||
Commitment { c: &self.c * rhs } | ||
} | ||
} | ||
|
||
impl<'a> Mul<Scalar> for &'a Commitment { | ||
type Output = Commitment; | ||
fn mul(self, rhs: Scalar) -> Self::Output { | ||
Commitment { c: &self.c * rhs } | ||
} | ||
} | ||
|
||
impl Mul<Scalar> for Commitment { | ||
type Output = Commitment; | ||
fn mul(self, rhs: Scalar) -> Self::Output { | ||
Commitment { c: &self.c * &rhs } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
use crate::commitment::CommitmentKey; | ||
use crate::gang::{GroupElement, Scalar}; | ||
use crate::gargamel::{PublicKey, SecretKey}; | ||
use crate::hybrid; | ||
use crate::math::Polynomial; | ||
use rand_core::{CryptoRng, RngCore}; | ||
|
||
/// Committee member election secret key | ||
#[derive(Clone)] | ||
pub struct MemberSecretKey(pub(crate) SecretKey); | ||
|
||
/// Committee member election public key | ||
#[derive(Clone)] | ||
pub struct MemberPublicKey(pub(crate) PublicKey); | ||
|
||
#[derive(Clone)] | ||
pub struct MemberCommunicationKey(SecretKey); | ||
|
||
/// Committee Member communication public key (with other committee members) | ||
#[derive(Clone)] | ||
pub struct MemberCommunicationPublicKey(PublicKey); | ||
|
||
/// The overall committee public key used for everyone to encrypt their vote to. | ||
#[derive(Clone)] | ||
pub struct ElectionPublicKey(pub(crate) PublicKey); | ||
|
||
impl ElectionPublicKey { | ||
#[doc(hidden)] | ||
pub fn as_raw(&self) -> &PublicKey { | ||
&self.0 | ||
} | ||
} | ||
|
||
/// Initial state generated by a Member, which include keys for this election | ||
#[derive(Clone)] | ||
pub struct MemberState { | ||
sk: MemberSecretKey, | ||
owner_index: usize, | ||
apubs: Vec<GroupElement>, | ||
es: Vec<GroupElement>, | ||
encrypted: Vec<(hybrid::Encrypted, hybrid::Encrypted)>, | ||
} | ||
|
||
pub type CRS = GroupElement; | ||
|
||
impl MemberState { | ||
/// Generate a new member state from random, where the number | ||
pub fn new<R: RngCore + CryptoRng>( | ||
rng: &mut R, | ||
t: usize, | ||
h: &CRS, // TODO: document | ||
committee_pks: &[MemberCommunicationPublicKey], | ||
my: usize, | ||
) -> MemberState { | ||
let n = committee_pks.len(); | ||
assert!(t > 0); | ||
assert!(t <= n); | ||
assert!(my < n); | ||
|
||
let pcomm = Polynomial::random(rng, t); | ||
let pshek = Polynomial::random(rng, t); | ||
|
||
let mut apubs = Vec::new(); | ||
let mut es = Vec::new(); | ||
|
||
for (ai, bi) in pshek.get_coefficients().zip(pcomm.get_coefficients()) { | ||
let apub = GroupElement::generator() * ai; | ||
let e = &apub + h * bi; | ||
apubs.push(apub); | ||
es.push(e); | ||
} | ||
|
||
let mut encrypted = Vec::new(); | ||
for i in 0..n { | ||
// don't generate share for self | ||
if i == my { | ||
continue; | ||
} else { | ||
let idx = Scalar::from_u64((i + 1) as u64); | ||
let share_comm = pcomm.evaluate(&idx); | ||
let share_shek = pshek.evaluate(&idx); | ||
|
||
let pk = &committee_pks[i]; | ||
let ck_comm = CommitmentKey::generate(rng); | ||
let ck_shek = CommitmentKey::generate(rng); | ||
|
||
let rcomm = Scalar::random(rng); | ||
let rshek = Scalar::random(rng); | ||
let ecomm = hybrid::encrypt(&pk.0, &ck_comm, &share_comm.to_bytes(), &rcomm); | ||
let eshek = hybrid::encrypt(&pk.0, &ck_shek, &share_shek.to_bytes(), &rshek); | ||
|
||
encrypted.push((ecomm, eshek)); | ||
} | ||
} | ||
|
||
assert_eq!(apubs.len(), t + 1); | ||
assert_eq!(es.len(), t + 1); | ||
assert_eq!(encrypted.len(), n - 1); | ||
|
||
MemberState { | ||
sk: MemberSecretKey(SecretKey { | ||
sk: pshek.at_zero(), | ||
}), | ||
owner_index: my + 1, // committee member are 1-indexed | ||
apubs, | ||
es, | ||
encrypted, | ||
} | ||
} | ||
|
||
pub fn secret_key(&self) -> &MemberSecretKey { | ||
&self.sk | ||
} | ||
|
||
pub fn public_key(&self) -> MemberPublicKey { | ||
MemberPublicKey(PublicKey { | ||
pk: self.apubs[0].clone(), | ||
}) | ||
} | ||
} | ||
|
||
impl MemberCommunicationKey { | ||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self { | ||
let sk = SecretKey::generate(rng); | ||
MemberCommunicationKey(sk) | ||
} | ||
|
||
pub fn to_public(&self) -> MemberCommunicationPublicKey { | ||
MemberCommunicationPublicKey(PublicKey { | ||
pk: &GroupElement::generator() * &self.0.sk, | ||
}) | ||
} | ||
} | ||
|
||
impl ElectionPublicKey { | ||
/// Create an election public key from all the participants of this committee | ||
pub fn from_participants(pks: &[MemberPublicKey]) -> Self { | ||
let mut k = pks[0].0.pk.clone(); | ||
for pk in &pks[1..] { | ||
k = k + &pk.0.pk; | ||
} | ||
ElectionPublicKey(PublicKey { pk: k }) | ||
} | ||
} |
Oops, something went wrong.