Skip to content

Commit

Permalink
inv-tshare: Threshold (re-)sharing protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
Aurélien Nicolas committed Jul 20, 2024
1 parent dbe9f29 commit 8781afe
Show file tree
Hide file tree
Showing 11 changed files with 1,888 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/broadcast/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub(crate) struct BroadcastParticipant {
pub(crate) enum BroadcastTag {
AuxinfoR1CommitHash,
KeyGenR1CommitHash,
TshareR1CommitHash,
KeyRefreshR1CommitHash,
PresignR1Ciphertexts,
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ pub mod presign;
mod protocol;
mod ring_pedersen;
pub mod sign;
pub mod tshare;
mod utils;
mod zkp;
mod zkstar;
Expand Down
31 changes: 31 additions & 0 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum MessageType {
Auxinfo(AuxinfoMessageType),
/// Keygen messages
Keygen(KeygenMessageType),
/// Tshare messages
Tshare(TshareMessageType),
/// Keyrefresh messages
Keyrefresh(KeyrefreshMessageType),
/// Presign messages
Expand Down Expand Up @@ -68,6 +70,22 @@ pub enum KeygenMessageType {
R3Proof,
}

/// An enum consisting of all tshare message types
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum TshareMessageType {
/// Signal to self that we're ready to run the protocol
Ready,
/// A hash commitment to the public keyshare and associated proofs
R1CommitHash,
/// The information committed to in Round 1
R2Decommit,
/// A proof of knowledge of the discrete log of the value decommitted in
/// Round 2
R3Proofs,
/// The encrypted private share from a participant to another.
R3PrivateShare,
}

/// An enum consisting of all keyrefresh message types
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyrefreshMessageType {
Expand Down Expand Up @@ -211,4 +229,17 @@ impl Message {
}
Ok(())
}

/// Check if the message type is one of the valid options.
pub(crate) fn check_one_of_type(&self, expected_types: &[MessageType]) -> Result<()> {
if !expected_types.contains(&self.message_type()) {
error!(
"A message was misrouted. Expected one of {:?}, Got {:?}",
expected_types,
self.message_type()
);
return Err(InternalError::InternalInvariantFailed);
}
Ok(())
}
}
7 changes: 7 additions & 0 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use tracing::{error, info, instrument, trace};
#[derive(Debug)]
pub enum ProtocolType {
Keygen,
Tshare,
Keyrefresh,
AuxInfo,
Presign,
Expand Down Expand Up @@ -168,6 +169,7 @@ impl<P: ProtocolParticipant> Participant<P> {
match (message.message_type(), P::protocol_type()) {
(MessageType::Auxinfo(_), ProtocolType::AuxInfo)
| (MessageType::Keygen(_), ProtocolType::Keygen)
| (MessageType::Tshare(_), ProtocolType::Tshare)
| (MessageType::Keyrefresh(_), ProtocolType::Keyrefresh)
| (MessageType::Presign(_), ProtocolType::Presign)
| (MessageType::Sign(_), ProtocolType::Sign)
Expand Down Expand Up @@ -420,6 +422,11 @@ impl ParticipantIdentifier {
pub fn from_u128(id: u128) -> Self {
Self(id)
}

/// Get the ID as a number.
pub fn as_u128(&self) -> u128 {
self.0
}
}

/// The `SharedContext` contains fixed known parameters across the entire
Expand Down
139 changes: 139 additions & 0 deletions src/tshare/commit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) Facebook, Inc. and its affiliates.
// Modifications Copyright (c) 2022-2023 Bolt Labs Holdings, Inc
//
// This source code is licensed under both the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.

use super::share::CoeffPublic;
use crate::{
errors::{InternalError, Result},
messages::{Message, MessageType, TshareMessageType},
protocol::{Identifier, ParticipantIdentifier},
utils::CurvePoint,
};
use merlin::Transcript;
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use tracing::error;

/// Public commitment to `TshareDecommit` in round 1.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub(crate) struct TshareCommit {
hash: [u8; 32],
}
impl TshareCommit {
pub(crate) fn from_message(message: &Message) -> Result<Self> {
message.check_type(MessageType::Tshare(TshareMessageType::R1CommitHash))?;
let tshare_commit: TshareCommit = deserialize!(&message.unverified_bytes)?;
Ok(tshare_commit)
}
}

/// Decommitment published in round 2.
#[derive(Serialize, Deserialize, Clone)]
pub(crate) struct TshareDecommit {
sid: Identifier,
sender: ParticipantIdentifier,
u_i: [u8; 32], // The blinding factor is never read but it is included in the commitment.
pub rid: [u8; 32],
pub coeff_publics: Vec<CoeffPublic>,
pub As: Vec<CurvePoint>,
}

impl TshareDecommit {
///`sid` corresponds to a unique session identifier.
pub(crate) fn new<R: RngCore + CryptoRng>(
rng: &mut R,
sid: &Identifier,
sender: &ParticipantIdentifier,
coeff_publics: Vec<CoeffPublic>,
sch_precoms: Vec<CurvePoint>,
) -> Self {
let mut rid = [0u8; 32];
let mut u_i = [0u8; 32];
rng.fill_bytes(rid.as_mut_slice());
rng.fill_bytes(u_i.as_mut_slice());
Self {
sid: *sid,
sender: *sender,
rid,
u_i,
coeff_publics,
As: sch_precoms,
}
}

/// Deserialize a TshareDecommit from a message and verify it.
pub(crate) fn from_message(
message: &Message,
com: &TshareCommit,
threshold: usize,
) -> Result<Self> {
message.check_type(MessageType::Tshare(TshareMessageType::R2Decommit))?;
let tshare_decommit: TshareDecommit = deserialize!(&message.unverified_bytes)?;
tshare_decommit.verify(message.id(), message.from(), com, threshold)?;
Ok(tshare_decommit)
}

pub(crate) fn commit(&self) -> Result<TshareCommit> {
let mut transcript = Transcript::new(b"TshareR1");
transcript.append_message(b"decom", &serialize!(&self)?);
let mut hash = [0u8; 32];
transcript.challenge_bytes(b"hashing r1", &mut hash);
Ok(TshareCommit { hash })
}

/// Verify this TshareDecommit against a commitment and expected
/// content.
fn verify(
&self,
sid: Identifier,
sender: ParticipantIdentifier,
com: &TshareCommit,
threshold: usize,
) -> Result<()> {
// Check the commitment.
let rebuilt_com = self.commit()?;
if &rebuilt_com != com {
error!("decommitment does not match original commitment");
return Err(InternalError::ProtocolError(Some(sender)));
}

// Check the session ID and sender ID.
if self.sid != sid {
error!("Incorrect session ID");
return Err(InternalError::ProtocolError(Some(sender)));
}
if self.sender != sender {
error!("Incorrect sender ID");
return Err(InternalError::ProtocolError(Some(sender)));
}

// Check the number of commitments As.
if self.As.len() != threshold {
error!("Incorrect number of As");
return Err(InternalError::ProtocolError(Some(sender)));
}

// Check the set of coefficients.
if self.coeff_publics.len() != threshold {
error!("Incorrect number of public shares");
return Err(InternalError::ProtocolError(Some(sender)));
}

Ok(())
}
}

// Implement custom Debug to avoid leaking secret information.
impl std::fmt::Debug for TshareDecommit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TshareDecommit")
.field("sid", &self.sid)
.field("sender", &self.sender)
.field("...", &"[redacted]")
.finish()
}
}
Loading

0 comments on commit 8781afe

Please sign in to comment.