use bls::{
error::Error as CryptoError,
group::{ff::Field, prime::PrimeCurveAffine},
poly::{BivarCommitment, BivarPoly, Poly},
serde_impl::FieldWrap,
Ciphertext, Fr, G1Affine, PublicKey, PublicKeySet, SecretKey, SecretKeyShare,
};
use serde::{Deserialize, Serialize};
use std::{
borrow::Borrow,
collections::{BTreeMap, BTreeSet},
fmt::{self, Debug, Formatter},
hash::Hash,
ops::{AddAssign, Mul},
};
use thiserror::Error;
pub trait NodeIdT: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
impl<N> NodeIdT for N where N: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
pub type PubKeyMap<N, PK = PublicKey> = BTreeMap<N, PK>;
pub fn to_pub_keys<'a, I, B, N: NodeIdT + 'a>(sec_keys: I) -> PubKeyMap<N>
where
B: Borrow<N>,
I: IntoIterator<Item = (B, &'a SecretKey)>,
{
let to_pub = |(id, sk): I::Item| (id.borrow().clone(), sk.public_key());
sec_keys.into_iter().map(to_pub).collect()
}
#[derive(Clone, PartialEq, Debug, Error)]
pub enum Error {
#[error("Error creating SyncKeyGen: {0}")]
Creation(CryptoError),
#[error("Error generating keys: {0}")]
Generation(CryptoError),
#[error("Unknown sender")]
UnknownSender,
#[error("Serialization error: {0}")]
Serialize(String),
#[error("Encryption error: {0}")]
Encrypt(String),
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Error {
Error::Serialize(format!("{err:?}"))
}
}
#[derive(Deserialize, Serialize, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct Part(BivarCommitment, Vec<Ciphertext>);
impl Debug for Part {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Part")
.field(&format!("<degree {}>", self.0.degree()))
.field(&format!("<{} rows>", self.1.len()))
.finish()
}
}
#[derive(Deserialize, Serialize, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct Ack(u64, Vec<Ciphertext>);
impl Debug for Ack {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Ack")
.field(&self.0)
.field(&format!("<{} values>", self.1.len()))
.finish()
}
}
#[derive(Debug, PartialEq, Eq)]
struct ProposalState {
commit: BivarCommitment,
values: BTreeMap<u64, Fr>,
acks: BTreeSet<u64>,
}
impl ProposalState {
fn new(commit: BivarCommitment) -> ProposalState {
ProposalState {
commit,
values: BTreeMap::new(),
acks: BTreeSet::new(),
}
}
fn is_complete(&self, all_nodes_len: usize) -> bool {
self.acks.len() == all_nodes_len
}
}
pub enum PartOutcome {
Valid(Option<Ack>),
Invalid(PartFault),
}
pub enum AckOutcome {
Valid,
Invalid(AckFault),
}
#[derive(Debug)]
pub struct SyncKeyGen<N> {
our_id: N,
our_idx: Option<u64>,
sec_key: SecretKey,
pub_keys: PubKeyMap<N, PublicKey>,
parts: BTreeMap<u64, ProposalState>,
threshold: usize,
}
impl<N: NodeIdT> SyncKeyGen<N> {
pub fn new<R: bls::rand::RngCore>(
our_id: N,
sec_key: SecretKey,
pub_keys: PubKeyMap<N, PublicKey>,
threshold: usize,
rng: &mut R,
) -> Result<(Self, Option<Part>), Error> {
let our_idx = pub_keys
.keys()
.position(|id| *id == our_id)
.map(|idx| idx as u64);
let key_gen = SyncKeyGen {
our_id,
our_idx,
sec_key,
pub_keys,
parts: BTreeMap::new(),
threshold,
};
if our_idx.is_none() {
return Ok((key_gen, None)); }
let our_part = BivarPoly::random(threshold, rng);
let commit = our_part.commitment();
let encrypt = |(i, pk): (usize, &PublicKey)| {
let row = bincode::serialize(&our_part.row(i + 1))?;
Ok(pk.encrypt_with_rng(rng, &row))
};
let rows = key_gen
.pub_keys
.values()
.enumerate()
.map(encrypt)
.collect::<Result<Vec<Ciphertext>, Error>>()?;
Ok((key_gen, Some(Part(commit, rows))))
}
pub fn our_id(&self) -> &N {
&self.our_id
}
pub fn public_keys(&self) -> &PubKeyMap<N> {
&self.pub_keys
}
pub fn handle_part<R: bls::rand::RngCore>(
&mut self,
sender_id: &N,
part: Part,
rng: &mut R,
) -> Result<PartOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
let row = match self.handle_part_or_fault(sender_idx, part) {
Ok(Some(row)) => row,
Ok(None) => return Ok(PartOutcome::Valid(None)),
Err(fault) => return Ok(PartOutcome::Invalid(fault)),
};
let mut values = Vec::new();
for (idx, pk) in self.pub_keys.values().enumerate() {
let val = row.evaluate(idx + 1);
let ser_val = bincode::serialize(&FieldWrap(val))?;
values.push(pk.encrypt_with_rng(rng, ser_val));
}
Ok(PartOutcome::Valid(Some(Ack(sender_idx, values))))
}
pub fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> Result<AckOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
Ok(match self.handle_ack_or_fault(sender_idx, ack) {
Ok(()) => AckOutcome::Valid,
Err(fault) => AckOutcome::Invalid(fault),
})
}
fn node_index(&self, node_id: &N) -> Option<u64> {
self.pub_keys
.keys()
.position(|id| id == node_id)
.map(|idx| idx as u64)
}
pub fn count_complete(&self) -> usize {
self.parts
.values()
.filter(|part| part.is_complete(self.pub_keys.len()))
.count()
}
pub fn is_node_ready(&self, proposer_id: &N) -> bool {
self.node_index(proposer_id)
.and_then(|proposer_idx| self.parts.get(&proposer_idx))
.map_or(false, |part| part.is_complete(self.pub_keys.len()))
}
pub fn is_ready(&self) -> bool {
self.count_complete() == self.pub_keys.len()
}
pub fn generate(&self) -> Result<(PublicKeySet, Option<SecretKeyShare>), Error> {
let mut pk_commit = Poly::zero().commitment();
let mut opt_sk_val = self.our_idx.map(|_| Fr::zero());
let is_complete = |part: &&ProposalState| part.is_complete(self.pub_keys.len());
for part in self.parts.values().filter(is_complete) {
pk_commit += part.commit.row(0);
if let Some(sk_val) = opt_sk_val.as_mut() {
let row = Poly::interpolate(part.values.iter().take(self.threshold + 1))
.map_err(Error::Generation)?;
sk_val.add_assign(&row.evaluate(0));
}
}
let opt_sk = if let Some(mut fr) = opt_sk_val {
let sk = SecretKeyShare::from_mut(&mut fr);
Some(sk)
} else {
None
};
Ok((pk_commit.into(), opt_sk))
}
pub fn num_nodes(&self) -> usize {
self.pub_keys.len()
}
fn handle_part_or_fault(
&mut self,
sender_idx: u64,
Part(commit, rows): Part,
) -> Result<Option<Poly>, PartFault> {
if rows.len() != self.pub_keys.len() {
return Err(PartFault::RowCount);
}
if let Some(state) = self.parts.get(&sender_idx) {
if state.commit != commit {
return Err(PartFault::MultipleParts);
}
return Ok(None); }
let opt_idx_commit_row = self.our_idx.map(|idx| (idx, commit.row(idx + 1)));
self.parts.insert(sender_idx, ProposalState::new(commit));
let (our_idx, commit_row) = match opt_idx_commit_row {
Some((idx, row)) => (idx, row),
None => return Ok(None), };
let ser_row = self
.sec_key
.decrypt(&rows[our_idx as usize])
.ok_or(PartFault::DecryptRow)?;
let row: Poly = bincode::deserialize(&ser_row).map_err(|_| PartFault::DeserializeRow)?;
if row.commitment() != commit_row {
return Err(PartFault::RowCommitment);
}
Ok(Some(row))
}
fn handle_ack_or_fault(
&mut self,
sender_idx: u64,
Ack(proposer_idx, values): Ack,
) -> Result<(), AckFault> {
if values.len() != self.pub_keys.len() {
return Err(AckFault::ValueCount);
}
let part = self
.parts
.get_mut(&proposer_idx)
.ok_or(AckFault::MissingPart)?;
if !part.acks.insert(sender_idx) {
return Ok(()); }
let our_idx = match self.our_idx {
Some(our_idx) => our_idx,
None => return Ok(()), };
let ser_val = self
.sec_key
.decrypt(&values[our_idx as usize])
.ok_or(AckFault::DecryptValue)?;
let val = bincode::deserialize::<FieldWrap<Fr>>(&ser_val)
.map_err(|_| AckFault::DeserializeValue)?
.into_inner();
if part.commit.evaluate(our_idx + 1, sender_idx + 1)
!= G1Affine::generator().mul(val).into()
{
return Err(AckFault::ValueCommitment);
}
part.values.insert(sender_idx + 1, val);
Ok(())
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
pub enum AckFault {
#[error("The number of values differs from the number of nodes")]
ValueCount,
#[error("No corresponding Part received")]
MissingPart,
#[error("Value decryption failed")]
DecryptValue,
#[error("Value deserialization failed")]
DeserializeValue,
#[error("Value doesn't match the commitment")]
ValueCommitment,
}
#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
pub enum PartFault {
#[error("The number of rows differs from the number of nodes")]
RowCount,
#[error("Received multiple different Part messages from the same sender")]
MultipleParts,
#[error("Could not decrypt our row in the Part message")]
DecryptRow,
#[error("Could not deserialize our row in the Part message")]
DeserializeRow,
#[error("Row does not match the commitment")]
RowCommitment,
}
#[cfg(test)]
pub(crate) mod tests {
use super::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
use bls::{PublicKey, PublicKeySet, SecretKey, SecretKeyShare, SignatureShare};
use eyre::{eyre, Result};
use std::collections::{BTreeMap, BTreeSet};
#[test]
fn test_sdkg() {
let mut rng = bls::rand::rngs::OsRng;
let (threshold, node_num) = (1, 4);
let sec_keys: Vec<SecretKey> = (0..node_num).map(|_| bls::rand::random()).collect();
let pub_keys: BTreeMap<usize, PublicKey> = sec_keys
.iter()
.map(SecretKey::public_key)
.enumerate()
.collect();
let mut nodes = BTreeMap::new();
let mut parts = Vec::new();
for (id, sk) in sec_keys.into_iter().enumerate() {
let (sync_key_gen, opt_part) =
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, &mut rng).unwrap_or_else(
|_| panic!("Failed to create `SyncKeyGen` instance for node #{id}"),
);
nodes.insert(id, sync_key_gen);
parts.push((id, opt_part.unwrap())); }
let mut acks = Vec::new();
for (sender_id, part) in parts {
for (&id, node) in &mut nodes {
match node
.handle_part(&sender_id, part.clone(), &mut rng)
.expect("Failed to handle Part")
{
PartOutcome::Valid(Some(ack)) => acks.push((id, ack)),
PartOutcome::Invalid(fault) => panic!("Invalid Part: {fault:?}"),
PartOutcome::Valid(None) => {
panic!("We are not an observer, so we should send Ack.")
}
}
}
}
for (sender_id, ack) in acks {
for node in nodes.values_mut() {
match node
.handle_ack(&sender_id, ack.clone())
.expect("Failed to handle Ack")
{
AckOutcome::Valid => (),
AckOutcome::Invalid(fault) => panic!("Invalid Ack: {fault:?}"),
}
}
}
let pub_key_set = nodes[&0]
.generate()
.expect("Failed to create `PublicKeySet` from node #0")
.0;
let mut secret_key_shares = BTreeMap::new();
for (&id, node) in &mut nodes {
assert!(node.is_ready());
let (pks, opt_sks) = node.generate().unwrap_or_else(|_| {
panic!("Failed to create `PublicKeySet` and `SecretKeyShare` for node #{id}")
});
assert_eq!(pks, pub_key_set); let sks = opt_sks.expect("Not an observer node: We receive a secret key share.");
secret_key_shares.insert(id, sks);
}
let msg = "Nodes 0 and 1 does not agree with this.";
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (&id, sks) in &secret_key_shares {
if id != 0 && id != 1 {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(id, sig_share);
}
}
let sig = pub_key_set
.combine_signatures(&sig_shares)
.expect("The shares can be combined.");
assert!(pub_key_set.public_key().verify(&sig, msg));
}
#[test]
fn test_threshold() -> Result<()> {
for nodes_num in 2..10 {
for threshold in 1..nodes_num {
println!("Testing for threshold {threshold}/{nodes_num}...");
let (secret_key_shares, pub_key_set) = simulate_dkg_round(nodes_num, threshold)?;
let msg = "signed message";
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold + 1] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let sig = pub_key_set
.combine_signatures(&sig_shares)
.map_err(|err| eyre!("The shares can be combined: {err:?}"))?;
assert!(pub_key_set.public_key().verify(&sig, msg));
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let _sig = pub_key_set.combine_signatures(&sig_shares).is_err();
}
}
Ok(())
}
#[allow(clippy::type_complexity)]
fn init_nodes<R: bls::rand::RngCore>(
num_nodes: usize,
threshold: usize,
rng: &mut R,
) -> Result<(BTreeMap<usize, SyncKeyGen<usize>>, Vec<(usize, Part)>)> {
let sec_keys: Vec<SecretKey> = (0..num_nodes).map(|_| bls::rand::random()).collect();
let pub_keys: BTreeMap<usize, PublicKey> = sec_keys
.iter()
.map(SecretKey::public_key)
.enumerate()
.collect();
let mut nodes = BTreeMap::new();
let mut parts = Vec::new();
for (id, sk) in sec_keys.into_iter().enumerate() {
let (sync_key_gen, opt_part) =
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, rng)?;
nodes.insert(id, sync_key_gen);
parts.push((id, opt_part.unwrap())); }
Ok((nodes, parts))
}
fn handle_parts<R: bls::rand::RngCore>(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
parts: &Vec<(usize, Part)>,
rng: &mut R,
) -> Result<Vec<(usize, Ack)>> {
let mut acks = Vec::new();
for (sender_id, part) in parts {
for (&id, node) in nodes.iter_mut() {
match node.handle_part(sender_id, part.clone(), rng)? {
PartOutcome::Valid(Some(ack)) => acks.push((id, ack)),
_ => return Err(eyre!("We are an observer/invalid part")),
}
}
}
Ok(acks)
}
fn handle_acks(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
acks: &Vec<(usize, Ack)>,
) -> Result<()> {
for (sender_id, ack) in acks {
for node in nodes.values_mut() {
match node
.handle_ack(sender_id, ack.clone())
.map_err(|err| eyre!("Failed to handle Ack {err:?}"))?
{
AckOutcome::Valid => (),
AckOutcome::Invalid(fault) => return Err(eyre!("Invalid Ack {fault:?}")),
}
}
}
Ok(())
}
fn gen_key_share(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
) -> Result<(Vec<(usize, SecretKeyShare)>, PublicKeySet)> {
let mut pk_set = BTreeSet::new();
let mut secret_key_shares = Vec::new();
for (&id, node) in nodes {
if !node.is_ready() {
return Err(eyre!("Node: {id} is not ready"));
}
let (pks, opt_sks) = node.generate()?;
let sks = opt_sks.ok_or_else(|| eyre!("Node: {id} is an observer"))?;
pk_set.insert(pks);
secret_key_shares.push((id, sks));
}
if pk_set.len() != 1 {
return Err(eyre!("The pub_key_set is not the same for all the nodes"));
}
let pk_set = Vec::from_iter(pk_set.into_iter());
Ok((secret_key_shares, pk_set[0].clone()))
}
fn simulate_dkg_round(
num_nodes: usize,
threshold: usize,
) -> Result<(Vec<(usize, SecretKeyShare)>, PublicKeySet)> {
let mut rng = bls::rand::rngs::OsRng;
let (mut nodes, parts) = init_nodes(num_nodes, threshold, &mut rng)?;
let acks = handle_parts(&mut nodes, &parts, &mut rng)?;
handle_acks(&mut nodes, &acks)?;
gen_key_share(&mut nodes)
}
pub(crate) fn verify_threshold(
threshold: usize,
sk_shares: &[(usize, SecretKeyShare)],
pk_set: &PublicKeySet,
) -> Result<()> {
let msg = "verify threshold";
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in sk_shares.iter().take(threshold + 1) {
let sig_share = sks.sign(msg);
let pks = pk_set.public_key_share(id);
if !pks.verify(&sig_share, msg) {
return Err(eyre!("The pub_key_share cannot verify the sig"));
}
sig_shares.insert(*id, sig_share);
}
let sig = pk_set.combine_signatures(&sig_shares)?;
if !pk_set.public_key().verify(&sig, msg) {
return Err(eyre!("The pub_key_set cannot verify the sig"));
}
Ok(())
}
}