From c179ad1e18d5a0b093c66b41c4c9c9b8d99f1107 Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Thu, 19 Jul 2018 11:32:32 +0200 Subject: [PATCH] Extend sync_key_gen documentation. --- src/sync_key_gen.rs | 92 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index 6eb5f74..0a3a6ff 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -8,9 +8,46 @@ //! //! When the protocol completes, every node receives a secret key share suitable for threshold //! signatures and encryption. The secret master key is not known by anyone. The protocol succeeds -//! if up to `threshold` nodes are faulty. +//! if up to _t_ nodes are faulty, where _t_ is the `threshold` parameter. The number of nodes must +//! be at least _2 t + 1_. //! -//! ## Usage example +//! ## Usage +//! +//! Before beginning the threshold key generation process, each validator needs to generate a +//! regular (non-threshold) key pair and multicast its public key. `SyncKeyGen::new` returns the +//! instance itself and a `Propose` message, containing a contribution to the new threshold keys. +//! It needs to be sent to all nodes. `SyncKeyGen::handle_propose` in turn produces an `Accept` +//! message, which is also multicast. +//! +//! All nodes must handle the exact same set of `Propose` and `Accept` messages. In this sense the +//! algorithm is synchronous: If Alice's `Accept` was handled by Bob but not by Carol, Bob and +//! Carol could receive different public key sets, and secret key shares that don't match. One way +//! to ensure this is to commit the messages to a public ledger before handling them, e.g. by +//! feeding them to a preexisting instance of Honey Badger. The messages will then appear in the +//! same order for everyone. +//! +//! To complete the process, call `SyncKeyGen::generate`. It produces your secret key share and the +//! public key set. +//! +//! While not asynchronous, the algorithm is fault tolerant: It is not necessary to handle a +//! `Propose` and all `Accept` messages from every validator. A `Propose` is _complete_ if it +//! received at least _2 t + 1_ valid `Accept`s. Only complete `Propose`s are used for key +//! generation in the end, and as long as at least one complete `Propose` is from a correct node, +//! the new key set is secure. You can use `SyncKeyGen::is_ready` to check whether at least +//! _t + 1_ `Propose`s are complete. So all nodes can call `generate` as soon as `is_ready` returns +//! `true`. +//! +//! Alternatively, you can use any stronger criterion, too, as long as all validators call +//! `generate` at the same point, i.e. after handling the same set of messages. +//! `SyncKeyGen::count_complete` returns the number of complete `Propose` messages. And +//! `SyncKeyGen::is_node_ready` can be used to check whether a particluar node's `Propose` is +//! complete. +//! +//! Finally, observer nodes can also use `SyncKeyGen`. For observers, no `Propose` and `Accept` +//! messages will be created and they do not need to send anything. On completion, they will only +//! receive the public key set, but no secret key share. +//! +//! ## Example //! //! ``` //! extern crate rand; @@ -100,17 +137,17 @@ //! //! In a trusted dealer scenario, the following steps occur: //! -//! 1. Dealer generates a `BivarPoly` of degree `t` and publishes the `BivarCommitment` which is +//! 1. Dealer generates a `BivarPoly` of degree _t_ and publishes the `BivarCommitment` which is //! used to publicly verify the polynomial's values. -//! 2. Dealer sends _row_ `m > 0` to node number `m`. -//! 3. Node `m`, in turn, sends _value_ `s` to node number `s`. -//! 4. This process continues until `2 * t + 1` nodes confirm they have received a valid row. If -//! there are at most `t` faulty nodes, we know that at least `t + 1` correct nodes sent on an -//! entry of every other node’s column to that node. -//! 5. This means every node can reconstruct its column, and the value at `0` of its column. -//! 6. These values all lie on a univariate polynomial of degree `t` and can be used as secret keys. +//! 2. Dealer sends _row_ _m > 0_ to node number _m_. +//! 3. Node _m_, in turn, sends _value_ number _s_ to node number _s_. +//! 4. This process continues until _2 t + 1_ nodes confirm they have received a valid row. If +//! there are at most _t_ faulty nodes, we know that at least _t + 1_ correct nodes sent on an +//! entry of every other node's column to that node. +//! 5. This means every node can reconstruct its column, and the value at _0_ of its column. +//! 6. These values all lie on a univariate polynomial of degree _t_ and can be used as secret keys. //! -//! In our _dealerless_ environment, at least `t + 1` nodes each generate a polynomial using the +//! In our _dealerless_ environment, at least _t + 1_ nodes each generate a polynomial using the //! method above. The sum of the secret keys we received from each node is then used as our secret //! key. No single node knows the secret master key. @@ -130,7 +167,12 @@ use fault_log::{FaultKind, FaultLog}; // TODO: No need to send our own row and value to ourselves. -/// A commitment to a bivariate polynomial, and for each node, an encrypted row of values. +/// A submission by a validator for the key generation. It must to be sent to all participating +/// nodes and handled by all of them, including the one that produced it. +/// +/// The message contains a commitment to a bivariate polynomial, and for each node, an encrypted +/// row of values. If this message receives enough `Accept`s, it will be used as summand to produce +/// the the key set in the end. #[derive(Deserialize, Serialize, Clone, Hash, Eq, PartialEq)] pub struct Propose(BivarCommitment, Vec); @@ -142,8 +184,11 @@ impl Debug for Propose { } } -/// A confirmation that we have received a node's proposal and verified our row against the -/// commitment. For each node, it contains one encrypted value of our row. +/// A confirmation that we have received and verified a validator's proposal. It must be sent to +/// all participating nodes and handled by all of them, including ourselves. +/// +/// The message is only produced after we verified our row against the commitment in the `Propose`. +/// For each node, it contains one encrypted value of that row. #[derive(Deserialize, Serialize, Clone, Hash, Eq, PartialEq)] pub struct Accept(u64, Vec); @@ -179,13 +224,15 @@ impl ProposalState { } } -/// Returned from `SyncKeyGen.handle_propose()`. +/// The outcome of handling and verifying a `Propose` message. pub enum ProposeOutcome { - // If the Propose message passed to `handle_propose()` is valid, an - // Accept message is returned. + /// The message was valid: the part of it that was encrypted to us matched the public + /// commitment, so we can multicast an `Accept` message for it. Valid(Accept), // If the Propose message passed to `handle_propose()` is invalid, the // fault is logged and passed onto the caller. + /// The message was invalid: the part encrypted to us was malformed or didn't match the + /// commitment. We now know that the proposer is faulty, and dont' send an `Accept`. Invalid(FaultLog), } @@ -207,7 +254,10 @@ pub struct SyncKeyGen { impl SyncKeyGen { /// Creates a new `SyncKeyGen` instance, together with the `Propose` message that should be - /// broadcast, if we are a peer. + /// multicast to all nodes. + /// + /// If we are not a validator but only an observer, no `Propose` message is produced and no + /// messages need to be sent. pub fn new( our_uid: &NodeUid, sec_key: SecretKey, @@ -241,6 +291,8 @@ impl SyncKeyGen { } /// Handles a `Propose` message. If it is valid, returns an `Accept` message to be broadcast. + /// + /// If we are only an observer, `None` is returned instead and no messages need to be sent. pub fn handle_propose( &mut self, sender_id: &NodeUid, @@ -315,10 +367,12 @@ impl SyncKeyGen { self.count_complete() > self.threshold } - /// Returns the new secret key and the public key set. + /// Returns the new secret key share and the public key set. /// /// These are only secure if `is_ready` returned `true`. Otherwise it is not guaranteed that /// none of the nodes knows the secret master key. + /// + /// If we are only an observer node, no secret key share is returned. pub fn generate(&self) -> (PublicKeySet, Option) { let mut pk_commit = Poly::zero().commitment(); let mut opt_sk_val = self.our_idx.map(|_| Fr::zero());