2018-10-23 02:49:19 -07:00
|
|
|
//! # Collaborative Threshold Signing
|
2018-07-02 03:09:06 -07:00
|
|
|
//!
|
2018-11-06 08:26:48 -08:00
|
|
|
//! The algorithm is instantiated and waits to recieve a document to sign, as well as signature
|
|
|
|
//! shares from threshold signature validation peers. Once `set_document` is successfully called,
|
|
|
|
//! then `handle_input(())` or `sign()` is called, which then sends a signature share to each
|
|
|
|
//! threshold signature validation peer. When at least _f + 1_ validators have shared their
|
|
|
|
//! signatures in this manner, each node outputs the same, valid signature of the data.
|
2018-07-02 03:09:06 -07:00
|
|
|
//!
|
2018-10-23 02:49:19 -07:00
|
|
|
//! In addition to signing, this can also be used as a source of pseudorandomness: The signature
|
|
|
|
//! cannot be known until more than _f_ validators have contributed their shares.
|
2018-07-02 03:09:06 -07:00
|
|
|
//!
|
|
|
|
//! ## How it works
|
|
|
|
//!
|
|
|
|
//! The algorithm uses a threshold signature scheme with the uniqueness property: For each public
|
2018-07-02 23:53:08 -07:00
|
|
|
//! key and message, there is exactly one valid signature. This group signature is produced using
|
2018-10-23 02:49:19 -07:00
|
|
|
//! signature shares from any combination of _f + 1_ secret key share holders.
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-07-24 02:57:50 -07:00
|
|
|
use std::collections::BTreeMap;
|
2018-07-11 12:15:08 -07:00
|
|
|
use std::sync::Arc;
|
2018-11-01 03:22:40 -07:00
|
|
|
use std::{fmt, result};
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
use crypto::{self, hash_g2, Signature, SignatureShare, G2};
|
2018-10-29 07:36:56 -07:00
|
|
|
use failure::Fail;
|
2018-11-01 03:22:40 -07:00
|
|
|
use log::debug;
|
2018-10-29 07:36:56 -07:00
|
|
|
use rand_derive::Rand;
|
|
|
|
use serde_derive::{Deserialize, Serialize};
|
|
|
|
|
2018-11-06 08:26:48 -08:00
|
|
|
use fault_log::{Fault, FaultKind, FaultLog};
|
2018-10-10 07:11:27 -07:00
|
|
|
use {DistAlgorithm, NetworkInfo, NodeIdT, Target};
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
/// A threshold signing error.
|
2018-07-25 14:38:33 -07:00
|
|
|
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
|
|
|
pub enum Error {
|
2018-11-26 06:35:24 -08:00
|
|
|
/// Redundant input provided.
|
2018-11-06 08:26:48 -08:00
|
|
|
#[fail(display = "Redundant input provided")]
|
|
|
|
MultipleMessagesToSign,
|
2018-11-26 06:35:24 -08:00
|
|
|
/// Error combining and verifying signature shares.
|
|
|
|
#[fail(
|
|
|
|
display = "Error combining and verifying signature shares: {}",
|
|
|
|
_0
|
|
|
|
)]
|
2018-10-10 07:11:27 -07:00
|
|
|
CombineAndVerifySigCrypto(crypto::error::Error),
|
2018-11-26 06:35:24 -08:00
|
|
|
/// Unknown sender
|
2018-07-25 14:38:33 -07:00
|
|
|
#[fail(display = "Unknown sender")]
|
|
|
|
UnknownSender,
|
2018-11-26 06:35:24 -08:00
|
|
|
/// Signature verification failed.
|
2018-07-25 14:38:33 -07:00
|
|
|
#[fail(display = "Signature verification failed")]
|
|
|
|
VerificationFailed,
|
2018-11-26 06:35:24 -08:00
|
|
|
/// Document hash is not set, cannot sign or verify signatures.
|
2018-11-06 08:26:48 -08:00
|
|
|
#[fail(display = "Document hash is not set, cannot sign or verify signatures")]
|
|
|
|
DocumentHashIsNone,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
/// A threshold signing result.
|
2018-07-25 14:38:33 -07:00
|
|
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
|
2018-11-26 06:35:24 -08:00
|
|
|
/// A threshold signing message, containing a signature share.
|
2018-07-05 09:20:53 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
|
2018-11-26 06:35:24 -08:00
|
|
|
pub struct Message(pub SignatureShare);
|
2018-06-09 13:50:36 -07:00
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
/// A threshold signing algorithm instance. On input, broadcasts our threshold signature share. Upon
|
2018-06-08 11:43:27 -07:00
|
|
|
/// receiving at least `num_faulty + 1` shares, attempts to combine them into a signature. If that
|
|
|
|
/// signature is valid, the instance outputs it and terminates; otherwise the instance aborts.
|
2018-06-04 14:00:38 -07:00
|
|
|
#[derive(Debug)]
|
2018-10-23 02:49:19 -07:00
|
|
|
pub struct ThresholdSign<N> {
|
2018-08-02 14:27:55 -07:00
|
|
|
netinfo: Arc<NetworkInfo<N>>,
|
2018-11-06 08:26:48 -08:00
|
|
|
/// The hash of the document to be signed.
|
|
|
|
doc_hash: Option<G2>,
|
2018-06-08 11:43:27 -07:00
|
|
|
/// All received threshold signature shares.
|
2018-08-02 14:27:55 -07:00
|
|
|
received_shares: BTreeMap<N, SignatureShare>,
|
2018-10-23 02:49:19 -07:00
|
|
|
/// Whether we already sent our shares.
|
2018-06-10 03:36:13 -07:00
|
|
|
had_input: bool,
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Termination flag.
|
|
|
|
terminated: bool,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-11-26 06:35:24 -08:00
|
|
|
/// A step returned from `ThresholdSign`. It contains at most one output.
|
2018-11-07 05:26:14 -08:00
|
|
|
pub type Step<N> = ::DaStep<ThresholdSign<N>>;
|
2018-07-09 04:35:26 -07:00
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
impl<N: NodeIdT> DistAlgorithm for ThresholdSign<N> {
|
2018-08-29 09:08:35 -07:00
|
|
|
type NodeId = N;
|
2018-06-04 14:00:38 -07:00
|
|
|
type Input = ();
|
2018-10-23 02:49:19 -07:00
|
|
|
type Output = Signature;
|
|
|
|
type Message = Message;
|
2018-06-04 14:00:38 -07:00
|
|
|
type Error = Error;
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Sends our threshold signature share if not yet sent.
|
2018-10-23 06:38:42 -07:00
|
|
|
fn handle_input(&mut self, _input: ()) -> Result<Step<N>> {
|
|
|
|
self.sign()
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Receives input from a remote node.
|
2018-10-23 06:38:42 -07:00
|
|
|
fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
|
|
|
|
self.handle_message(sender_id, message)
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether the algorithm has terminated.
|
|
|
|
fn terminated(&self) -> bool {
|
2018-06-08 11:43:27 -07:00
|
|
|
self.terminated
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-08-29 09:08:35 -07:00
|
|
|
fn our_id(&self) -> &Self::NodeId {
|
|
|
|
self.netinfo.our_id()
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
impl<N: NodeIdT> ThresholdSign<N> {
|
2018-11-06 08:26:48 -08:00
|
|
|
/// Creates a new instance of `ThresholdSign`, with the goal to collaboratively sign `doc`.
|
|
|
|
pub fn new(netinfo: Arc<NetworkInfo<N>>) -> Self {
|
2018-10-23 02:49:19 -07:00
|
|
|
ThresholdSign {
|
2018-06-14 02:05:05 -07:00
|
|
|
netinfo,
|
2018-11-06 08:26:48 -08:00
|
|
|
doc_hash: None,
|
2018-06-08 11:43:27 -07:00
|
|
|
received_shares: BTreeMap::new(),
|
2018-06-10 03:36:13 -07:00
|
|
|
had_input: false,
|
2018-06-08 11:43:27 -07:00
|
|
|
terminated: false,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
}
|
2018-06-07 09:38:27 -07:00
|
|
|
|
2018-11-06 08:26:48 -08:00
|
|
|
/// Creates a new instance of `ThresholdSign`, including setting the document to sign.
|
|
|
|
pub fn new_with_document<M: AsRef<[u8]>>(netinfo: Arc<NetworkInfo<N>>, doc: M) -> Result<Self> {
|
|
|
|
let mut ts = ThresholdSign::new(netinfo);
|
|
|
|
ts.set_document(doc)?;
|
|
|
|
Ok(ts)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets doc_hash. Signature shares can only be sent after this function is completed.
|
|
|
|
pub fn set_document<M: AsRef<[u8]>>(&mut self, doc: M) -> Result<()> {
|
|
|
|
if self.doc_hash.is_some() {
|
|
|
|
return Err(Error::MultipleMessagesToSign);
|
|
|
|
}
|
|
|
|
self.doc_hash = Some(hash_g2(doc));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-10-23 06:38:42 -07:00
|
|
|
/// Sends our signature shares, and if we have collected enough, returns the full signature.
|
2018-11-06 08:26:48 -08:00
|
|
|
/// Returns an error if the message to sign hasn't been received yet.
|
2018-10-23 06:38:42 -07:00
|
|
|
pub fn sign(&mut self) -> Result<Step<N>> {
|
2018-10-23 02:49:19 -07:00
|
|
|
if self.had_input {
|
2018-11-06 08:26:48 -08:00
|
|
|
// Don't waste time on redundant shares.
|
2018-10-23 02:49:19 -07:00
|
|
|
return Ok(Step::default());
|
|
|
|
}
|
2018-11-06 08:26:48 -08:00
|
|
|
let hash = self.doc_hash.ok_or(Error::DocumentHashIsNone)?;
|
2018-10-23 02:49:19 -07:00
|
|
|
self.had_input = true;
|
2018-11-06 08:26:48 -08:00
|
|
|
let mut step = Step::default();
|
|
|
|
step.fault_log.extend(self.remove_invalid_shares());
|
2018-06-29 08:20:54 -07:00
|
|
|
if !self.netinfo.is_validator() {
|
2018-11-06 08:26:48 -08:00
|
|
|
return Ok(step.join(self.try_output()?));
|
2018-06-25 12:09:45 -07:00
|
|
|
}
|
2018-11-06 08:26:48 -08:00
|
|
|
let msg = Message(self.netinfo.secret_key_share().sign_g2(hash));
|
|
|
|
step.messages.push(Target::All.message(msg.clone()));
|
2018-10-25 05:44:28 -07:00
|
|
|
let id = self.our_id().clone();
|
2018-10-23 06:38:42 -07:00
|
|
|
step.extend(self.handle_message(&id, msg)?);
|
2018-07-24 02:57:50 -07:00
|
|
|
Ok(step)
|
2018-06-08 11:43:27 -07:00
|
|
|
}
|
|
|
|
|
2018-10-24 03:26:43 -07:00
|
|
|
/// Handles a message with a signature share received from `sender_id`.
|
|
|
|
///
|
|
|
|
/// This must be called with every message we receive from another node.
|
|
|
|
///
|
|
|
|
/// If we have collected enough, returns the full signature.
|
2018-10-23 06:38:42 -07:00
|
|
|
pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
|
|
|
|
if self.terminated {
|
|
|
|
return Ok(Step::default());
|
|
|
|
}
|
|
|
|
let Message(share) = message;
|
2018-11-06 08:26:48 -08:00
|
|
|
// Before checking the share, ensure the sender is a known validator
|
|
|
|
self.netinfo
|
2018-11-01 03:22:40 -07:00
|
|
|
.public_key_share(sender_id)
|
2018-11-06 08:26:48 -08:00
|
|
|
.ok_or(Error::UnknownSender)?;
|
|
|
|
if !self.is_share_valid(sender_id, &share) {
|
2018-11-01 03:22:40 -07:00
|
|
|
let fault_kind = FaultKind::UnverifiedSignatureShareSender;
|
|
|
|
return Ok(Fault::new(sender_id.clone(), fault_kind).into());
|
2018-06-25 12:09:45 -07:00
|
|
|
}
|
2018-11-01 03:22:40 -07:00
|
|
|
self.received_shares.insert(sender_id.clone(), share);
|
2018-07-24 02:57:50 -07:00
|
|
|
self.try_output()
|
2018-06-25 12:09:45 -07:00
|
|
|
}
|
|
|
|
|
2018-11-06 08:26:48 -08:00
|
|
|
/// Removes all shares that are invalid, and returns faults for their senders.
|
|
|
|
fn remove_invalid_shares(&mut self) -> FaultLog<N> {
|
|
|
|
let faulty_senders: Vec<N> = self
|
|
|
|
.received_shares
|
|
|
|
.iter()
|
|
|
|
.filter(|(id, share)| !self.is_share_valid(id, share))
|
|
|
|
.map(|(id, _)| id.clone())
|
|
|
|
.collect();
|
|
|
|
let mut fault_log = FaultLog::default();
|
|
|
|
for id in faulty_senders {
|
|
|
|
self.received_shares.remove(&id);
|
|
|
|
fault_log.append(id, FaultKind::UnverifiedSignatureShareSender);
|
|
|
|
}
|
|
|
|
fault_log
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if the share is valid, or if we don't have the message data yet.
|
|
|
|
fn is_share_valid(&self, id: &N, share: &SignatureShare) -> bool {
|
|
|
|
let hash = match self.doc_hash {
|
|
|
|
None => return true, // No document yet. Verification postponed.
|
|
|
|
Some(ref doc_hash) => doc_hash,
|
|
|
|
};
|
|
|
|
match self.netinfo.public_key_share(id) {
|
|
|
|
None => false, // Unknown sender.
|
|
|
|
Some(pk_i) => pk_i.verify_g2(&share, *hash),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 02:49:19 -07:00
|
|
|
fn try_output(&mut self) -> Result<Step<N>> {
|
2018-11-06 08:26:48 -08:00
|
|
|
let hash = match self.doc_hash {
|
|
|
|
Some(hash) => hash,
|
|
|
|
None => return Ok(Step::default()),
|
|
|
|
};
|
|
|
|
if !self.terminated && self.received_shares.len() > self.netinfo.num_faulty() {
|
|
|
|
let sig = self.combine_and_verify_sig(hash)?;
|
|
|
|
self.terminated = true;
|
2018-11-01 03:22:40 -07:00
|
|
|
let step = self.sign()?; // Before terminating, make sure we sent our share.
|
|
|
|
debug!("{} output {:?}", self, sig);
|
2018-10-23 02:49:19 -07:00
|
|
|
Ok(step.with_output(sig))
|
2018-07-24 02:57:50 -07:00
|
|
|
} else {
|
2018-11-01 03:22:40 -07:00
|
|
|
debug!(
|
|
|
|
"{} received {} shares, {}",
|
|
|
|
self,
|
|
|
|
self.received_shares.len(),
|
|
|
|
if self.had_input { ", had input" } else { "" }
|
|
|
|
);
|
2018-07-24 02:57:50 -07:00
|
|
|
Ok(Step::default())
|
2018-06-08 11:43:27 -07:00
|
|
|
}
|
2018-06-07 09:38:27 -07:00
|
|
|
}
|
2018-06-14 04:28:38 -07:00
|
|
|
|
2018-11-06 08:26:48 -08:00
|
|
|
fn combine_and_verify_sig(&self, hash: G2) -> Result<Signature> {
|
2018-06-14 04:28:38 -07:00
|
|
|
// Pass the indices of sender nodes to `combine_signatures`.
|
2018-07-23 02:50:44 -07:00
|
|
|
let to_idx = |(id, share)| (self.netinfo.node_index(id).unwrap(), share);
|
|
|
|
let shares = self.received_shares.iter().map(to_idx);
|
2018-07-25 14:38:33 -07:00
|
|
|
let sig = self
|
|
|
|
.netinfo
|
|
|
|
.public_key_set()
|
|
|
|
.combine_signatures(shares)
|
|
|
|
.map_err(Error::CombineAndVerifySigCrypto)?;
|
2018-06-14 04:28:38 -07:00
|
|
|
if !self
|
|
|
|
.netinfo
|
|
|
|
.public_key_set()
|
|
|
|
.public_key()
|
2018-11-06 08:26:48 -08:00
|
|
|
.verify_g2(&sig, hash)
|
2018-06-14 04:28:38 -07:00
|
|
|
{
|
2018-07-25 14:38:33 -07:00
|
|
|
Err(Error::VerificationFailed)
|
2018-06-14 04:28:38 -07:00
|
|
|
} else {
|
|
|
|
Ok(sig)
|
|
|
|
}
|
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
2018-11-01 03:22:40 -07:00
|
|
|
|
|
|
|
impl<N: NodeIdT> fmt::Display for ThresholdSign<N> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
2018-11-06 08:26:48 -08:00
|
|
|
write!(f, "{:?} TS({:?})", self.our_id(), self.doc_hash)
|
2018-11-01 03:22:40 -07:00
|
|
|
}
|
|
|
|
}
|