2018-07-02 03:09:06 -07:00
|
|
|
//! # A Cryptographic Common Coin
|
|
|
|
//!
|
2018-07-05 08:51:55 -07:00
|
|
|
//! The Common Coin produces a pseudorandom binary value that the correct nodes agree on, and that
|
2018-07-02 03:09:06 -07:00
|
|
|
//! cannot be known beforehand.
|
|
|
|
//!
|
|
|
|
//! Every Common Coin instance has a _nonce_ that determines the value, without giving it away: It
|
2018-07-05 08:51:55 -07:00
|
|
|
//! is not feasible to compute the output from the nonce alone, and the output is uniformly
|
|
|
|
//! distributed.
|
2018-07-02 03:09:06 -07:00
|
|
|
//!
|
2018-07-02 06:42:31 -07:00
|
|
|
//! The nodes input a signal (no data, just `()`), and after _2 f + 1_ nodes have provided input,
|
2018-07-02 03:09:06 -07:00
|
|
|
//! everyone receives the output value. In particular, the adversary cannot know the output value
|
|
|
|
//! before at least one correct node has provided input.
|
|
|
|
//!
|
|
|
|
//! ## 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
|
|
|
|
//! signature shares from any combination of _2 f + 1_ secret key share holders.
|
2018-07-02 03:09:06 -07:00
|
|
|
//!
|
|
|
|
//! * On input, a node signs the nonce and sends its signature share to everyone else.
|
2018-07-02 06:42:31 -07:00
|
|
|
//! * When a node has received _2 f + 1_ shares, it computes the main signature and outputs the XOR
|
2018-07-02 03:09:06 -07:00
|
|
|
//! of its bits.
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
use std::collections::{BTreeMap, VecDeque};
|
2018-06-04 14:00:38 -07:00
|
|
|
use std::fmt::Debug;
|
2018-07-11 12:15:08 -07:00
|
|
|
use std::sync::Arc;
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
use crypto::error as cerror;
|
|
|
|
use crypto::Signature;
|
2018-07-08 09:41:50 -07:00
|
|
|
use fault_log::{FaultKind, FaultLog};
|
2018-07-09 04:35:26 -07:00
|
|
|
use messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage};
|
2018-06-04 14:00:38 -07:00
|
|
|
|
|
|
|
error_chain! {
|
2018-06-08 11:43:27 -07:00
|
|
|
links {
|
|
|
|
Crypto(cerror::Error, cerror::ErrorKind);
|
|
|
|
}
|
|
|
|
|
2018-06-04 14:00:38 -07:00
|
|
|
errors {
|
2018-06-08 11:43:27 -07:00
|
|
|
UnknownSender {
|
|
|
|
description("unknown sender")
|
|
|
|
}
|
2018-06-09 02:03:38 -07:00
|
|
|
VerificationFailed {
|
|
|
|
description("signature verification failed")
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-20 01:21:52 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
2018-06-21 08:31:15 -07:00
|
|
|
pub struct CommonCoinMessage(Signature);
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-06-09 13:50:36 -07:00
|
|
|
impl CommonCoinMessage {
|
2018-06-21 08:31:15 -07:00
|
|
|
pub fn new(sig: Signature) -> Self {
|
2018-06-09 13:50:36 -07:00
|
|
|
CommonCoinMessage(sig)
|
|
|
|
}
|
|
|
|
|
2018-06-21 08:31:15 -07:00
|
|
|
pub fn to_sig(&self) -> &Signature {
|
2018-06-09 13:50:36 -07:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// A common coin algorithm instance. On input, broadcasts our threshold signature share. Upon
|
|
|
|
/// 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-06-18 07:37:07 -07:00
|
|
|
pub struct CommonCoin<NodeUid, T> {
|
2018-07-11 12:15:08 -07:00
|
|
|
netinfo: Arc<NetworkInfo<NodeUid>>,
|
2018-06-08 11:43:27 -07:00
|
|
|
/// The name of this common coin. It is required to be unique for each common coin round.
|
|
|
|
nonce: T,
|
|
|
|
/// The result of combination of at least `num_faulty + 1` threshold signature shares.
|
|
|
|
output: Option<bool>,
|
|
|
|
/// Outgoing message queue.
|
|
|
|
messages: VecDeque<CommonCoinMessage>,
|
|
|
|
/// All received threshold signature shares.
|
2018-06-21 08:31:15 -07:00
|
|
|
received_shares: BTreeMap<NodeUid, Signature>,
|
2018-06-10 03:36:13 -07:00
|
|
|
/// Whether we provided input to the common coin.
|
|
|
|
had_input: bool,
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Termination flag.
|
|
|
|
terminated: bool,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-07-09 04:35:26 -07:00
|
|
|
pub type CommonCoinStep<N> = Step<N, bool>;
|
|
|
|
|
2018-06-18 07:14:17 -07:00
|
|
|
impl<NodeUid, T> DistAlgorithm for CommonCoin<NodeUid, T>
|
2018-06-04 14:00:38 -07:00
|
|
|
where
|
2018-06-18 07:37:07 -07:00
|
|
|
NodeUid: Clone + Debug + Ord,
|
2018-06-08 11:43:27 -07:00
|
|
|
T: Clone + AsRef<[u8]>,
|
2018-06-04 14:00:38 -07:00
|
|
|
{
|
2018-06-18 07:14:17 -07:00
|
|
|
type NodeUid = NodeUid;
|
2018-06-04 14:00:38 -07:00
|
|
|
type Input = ();
|
2018-06-08 11:43:27 -07:00
|
|
|
type Output = bool;
|
2018-06-04 14:00:38 -07:00
|
|
|
type Message = CommonCoinMessage;
|
|
|
|
type Error = Error;
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Sends our threshold signature share if not yet sent.
|
2018-07-09 04:35:26 -07:00
|
|
|
fn input(&mut self, _input: Self::Input) -> Result<CommonCoinStep<NodeUid>> {
|
|
|
|
let fault_log = if !self.had_input {
|
2018-06-10 03:36:13 -07:00
|
|
|
self.had_input = true;
|
2018-07-09 04:35:26 -07:00
|
|
|
self.get_coin()?
|
2018-06-08 11:43:27 -07:00
|
|
|
} else {
|
2018-07-09 04:35:26 -07:00
|
|
|
FaultLog::new()
|
|
|
|
};
|
|
|
|
self.step().with_fault_log(fault_log)
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Receives input from a remote node.
|
2018-07-08 09:41:50 -07:00
|
|
|
fn handle_message(
|
|
|
|
&mut self,
|
|
|
|
sender_id: &Self::NodeUid,
|
|
|
|
message: Self::Message,
|
2018-07-09 04:35:26 -07:00
|
|
|
) -> Result<CommonCoinStep<NodeUid>> {
|
|
|
|
let fault_log = if !self.terminated {
|
|
|
|
let CommonCoinMessage(share) = message;
|
|
|
|
self.handle_share(sender_id, share)?
|
|
|
|
} else {
|
|
|
|
FaultLog::default()
|
|
|
|
};
|
|
|
|
self.step().with_fault_log(fault_log)
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Takes the next share of a threshold signature message for multicasting to all other nodes.
|
2018-06-04 14:00:38 -07:00
|
|
|
fn next_message(&mut self) -> Option<TargetedMessage<Self::Message, Self::NodeUid>> {
|
2018-06-08 11:43:27 -07:00
|
|
|
self.messages
|
|
|
|
.pop_front()
|
|
|
|
.map(|msg| Target::All.message(msg))
|
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
|
|
|
}
|
|
|
|
|
|
|
|
fn our_id(&self) -> &Self::NodeUid {
|
|
|
|
self.netinfo.our_uid()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-18 07:14:17 -07:00
|
|
|
impl<NodeUid, T> CommonCoin<NodeUid, T>
|
2018-06-04 14:00:38 -07:00
|
|
|
where
|
2018-06-18 07:37:07 -07:00
|
|
|
NodeUid: Clone + Debug + Ord,
|
2018-06-08 11:43:27 -07:00
|
|
|
T: Clone + AsRef<[u8]>,
|
2018-06-04 14:00:38 -07:00
|
|
|
{
|
2018-07-11 12:15:08 -07:00
|
|
|
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, nonce: T) -> Self {
|
2018-06-04 14:00:38 -07:00
|
|
|
CommonCoin {
|
2018-06-14 02:05:05 -07:00
|
|
|
netinfo,
|
2018-06-08 11:43:27 -07:00
|
|
|
nonce,
|
2018-06-04 14:00:38 -07:00
|
|
|
output: None,
|
|
|
|
messages: VecDeque::new(),
|
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-07-09 04:35:26 -07:00
|
|
|
fn step(&mut self) -> Result<CommonCoinStep<NodeUid>> {
|
2018-07-10 04:23:50 -07:00
|
|
|
Ok(Step::new(self.output.take()))
|
2018-07-09 04:35:26 -07:00
|
|
|
}
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
fn get_coin(&mut self) -> Result<FaultLog<NodeUid>> {
|
2018-06-29 08:20:54 -07:00
|
|
|
if !self.netinfo.is_validator() {
|
2018-07-08 09:41:50 -07:00
|
|
|
self.try_output()?;
|
|
|
|
return Ok(FaultLog::new());
|
2018-06-25 12:09:45 -07:00
|
|
|
}
|
2018-06-08 11:43:27 -07:00
|
|
|
let share = self.netinfo.secret_key().sign(&self.nonce);
|
2018-06-09 13:50:36 -07:00
|
|
|
self.messages.push_back(CommonCoinMessage(share.clone()));
|
2018-06-08 11:43:27 -07:00
|
|
|
let id = self.netinfo.our_uid().clone();
|
|
|
|
self.handle_share(&id, share)
|
|
|
|
}
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
fn handle_share(&mut self, sender_id: &NodeUid, share: Signature) -> Result<FaultLog<NodeUid>> {
|
2018-06-27 05:25:32 -07:00
|
|
|
if let Some(pk_i) = self.netinfo.public_key_share(sender_id) {
|
2018-06-14 02:05:05 -07:00
|
|
|
if !pk_i.verify(&share, &self.nonce) {
|
2018-07-08 09:41:50 -07:00
|
|
|
// Log the faulty node and ignore the invalid share.
|
|
|
|
let fault_kind = FaultKind::UnverifiedSignatureShareSender;
|
|
|
|
let fault_log = FaultLog::init(sender_id.clone(), fault_kind);
|
|
|
|
return Ok(fault_log);
|
2018-06-14 02:05:05 -07:00
|
|
|
}
|
|
|
|
self.received_shares.insert(sender_id.clone(), share);
|
2018-06-08 11:43:27 -07:00
|
|
|
} else {
|
2018-06-25 12:09:45 -07:00
|
|
|
return Err(ErrorKind::UnknownSender.into());
|
|
|
|
}
|
2018-07-08 09:41:50 -07:00
|
|
|
self.try_output()?;
|
|
|
|
Ok(FaultLog::new())
|
2018-06-25 12:09:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn try_output(&mut self) -> Result<()> {
|
|
|
|
let received_shares = &self.received_shares;
|
2018-07-10 04:23:50 -07:00
|
|
|
debug!(
|
|
|
|
"{:?} received {} shares, had_input = {}",
|
|
|
|
self.netinfo.our_uid(),
|
|
|
|
received_shares.len(),
|
|
|
|
self.had_input
|
|
|
|
);
|
2018-06-25 12:09:45 -07:00
|
|
|
if self.had_input && received_shares.len() > self.netinfo.num_faulty() {
|
|
|
|
let sig = self.combine_and_verify_sig()?;
|
|
|
|
// Output the parity of the verified signature.
|
|
|
|
let parity = sig.parity();
|
2018-07-10 04:23:50 -07:00
|
|
|
debug!("{:?} output {}", self.netinfo.our_uid(), parity);
|
2018-06-25 12:09:45 -07:00
|
|
|
self.output = Some(parity);
|
|
|
|
self.terminated = true;
|
2018-06-08 11:43:27 -07:00
|
|
|
}
|
2018-06-25 12:09:45 -07:00
|
|
|
Ok(())
|
2018-06-07 09:38:27 -07:00
|
|
|
}
|
2018-06-14 04:28:38 -07:00
|
|
|
|
2018-06-21 08:31:15 -07:00
|
|
|
fn combine_and_verify_sig(&self) -> Result<Signature> {
|
2018-06-14 04:28:38 -07:00
|
|
|
// Pass the indices of sender nodes to `combine_signatures`.
|
2018-06-21 08:31:15 -07:00
|
|
|
let ids_shares: BTreeMap<&NodeUid, &Signature> = self.received_shares.iter().collect();
|
2018-06-18 07:14:17 -07:00
|
|
|
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
|
2018-06-14 04:28:38 -07:00
|
|
|
.keys()
|
|
|
|
.map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64))
|
|
|
|
.collect();
|
|
|
|
// Convert indices to `u64` which is an interface type for `pairing`.
|
2018-06-21 08:31:15 -07:00
|
|
|
let shares: BTreeMap<&u64, &Signature> = ids_shares
|
2018-06-14 04:28:38 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(id, &share)| (&ids_u64[id], share))
|
|
|
|
.collect();
|
|
|
|
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
|
|
|
|
if !self
|
|
|
|
.netinfo
|
|
|
|
.public_key_set()
|
|
|
|
.public_key()
|
|
|
|
.verify(&sig, &self.nonce)
|
|
|
|
{
|
|
|
|
// Abort
|
|
|
|
error!(
|
|
|
|
"{:?} main public key verification failed",
|
|
|
|
self.netinfo.our_uid()
|
|
|
|
);
|
|
|
|
Err(ErrorKind::VerificationFailed.into())
|
|
|
|
} else {
|
|
|
|
Ok(sig)
|
|
|
|
}
|
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|