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

inv-tshare: Threshold (re-)sharing protocol #542

Merged
merged 74 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
b0d074b
inv-tshare: Threshold (re-)sharing protocol
Jul 20, 2024
acbfc83
Include Lagrange
emmorais Aug 19, 2024
b1dee3d
Call Tshare once for sharing k
emmorais Aug 21, 2024
44a983c
Include tshare for chi
emmorais Aug 22, 2024
8008d7e
Add test for lagrange coefficient at zero, conversion works
emmorais Aug 30, 2024
2acd393
Create unit test to validate auxinfo ids
emmorais Aug 30, 2024
edca2a2
Unit test for checking auxinfo id consistency
emmorais Aug 30, 2024
37f88fe
Conver test cases for the input
emmorais Aug 30, 2024
a7d6aef
Check constant terms consistency
emmorais Aug 30, 2024
3b038d5
Check constant terms
emmorais Aug 30, 2024
9f32b5f
Check the share is consistent
emmorais Aug 30, 2024
9de4431
Fmt
emmorais Sep 3, 2024
798ecca
Warnings and clippy
emmorais Sep 3, 2024
4a691a8
Public key checks
emmorais Sep 6, 2024
0a95528
Include unit test for lagrange at zero
emmorais Sep 6, 2024
b680b94
Fmt, clippy and warnings
emmorais Sep 6, 2024
609a91e
More warnings
emmorais Sep 6, 2024
ee4ea23
Merge branch 'main' into inv-tshare
emmorais Sep 6, 2024
1924882
Clippy
emmorais Sep 6, 2024
7a47396
Documentation
emmorais Sep 6, 2024
a90c0d4
Fix the public key validation
emmorais Sep 8, 2024
830dafd
Fix saved public key values, protocol working
emmorais Sep 8, 2024
bcd66f9
Use participant_coordinate instead of incrementing the index
emmorais Sep 10, 2024
272d067
Fmt
emmorais Sep 10, 2024
89d77c0
Remove unused module
emmorais Sep 10, 2024
64bef43
Testing cases for the full protocol
emmorais Sep 12, 2024
8068f07
Full protocol test for 2/3 threshold sharing
emmorais Sep 13, 2024
516dae2
Negative test cases
emmorais Sep 13, 2024
edb525d
Refactor the t-out-of-t sub-protocol
emmorais Sep 13, 2024
4e213bd
Remove unused code
emmorais Sep 13, 2024
b68c071
More adjustments
emmorais Sep 13, 2024
6dbc750
Introduce dedicated type for evaluations
emmorais Sep 13, 2024
b478441
Remove old TODOs
emmorais Sep 13, 2024
0207eac
Remove unneeded variable
emmorais Sep 16, 2024
eeb06eb
Use Scalar instead of BigInt
emmorais Sep 16, 2024
80f605a
Remove old TODOs
emmorais Sep 16, 2024
b4b948e
Minor adjustments
emmorais Sep 16, 2024
ae88cb1
Remove unneeded operation in share.rs
emmorais Oct 4, 2024
767ca09
Remove unnecessary clone derivation
emmorais Oct 4, 2024
d855355
Remove unnecessary validations
emmorais Oct 4, 2024
ce0977d
Update old comment
emmorais Oct 7, 2024
6fa035f
Add new comment
emmorais Oct 7, 2024
560683c
Update comment
emmorais Oct 7, 2024
b432f0e
Replace Schnorr proofs for all coefficients by a single proof for the…
emmorais Oct 7, 2024
9f70a3d
Fmt
emmorais Oct 7, 2024
679e9c9
Fmt
emmorais Oct 7, 2024
8410c77
Remove public_share from commitment
emmorais Oct 8, 2024
6715a9a
Move encryption of shares one round earlier
emmorais Oct 9, 2024
abf946c
Continue moving encrypted shares to second round
emmorais Oct 9, 2024
7e3b0ac
Renaming variables
emmorais Oct 9, 2024
c578fc9
Remove commented code
emmorais Oct 9, 2024
6550a2b
Minor adjustments
emmorais Oct 9, 2024
fa1c8c5
Add chain_code to keygen output
emmorais Sep 23, 2024
703bc57
Add shift to signature generation
emmorais Sep 25, 2024
1d3a030
Improve error message
emmorais Sep 25, 2024
22496d2
Refactor expect expression
emmorais Sep 25, 2024
37c8ebe
Refactor repeated code
emmorais Sep 25, 2024
387dae7
Refactor the hash calculation
emmorais Sep 25, 2024
1afb305
Distributed chain code finished and included in the transcript
emmorais Sep 26, 2024
81dfbd4
Remove unnecessary copyright
emmorais Sep 26, 2024
0160e10
If CKD fails, try again with incremented counter
emmorais Sep 26, 2024
4cd32aa
Introduce auxiliary macro to xor 256 bits
emmorais Sep 27, 2024
29cd712
Replace keccak by hmac512
emmorais Sep 29, 2024
ce5541e
Introduce first unit test from test vectors
emmorais Sep 30, 2024
8357a0a
Child derivation key unit test from test vectors
emmorais Oct 1, 2024
d9576c6
Clippy
emmorais Oct 1, 2024
0825068
Use CKDOutput instead of tuple
emmorais Oct 2, 2024
3946c17
Retry master key generation on failure
emmorais Oct 9, 2024
aaa33ad
Merge branch 'main' into inv-tshare
emmorais Oct 10, 2024
e5102ab
Fmt
emmorais Oct 10, 2024
ab5a265
Typo
emmorais Oct 10, 2024
f1cdc3e
n=2 working, but n=3 not
emmorais Oct 14, 2024
1389fe7
Minor adjustments
emmorais Oct 15, 2024
f42dbdc
Create maybe_finish_round2
emmorais Oct 15, 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
251 changes: 143 additions & 108 deletions src/tshare/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,22 @@ impl TshareParticipant {
// Finish round 1 by generating messages for round 2
let round_one_messages = run_only_once!(self.gen_round_two_msgs(rng, message.id()))?;

let mut outcomes = self
.fetch_messages(MessageType::Tshare(TshareMessageType::R2PrivateShare))?
.iter()
.map(|msg| self.handle_round_two_msg_private(msg))
.collect::<Result<Vec<_>>>()?;

// Process any round 2 messages we may have received early
let round_two_outcomes = self
.fetch_messages(MessageType::Tshare(TshareMessageType::R2Decommit))?
.iter()
.map(|msg| self.handle_round_two_msg(msg))
.collect::<Result<Vec<_>>>()?;

ProcessOutcome::collect_with_messages(round_two_outcomes, round_one_messages)
outcomes.extend(round_two_outcomes);

ProcessOutcome::collect_with_messages(outcomes, round_one_messages)
} else {
// Otherwise, wait for more round 1 messages
Ok(ProcessOutcome::Incomplete)
Expand Down Expand Up @@ -429,7 +437,7 @@ impl TshareParticipant {
MessageType::Tshare(TshareMessageType::R2Decommit),
decom,
)?;
messages.extend_from_slice(&more_messages);
//messages.extend_from_slice(&more_messages);
becgabri marked this conversation as resolved.
Show resolved Hide resolved

let private_coeffs = self
.local_storage
Expand Down Expand Up @@ -463,10 +471,41 @@ impl TshareParticipant {
})
.collect::<Result<Vec<Message>>>()?,
);
messages.extend_from_slice(&more_messages);

Ok(messages)
}

/// Handle the protocol's round two private messages.
///
/// Here we validate and store a private share from someone to us.
#[cfg_attr(feature = "flame_it", flame("tshare"))]
#[instrument(skip_all, err(Debug))]
fn handle_round_two_msg_private(
&mut self,
message: &Message,
) -> Result<ProcessOutcome<<Self as ProtocolParticipant>::Output>> {
self.check_for_duplicate_msg::<storage::ValidPrivateEval>(message.from())?;

info!("Handling round two tshare private message.");

message.check_type(MessageType::Tshare(TshareMessageType::R2PrivateShare))?;
let encrypted_share: EvalEncrypted = deserialize!(&message.unverified_bytes)?;

// Get my private key from the AuxInfo protocol.
let my_dk = self.input.private_auxinfo().decryption_key();

// Decrypt the private share.
let private_share = encrypted_share.decrypt(my_dk)?;
// Write the private share to the storage
self.local_storage
.store_once::<storage::ValidPrivateEval>(message.from(), private_share)?;

Ok(self
.maybe_finish_round2()
.expect("Could not finish round 2"))
}

/// Handle the protocol's round two messages.
///
/// Here we check that the decommitments from each participant are valid.
Expand Down Expand Up @@ -496,34 +535,57 @@ impl TshareParticipant {
.retrieve::<storage::Commit>(message.from())?;
let decom = TshareDecommit::from_message(message, com)?;
self.local_storage
.store_once::<storage::Decommit>(message.from(), decom)?;
.store_once::<storage::Decommit>(message.from(), decom.clone())?;
self.local_storage
.store::<storage::PublicCoeffs>(message.from(), decom.coeff_publics);

Ok(self
.maybe_finish_round2()
.expect("Could not finish round 2"))
}

fn maybe_finish_round2(
&mut self,
) -> Result<ProcessOutcome<<Self as ProtocolParticipant>::Output>> {
let got_all_private_shares = self
.local_storage
.contains_for_all_ids::<storage::ValidPrivateEval>(self.other_ids());

// Check if we've received all the decommits
let r2_done = self
let mut r2_done = self
.local_storage
.contains_for_all_ids::<storage::Decommit>(&self.all_participants());

r2_done &= got_all_private_shares;

if r2_done {
// for each participant, read the private share and check if it matches the
// public share
for pid in self.other_ids() {
let decom = self.local_storage.retrieve::<storage::Decommit>(*pid)?;
let coeff_publics = decom.coeff_publics.clone();
let expected_public = Self::eval_public_share(coeff_publics.as_slice(), self.id())?;
let private_share = self
.local_storage
.retrieve::<storage::ValidPrivateEval>(*pid)?;
let implied_public = private_share.public_point();
if implied_public != expected_public {
error!("the private share does not match the public share");
return Err(InternalError::ProtocolError(Some(*pid)));
}
}

// Generate messages for round 3...
let round_three_messages = run_only_once!(self.gen_round_three_msgs())?;

let mut round_outcomes = self
.fetch_messages(MessageType::Tshare(TshareMessageType::R2PrivateShare))?
.iter()
.map(|msg| self.handle_round_two_msg_private(msg))
.collect::<Result<Vec<_>>>()?;

// ...and handle any messages that other participants have sent for round 3.
let round_three_outcomes = self
.fetch_messages(MessageType::Tshare(TshareMessageType::R3Proof))?
.iter()
.map(|msg| self.handle_round_three_msg(msg))
.collect::<Result<Vec<_>>>()?;

//round_three_outcomes.extend(outcomes_private);
round_outcomes.extend(round_three_outcomes);

ProcessOutcome::collect_with_messages(round_outcomes, round_three_messages)
ProcessOutcome::collect_with_messages(round_three_outcomes, round_three_messages)
} else {
// Otherwise, wait for more round 2 messages.
Ok(ProcessOutcome::Incomplete)
Expand Down Expand Up @@ -576,37 +638,60 @@ impl TshareParticipant {
if let Some(private) = self.input.share() {
assert_eq!(my_contant_term, private.x);
}
let public_share = EvalPublic::new(my_private_share.public_point());

// Generate proofs for each share.
let precom = self
// Have we got the private shares from everybody to us?
let got_all_private_shares = self
.local_storage
.retrieve::<storage::SchnorrPrecom>(self.id())?;
.contains_for_all_ids::<storage::ValidPrivateEval>(self.other_ids());

let pk = &public_share;
let input = CommonInput::new(pk);
let sk = &my_private_share.x;
if got_all_private_shares {
// Compute the one's own private evaluation.

let proof = PiSchProof::prove_from_precommit(
&self.retrieve_context(),
precom,
&input,
&ProverSecret::new(&scalar_to_bn(sk)),
&transcript,
)?;
// Get a slice of EvalPrivate from other participants
let mut from_all_to_me_private = vec![];
for pid in self.other_ids() {
let private_share = self
.local_storage
.retrieve::<storage::ValidPrivateEval>(*pid)?;
from_all_to_me_private.push(private_share.clone());
}

self.local_storage
.store::<storage::ValidPrivateEval>(self.id(), my_private_share.clone());
self.local_storage
.store::<storage::ValidPublicShare>(self.id(), public_share.clone());
let other_private_shares = Self::aggregate_private_shares(&from_all_to_me_private);
let final_private_share = other_private_shares + &my_private_share;
let final_public_share = EvalPublic::new(final_private_share.public_point());

// Generate proofs for each share.
let precom = self
.local_storage
.retrieve::<storage::SchnorrPrecom>(self.id())?;

let pk = &final_public_share;
let input = CommonInput::new(pk);
let sk = &final_private_share.x;

let proof = PiSchProof::prove_from_precommit(
&self.retrieve_context(),
precom,
&input,
&ProverSecret::new(&scalar_to_bn(sk)),
&transcript,
)?;

// Send all proofs to everybody.
let messages = self.message_for_other_participants(
MessageType::Tshare(TshareMessageType::R3Proof),
proof,
)?;
self.local_storage
.store::<storage::ValidPrivateEval>(self.id(), final_private_share.clone());
self.local_storage
.store::<storage::ValidPublicShare>(self.id(), final_public_share.clone());

Ok(messages)
// Send all proofs to everybody.
let messages = self.message_for_other_participants(
MessageType::Tshare(TshareMessageType::R3Proof),
proof,
)?;

Ok(messages)
} else {
Err(InternalError::ProtocolError(None))
}
}

/// Assign a non-null x coordinate to each participant.
Expand Down Expand Up @@ -740,56 +825,6 @@ impl TshareParticipant {
result
}

/// Handle the protocol's round two private messages.
///
/// Here we validate and store a private share from someone to us.
#[cfg_attr(feature = "flame_it", flame("tshare"))]
#[instrument(skip_all, err(Debug))]
fn handle_round_two_msg_private(
&mut self,
message: &Message,
) -> Result<ProcessOutcome<<Self as ProtocolParticipant>::Output>> {
self.check_for_duplicate_msg::<storage::ValidPrivateEval>(message.from())?;

if !self.can_handle_round_three_msg() {
info!("Not yet ready to handle round three tshare private message.");
self.stash_message(message)?;
return Ok(ProcessOutcome::Incomplete);
}
info!("Handling round three tshare private message.");

message.check_type(MessageType::Tshare(TshareMessageType::R2PrivateShare))?;
let encrypted_share: EvalEncrypted = deserialize!(&message.unverified_bytes)?;

// Get my private key from the AuxInfo protocol.
let my_dk = self.input.private_auxinfo().decryption_key();

// Decrypt the private share.
let private_share = encrypted_share.decrypt(my_dk)?;

// Feldman validation
// Check that this private share matches our public share in TshareDecommit
// from this participant.
let decom = self
.local_storage
.retrieve::<storage::Decommit>(message.from())?;
let coeff_publics = decom.coeff_publics.clone();
let expected_public = Self::eval_public_share(coeff_publics.as_slice(), self.id())?;
let implied_public = private_share.public_point();
if implied_public != expected_public {
error!("the private share does not match the public share");
return Err(InternalError::ProtocolError(Some(message.from())));
}

self.local_storage
.store::<storage::ValidPrivateEval>(message.from(), private_share);

self.local_storage
.store::<storage::PublicCoeffs>(message.from(), coeff_publics);

self.maybe_finish()
}

/// Handle round three messages only after our own `gen_round_three_msgs`.
fn can_handle_round_three_msg(&self) -> bool {
self.local_storage.contains::<storage::GlobalRid>(self.id())
Expand Down Expand Up @@ -821,38 +856,40 @@ impl TshareParticipant {
let decom = self
.local_storage
.retrieve::<storage::Decommit>(message.from())?;
// calculate public share from the public coefficients
let public_share = Self::eval_public_share(&decom.coeff_publics, message.from())?;
let public_share = EvalPublic::new(public_share);

let mut final_public_share = CurvePoint::IDENTITY;
for pid in self.all_participants().iter() {
let coeff_publics = self.local_storage.retrieve::<storage::PublicCoeffs>(*pid)?;
let public_share = Self::eval_public_share(coeff_publics, message.from())?;
final_public_share = final_public_share + public_share;
}

let mut transcript = schnorr_proof_transcript(self.sid(), &global_rid, message.from())?;
proof.verify_with_precommit(
CommonInput::new(&public_share),
CommonInput::new(&final_public_share),
&self.retrieve_context(),
&mut transcript,
&decom.precom,
)?;

let final_public_share = EvalPublic::new(final_public_share);
// Only if the proof verifies do we store the participant's shares.
self.local_storage
.store_once::<storage::ValidPublicShare>(message.from(), public_share.clone())?;
.store_once::<storage::ValidPublicShare>(message.from(), final_public_share.clone())?;

self.maybe_finish()
self.maybe_finish_protocol()
}

fn maybe_finish(&mut self) -> Result<ProcessOutcome<<Self as ProtocolParticipant>::Output>> {
fn maybe_finish_protocol(
&mut self,
) -> Result<ProcessOutcome<<Self as ProtocolParticipant>::Output>> {
// Have we validated and stored the public shares from everybody to everybody?
let got_all_public_shares = self
.local_storage
.contains_for_all_ids::<storage::ValidPublicShare>(&self.all_participants());

// Have we got the private shares from everybody to us?
let got_all_private_shares = self
.local_storage
.contains_for_all_ids::<storage::ValidPrivateEval>(&self.all_participants());

// If so, we completed the protocol! Return the outputs.
if got_all_public_shares && got_all_private_shares {
if got_all_public_shares {
// Compute the public polynomial.
let coeffs_from_all = self
.all_participants()
Expand All @@ -861,13 +898,11 @@ impl TshareParticipant {
.collect::<Result<Vec<_>>>()?;
let all_public_coeffs = Self::aggregate_public_coeffs(&coeffs_from_all);

// Compute the one's own private evaluation.
let from_all_to_me_private = self
.all_participants()
.iter()
.map(|pid| self.local_storage.remove::<storage::ValidPrivateEval>(*pid))
.collect::<Result<Vec<_>>>()?;
let my_private_share = Self::aggregate_private_shares(&from_all_to_me_private);
// Read my_private_share from the storage, it was already aggregated in the end
// of round 2
let my_private_share = self
.local_storage
.retrieve::<storage::ValidPrivateEval>(self.id())?;

// Double-check that the aggregated private share matches the aggregated public
// coeffs.
Expand Down
9 changes: 9 additions & 0 deletions src/tshare/share.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ pub struct EvalPrivate {
pub x: Scalar,
}

/// Implement addition operation for `EvalPrivate`.
impl Add<&EvalPrivate> for EvalPrivate {
type Output = Self;

fn add(self, rhs: &EvalPrivate) -> Self::Output {
EvalPrivate { x: self.x + rhs.x }
}
}

impl EvalPrivate {
pub fn new(x: Scalar) -> Self {
EvalPrivate { x }
Expand Down
Loading