Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] Handover Protocol (or The PR Formerly Known as Spongebob) #186

Draft
wants to merge 42 commits into
base: rocknroll
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3a712db
Jammin' with Piotr: draft of share update helpers with verifiability
cygnusv Mar 7, 2024
d721d03
Distinction between ShareCommitments and TDec PublicKeys
cygnusv Mar 12, 2024
a760a9c
Clarifying some refresh tests
cygnusv Mar 12, 2024
88994f5
Yay! Tests work when blinding is deactivated, so the problem is unbli…
cygnusv Mar 13, 2024
16e296c
Some tests fixed: share updating should be done on top of blinded shares
cygnusv Mar 14, 2024
b23fae4
ShareUpdate verification method
cygnusv Mar 18, 2024
1459c1f
Clarification
cygnusv Mar 18, 2024
de9fd32
Verify share update in first tests
cygnusv Mar 18, 2024
2b370fd
UpdateTranscript struct to encapsulate updates and poly commitments
cygnusv Mar 20, 2024
920353f
Move methods to create updates from ShareUpdate to UpdateTranscript
cygnusv Mar 20, 2024
1e45be3
Use UpdateTranscripts instead of ShareUpdate
cygnusv Mar 20, 2024
4bfcf20
First version of UpdateTranscript validation
cygnusv Mar 20, 2024
4e2d751
UpdateTranscript validation: add consistency checks with update poly
cygnusv Mar 20, 2024
bdb56a2
cargo-fix'n stuff
cygnusv Mar 20, 2024
7fe5327
UpdateTranscript validation: poly commitments fit the update type
cygnusv Mar 25, 2024
0f44b0c
Comments
cygnusv Mar 25, 2024
0a02df7
Code quality
cygnusv Mar 25, 2024
f7b3356
Preparing refactor
cygnusv Mar 26, 2024
6c8e36d
preparing refactor 2
cygnusv Mar 27, 2024
7d565a9
preparing for refactor 3
cygnusv Mar 27, 2024
5f41897
Introduce UpdatableBlindedKeyShare as part of refresh API
cygnusv Apr 1, 2024
dbaef87
Use BlindedKeyShare at the PVSS level
cygnusv Apr 1, 2024
4f1f1b1
Don't update private key shares directly at the PVSS level
cygnusv Apr 1, 2024
c61ccbd
Basic refresh tests work again!
cygnusv Apr 1, 2024
a2b17ad
Use UpdateTranscripts as input to update BlindedKeyShares
cygnusv May 17, 2024
08ec657
Method to refresh an AggregateTranscript
cygnusv May 20, 2024
5e6a5bc
DKG level method for validators to create refresh transcripts
cygnusv May 20, 2024
c3abb23
Fix test_dkg_simple_tdec_share_refreshing test!
cygnusv May 21, 2024
fabd1be
Point out that aggregate coefficients need to be updated too
cygnusv May 21, 2024
26898a9
First draft of HandoverTranscript - a.k.a. The Baton
cygnusv May 29, 2024
b9878af
Draft for testing handover transcripts
cygnusv May 30, 2024
be4a91e
Recovery tests are broken. Marked as ignored and adjusted to compile
cygnusv Sep 20, 2024
f00d592
Pass Keypairs as input to unblind BlindedKeyShares
cygnusv Sep 20, 2024
24978fa
Tidy up imports in several places
cygnusv Sep 20, 2024
68daa42
PrivateKeys are never blinded directly
cygnusv Sep 20, 2024
dc41325
Assorted cleanup
cygnusv Sep 20, 2024
90c0d2c
Add TODO about using explicit imports (see #194)
cygnusv Sep 23, 2024
966e265
Explicitly rename DKG PublicKeys to avoid confusion with Validator PKs
cygnusv Sep 23, 2024
7c32b2d
Use PublicKeys instead of internal G2 type when possible
cygnusv Sep 23, 2024
18252f2
Generating random DKG public keys should only be a test function
cygnusv Sep 23, 2024
9032e93
Consider using multipairings
cygnusv Sep 20, 2024
2304710
TODO: Next steps
cygnusv Sep 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ferveo-tdec/benches/tpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct SetupShared {
shares_num: usize,
msg: Vec<u8>,
aad: Vec<u8>,
pubkey: PublicKey<E>,
pubkey: DkgPublicKey<E>,
privkey: PrivateKeyShare<E>,
ciphertext: Ciphertext<E>,
shared_secret: SharedSecret<E>,
Expand Down
4 changes: 2 additions & 2 deletions ferveo-tdec/src/ciphertext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use sha2::{digest::Digest, Sha256};
use zeroize::ZeroizeOnDrop;

use crate::{
htp_bls12381_g2, Error, PrivateKeyShare, PublicKey, Result, SecretBox,
htp_bls12381_g2, DkgPublicKey, Error, PrivateKeyShare, Result, SecretBox,
SharedSecret,
};

Expand Down Expand Up @@ -98,7 +98,7 @@ impl<E: Pairing> CiphertextHeader<E> {
pub fn encrypt<E: Pairing>(
message: SecretBox<Vec<u8>>,
aad: &[u8],
pubkey: &PublicKey<E>,
pubkey: &DkgPublicKey<E>,
rng: &mut impl rand::Rng,
) -> Result<Ciphertext<E>> {
// r
Expand Down
11 changes: 6 additions & 5 deletions ferveo-tdec/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use ark_ec::pairing::Pairing;

use crate::{
prepare_combine_simple, BlindedKeyShare, CiphertextHeader,
DecryptionSharePrecomputed, DecryptionShareSimple, PrivateKeyShare,
PublicKey, Result,
DecryptionSharePrecomputed, DecryptionShareSimple, PrivateKeyShare, Result,
ShareCommitment,
};

#[derive(Clone, Debug)]
pub struct PublicDecryptionContextFast<E: Pairing> {
pub domain: E::ScalarField,
pub public_key: PublicKey<E>,
pub public_key: ShareCommitment<E>, // FIXME
pub blinded_key_share: BlindedKeyShare<E>,
// This decrypter's contribution to N(0), namely (-1)^|domain| * \prod_i omega_i
pub lagrange_n_0: E::ScalarField,
Expand All @@ -19,12 +19,13 @@ pub struct PublicDecryptionContextFast<E: Pairing> {
#[derive(Clone, Debug)]
pub struct PublicDecryptionContextSimple<E: Pairing> {
pub domain: E::ScalarField,
pub public_key: PublicKey<E>,
pub share_commitment: ShareCommitment<E>,
pub blinded_key_share: BlindedKeyShare<E>,
pub h: E::G2Affine,
pub validator_public_key: E::G2,
pub validator_public_key: ferveo_common::PublicKey<E>,
}

// TODO: Mark for removal
#[derive(Clone, Debug)]
pub struct SetupParams<E: Pairing> {
pub b: E::ScalarField, // Validator private key
Expand Down
4 changes: 2 additions & 2 deletions ferveo-tdec/src/decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<E: Pairing> ValidatorShareChecksum<E> {
return false;
}

// TODO: use multipairing here (h_inv)
// TODO: use multipairing here (h_inv) - Issue #192
// e(C_i, ek_i) == e(U, H)
if E::pairing(self.checksum, *validator_public_key)
!= E::pairing(ciphertext.commitment, *h)
Expand Down Expand Up @@ -234,7 +234,7 @@ pub fn verify_decryption_shares_simple<E: Pairing>(
{
let is_valid = decryption_share.verify(
y_i,
&pub_context.validator_public_key.into_affine(),
&pub_context.validator_public_key.encryption_key,
&pub_context.h.into(),
ciphertext,
);
Expand Down
91 changes: 50 additions & 41 deletions ferveo-tdec/src/key_share.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,72 @@
use std::ops::Mul;

use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
use ark_ff::One;
use ark_std::UniformRand;
use ferveo_common::serialization;
use rand_core::RngCore;
use ark_ec::{pairing::Pairing, CurveGroup};
use ark_ff::Field;
use ferveo_common::{serialization, Keypair};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use zeroize::{Zeroize, ZeroizeOnDrop};

#[serde_as]
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PublicKey<E: Pairing>(
pub struct DkgPublicKey<E: Pairing>(
#[serde_as(as = "serialization::SerdeAs")] pub E::G1Affine,
);

#[serde_as]
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ShareCommitment<E: Pairing>(
#[serde_as(as = "serialization::SerdeAs")] pub E::G1Affine, // A_{i, \omega_i}
);

#[derive(Debug, Clone)]
// TODO: Improve by adding share commitment here
// TODO: Is this a test utility perhaps?
#[derive(Debug, Copy, Clone)]
pub struct BlindedKeyShare<E: Pairing> {
pub blinding_key: E::G2Affine, // [b] H
pub blinded_key_share: E::G2Affine, // [b] Z_{i, \omega_i}
pub validator_public_key: E::G2Affine, // [b] H
pub blinded_key_share: E::G2Affine, // [b] Z_{i, \omega_i}
}

impl<E: Pairing> BlindedKeyShare<E> {
pub fn verify_blinding<R: RngCore>(
&self,
public_key: &PublicKey<E>,
rng: &mut R,
) -> bool {
let g = E::G1Affine::generator();
let alpha = E::ScalarField::rand(rng);
// TODO: Salvage and cleanup
// pub fn verify_blinding<R: RngCore>(
// &self,
// public_key: &PublicKey<E>,
// rng: &mut R,
// ) -> bool {
// let g = E::G1Affine::generator();
// let alpha = E::ScalarField::rand(rng);

let alpha_a =
E::G1Prepared::from(g + public_key.0.mul(alpha).into_affine());
// let alpha_a =
// E::G1Prepared::from(g + public_key.0.mul(alpha).into_affine());

// \sum_i(Y_i)
let alpha_z = E::G2Prepared::from(
self.blinding_key + self.blinded_key_share.mul(alpha).into_affine(),
);
// // \sum_i(Y_i)
// let alpha_z = E::G2Prepared::from(
// self.blinding_key + self.blinded_key_share.mul(alpha).into_affine(),
// );

// e(g, Yi) == e(Ai, [b] H)
let g_inv = E::G1Prepared::from(-g.into_group());
E::multi_pairing([g_inv, alpha_a], [alpha_z, self.blinding_key.into()])
.0
== E::TargetField::one()
}
// // e(g, Yi) == e(Ai, [b] H)
// let g_inv = E::G1Prepared::from(-g.into_group());
// E::multi_pairing([g_inv, alpha_a], [alpha_z, self.blinding_key.into()])
// .0
// == E::TargetField::one()
// }

pub fn multiply_by_omega_inv(&mut self, omega_inv: &E::ScalarField) {
self.blinded_key_share =
self.blinded_key_share.mul(-*omega_inv).into_affine();
// pub fn multiply_by_omega_inv(&mut self, omega_inv: &E::ScalarField) {
// self.blinded_key_share =
// self.blinded_key_share.mul(-*omega_inv).into_affine();
// }
pub fn unblind(
&self,
validator_keypair: &Keypair<E>,
) -> PrivateKeyShare<E> {
let unblinding_factor = validator_keypair
.decryption_key
.inverse()
.expect("Validator decryption key must have an inverse");
PrivateKeyShare::<E>(
self.blinded_key_share.mul(unblinding_factor).into_affine(),
)
}
}

Expand All @@ -58,13 +77,3 @@ impl<E: Pairing> BlindedKeyShare<E> {
pub struct PrivateKeyShare<E: Pairing>(
#[serde_as(as = "serialization::SerdeAs")] pub E::G2Affine,
);

impl<E: Pairing> PrivateKeyShare<E> {
pub fn blind(&self, b: E::ScalarField) -> BlindedKeyShare<E> {
let blinding_key = E::G2Affine::generator().mul(b).into_affine();
BlindedKeyShare::<E> {
blinding_key,
blinded_key_share: self.0.mul(b).into_affine(),
}
}
}
94 changes: 61 additions & 33 deletions ferveo-tdec/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![warn(rust_2018_idioms)]

// TODO: Use explicit imports - #194
pub mod ciphertext;
pub mod combine;
pub mod context;
Expand Down Expand Up @@ -59,7 +60,7 @@ pub mod test_common {
use std::{ops::Mul, usize};

pub use ark_bls12_381::Bls12_381 as EllipticCurve;
use ark_ec::{pairing::Pairing, AffineRepr};
use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
pub use ark_ff::UniformRand;
use ark_ff::{Field, Zero};
use ark_poly::{
Expand All @@ -76,7 +77,7 @@ pub mod test_common {
threshold: usize,
rng: &mut impl rand::Rng,
) -> (
PublicKey<E>,
DkgPublicKey<E>,
PrivateKeyShare<E>,
Vec<PrivateDecryptionContextSimple<E>>,
) {
Expand All @@ -86,50 +87,74 @@ pub mod test_common {
// The dealer chooses a uniformly random polynomial f of degree t-1
let threshold_poly =
DensePolynomial::<E::ScalarField>::rand(threshold - 1, rng);

// Domain, or omega Ω
let fft_domain =
ark_poly::GeneralEvaluationDomain::<E::ScalarField>::new(
shares_num,
)
.unwrap();

// domain points: - ω_j in Ω
let domain_points = fft_domain.elements().collect::<Vec<_>>();

// `evals` are evaluations of the polynomial f over the domain, omega: f(ω_j) for ω_j in Ω
let evals = threshold_poly.evaluate_over_domain_by_ref(fft_domain);

let shares_x = fft_domain.elements().collect::<Vec<_>>();
// A_j, share commitments of participants: [f(ω_j)] G
let share_commitments = fast_multiexp(&evals.evals, g.into_group());

// A - public key shares of participants
let pubkey_shares = fast_multiexp(&evals.evals, g.into_group());
let pubkey_share = g.mul(evals.evals[0]);
debug_assert!(pubkey_shares[0] == E::G1Affine::from(pubkey_share));
// FIXME: These 2 lines don't make sense
//let pubkey_share = g.mul(evals.evals[0]);
//debug_assert!(share_commitments[0] == E::G1Affine::from(pubkey_share));

// Y, but only when b = 1 - private key shares of participants
// Z_j, private key shares of participants (unblinded): [f(ω_j)] H
// NOTE: In production, these are never produced this way, as the DKG
// directly generates blinded shares Y_j. Only then, node j can use their
// validator key to unblind Y_j and obtain the private key share Z_j.
let privkey_shares = fast_multiexp(&evals.evals, h.into_group());

// a_0
let x = threshold_poly.coeffs[0];
// F_0
let pubkey = g.mul(x);
let privkey = h.mul(x);
// The shared secret is the free coefficient from threshold poly
let a_0 = threshold_poly.coeffs[0];

// F_0, group's public key
let group_pubkey = g.mul(a_0);

// group's private key (NOTE: just for tests, this is NEVER constructed in production)
let group_privkey = h.mul(a_0);

// As in SSS, shared secret should be f(0), which is also the free coefficient
let secret = threshold_poly.evaluate(&E::ScalarField::zero());
debug_assert!(secret == x);
debug_assert!(secret == a_0);

let mut private_contexts = vec![];
let mut public_contexts = vec![];

// (domain, A, Y)
for (index, (domain, public, private)) in
izip!(shares_x.iter(), pubkey_shares.iter(), privkey_shares.iter())
.enumerate()
// (domain_point, A, Z)
for (index, (domain_point, share_commit, private_share)) in izip!(
domain_points.iter(),
share_commitments.iter(),
privkey_shares.iter()
)
.enumerate()
{
let private_key_share = PrivateKeyShare::<E>(*private);
let b = E::ScalarField::rand(rng);
let blinded_key_share = private_key_share.blind(b);
let private_key_share = PrivateKeyShare::<E>(*private_share);
let blinding_factor = E::ScalarField::rand(rng);

let validator_public_key = h.mul(blinding_factor).into_affine();
let blinded_key_share = BlindedKeyShare::<E> {
validator_public_key,
blinded_key_share: private_key_share
.0
.mul(blinding_factor)
.into_affine(),
};

private_contexts.push(PrivateDecryptionContextSimple::<E> {
index,
setup_params: SetupParams {
b,
b_inv: b.inverse().unwrap(),
b: blinding_factor,
b_inv: blinding_factor.inverse().unwrap(),
g,
h_inv: E::G2Prepared::from(-h.into_group()),
g_inv: E::G1Prepared::from(-g.into_group()),
Expand All @@ -139,20 +164,22 @@ pub mod test_common {
public_decryption_contexts: vec![],
});
public_contexts.push(PublicDecryptionContextSimple::<E> {
domain: *domain,
public_key: PublicKey::<E>(*public),
domain: *domain_point,
share_commitment: ShareCommitment::<E>(*share_commit), // FIXME
blinded_key_share,
h,
validator_public_key: h.mul(b),
validator_public_key: ferveo_common::PublicKey {
encryption_key: blinded_key_share.validator_public_key,
},
});
}
for private in private_contexts.iter_mut() {
private.public_decryption_contexts = public_contexts.clone();
for private_ctxt in private_contexts.iter_mut() {
private_ctxt.public_decryption_contexts = public_contexts.clone();
}

(
PublicKey(pubkey.into()),
PrivateKeyShare(privkey.into()),
DkgPublicKey(group_pubkey.into()),
PrivateKeyShare(group_privkey.into()), // TODO: Not the correct type, but whatever
private_contexts,
)
}
Expand All @@ -162,7 +189,7 @@ pub mod test_common {
threshold: usize,
rng: &mut impl rand::Rng,
) -> (
PublicKey<E>,
DkgPublicKey<E>,
PrivateKeyShare<E>,
Vec<PrivateDecryptionContextSimple<E>>,
) {
Expand Down Expand Up @@ -411,6 +438,7 @@ mod tests {

// There is no share aggregation in current version of tpke (it's mocked).
// ShareEncryptions are called BlindedKeyShares.
// TOOD: ^Fix this comment later

let pub_contexts = &contexts[0].public_decryption_contexts;
assert!(verify_decryption_shares_simple(
Expand All @@ -430,7 +458,7 @@ mod tests {

assert!(!has_bad_checksum.verify(
&pub_contexts[0].blinded_key_share.blinded_key_share,
&pub_contexts[0].validator_public_key.into_affine(),
&pub_contexts[0].validator_public_key.encryption_key,
&pub_contexts[0].h.into_group(),
&ciphertext,
));
Expand All @@ -441,7 +469,7 @@ mod tests {

assert!(!has_bad_share.verify(
&pub_contexts[0].blinded_key_share.blinded_key_share,
&pub_contexts[0].validator_public_key.into_affine(),
&pub_contexts[0].validator_public_key.encryption_key,
&pub_contexts[0].h.into_group(),
&ciphertext,
));
Expand Down
Loading
Loading