Move FROST to HashMaps

Honestly, the borrowed keys are frustrating, and this probably reduces 
performance while no longer offering an order when iterating. That said, 
they enable full u16 indexing and should mildly improve the API.

Cleans the Proof of Knowledge handling present in key gen.
This commit is contained in:
Luke Parker
2022-05-24 21:41:14 -04:00
parent 5ff65bd268
commit d10c6e16dc
12 changed files with 585 additions and 747 deletions

View File

@@ -1,4 +1,5 @@
use core::{ops::Mul, fmt::Debug};
use std::collections::HashMap;
use thiserror::Error;
@@ -10,7 +11,6 @@ pub use multiexp::multiexp_vartime;
pub mod key_gen;
pub mod algorithm;
pub mod sign;
use sign::lagrange;
/// Set of errors for curve-related operations, namely encoding and decoding
#[derive(Error, Debug)]
@@ -118,27 +118,23 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MultisigParams {
/// Participants needed to sign on behalf of the group
t: usize,
t: u16,
/// Amount of participants
n: usize,
n: u16,
/// Index of the participant being acted for
i: usize,
i: u16,
}
impl MultisigParams {
pub fn new(
t: usize,
n: usize,
i: usize
t: u16,
n: u16,
i: u16
) -> Result<MultisigParams, FrostError> {
if (t == 0) || (n == 0) {
Err(FrostError::ZeroParameter(t, n))?;
}
if u16::try_from(n).is_err() {
Err(FrostError::TooManyParticipants(n, u16::MAX))?;
}
// When t == n, this shouldn't be used (MuSig2 and other variants of MuSig exist for a reason),
// but it's not invalid to do so
if t > n {
@@ -151,21 +147,21 @@ impl MultisigParams {
Ok(MultisigParams{ t, n, i })
}
pub fn t(&self) -> usize { self.t }
pub fn n(&self) -> usize { self.n }
pub fn i(&self) -> usize { self.i }
pub fn t(&self) -> u16 { self.t }
pub fn n(&self) -> u16 { self.n }
pub fn i(&self) -> u16 { self.i }
}
#[derive(Error, Debug)]
pub enum FrostError {
#[error("a parameter was 0 (required {0}, participants {1})")]
ZeroParameter(usize, usize),
ZeroParameter(u16, u16),
#[error("too many participants (max {1}, got {0})")]
TooManyParticipants(usize, u16),
#[error("invalid amount of required participants (max {1}, got {0})")]
InvalidRequiredQuantity(usize, usize),
InvalidRequiredQuantity(u16, u16),
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")]
InvalidParticipantIndex(usize, usize),
InvalidParticipantIndex(u16, u16),
#[error("invalid signing set ({0})")]
InvalidSigningSet(String),
@@ -173,16 +169,14 @@ pub enum FrostError {
InvalidParticipantQuantity(usize, usize),
#[error("duplicated participant index ({0})")]
DuplicatedIndex(usize),
#[error("participant 0 provided data despite not existing")]
NonEmptyParticipantZero,
#[error("invalid commitment quantity (participant {0}, expected {1}, got {2})")]
InvalidCommitmentQuantity(usize, usize, usize),
#[error("missing participant {0}")]
MissingParticipant(u16),
#[error("invalid commitment (participant {0})")]
InvalidCommitment(usize),
InvalidCommitment(u16),
#[error("invalid proof of knowledge (participant {0})")]
InvalidProofOfKnowledge(usize),
InvalidProofOfKnowledge(u16),
#[error("invalid share (participant {0})")]
InvalidShare(usize),
InvalidShare(u16),
#[error("invalid key generation state machine transition (expected {0}, was {1})")]
InvalidKeyGenTransition(key_gen::State, key_gen::State),
@@ -197,9 +191,9 @@ pub enum FrostError {
#[derive(Clone)]
pub struct MultisigView<C: Curve> {
group_key: C::G,
included: Vec<usize>,
included: Vec<u16>,
secret_share: C::F,
verification_shares: Vec<C::G>,
verification_shares: HashMap<u16, C::G>,
}
impl<C: Curve> MultisigView<C> {
@@ -207,7 +201,7 @@ impl<C: Curve> MultisigView<C> {
self.group_key
}
pub fn included(&self) -> Vec<usize> {
pub fn included(&self) -> Vec<u16> {
self.included.clone()
}
@@ -215,11 +209,33 @@ impl<C: Curve> MultisigView<C> {
self.secret_share
}
pub fn verification_share(&self, l: usize) -> C::G {
self.verification_shares[l]
pub fn verification_share(&self, l: u16) -> C::G {
self.verification_shares[&l]
}
}
/// Calculate the lagrange coefficient for a signing set
pub fn lagrange<F: PrimeField>(
i: u16,
included: &[u16],
) -> F {
let mut num = F::one();
let mut denom = F::one();
for l in included {
if i == *l {
continue;
}
let share = F::from(u64::try_from(*l).unwrap());
num *= share;
denom *= share - F::from(u64::try_from(i).unwrap());
}
// Safe as this will only be 0 if we're part of the above loop
// (which we have an if case to avoid)
num * denom.invert().unwrap()
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct MultisigKeys<C: Curve> {
/// Multisig Parameters
@@ -230,7 +246,7 @@ pub struct MultisigKeys<C: Curve> {
/// Group key
group_key: C::G,
/// Verification shares
verification_shares: Vec<C::G>,
verification_shares: HashMap<u16, C::G>,
/// Offset applied to these keys
offset: Option<C::F>,
@@ -258,12 +274,12 @@ impl<C: Curve> MultisigKeys<C> {
self.group_key
}
pub fn verification_shares(&self) -> Vec<C::G> {
pub fn verification_shares(&self) -> HashMap<u16, C::G> {
self.verification_shares.clone()
}
pub fn view(&self, included: &[usize]) -> Result<MultisigView<C>, FrostError> {
if (included.len() < self.params.t) || (self.params.n < included.len()) {
pub fn view(&self, included: &[u16]) -> Result<MultisigView<C>, FrostError> {
if (included.len() < self.params.t.into()) || (usize::from(self.params.n) < included.len()) {
Err(FrostError::InvalidSigningSet("invalid amount of participants included".to_string()))?;
}
@@ -274,16 +290,18 @@ impl<C: Curve> MultisigKeys<C> {
Ok(MultisigView {
group_key: self.group_key + (C::generator_table() * offset),
secret_share: secret_share + offset_share,
verification_shares: self.verification_shares.clone().iter().enumerate().map(
|(l, share)| (*share * lagrange::<C::F>(l, &included)) +
(C::generator_table() * offset_share)
verification_shares: self.verification_shares.iter().map(
|(l, share)| (
*l,
(*share * lagrange::<C::F>(*l, &included)) + (C::generator_table() * offset_share)
)
).collect(),
included: included.to_vec(),
})
}
pub fn serialized_len(n: usize) -> usize {
1 + usize::from(C::id_len()) + (3 * 8) + C::F_len() + C::G_len() + (n * C::G_len())
pub fn serialized_len(n: u16) -> usize {
1 + usize::from(C::id_len()) + (3 * 2) + C::F_len() + C::G_len() + (usize::from(n) * C::G_len())
}
pub fn serialize(&self) -> Vec<u8> {
@@ -292,13 +310,13 @@ impl<C: Curve> MultisigKeys<C> {
);
serialized.push(C::id_len());
serialized.extend(C::id().as_bytes());
serialized.extend(&(self.params.n as u64).to_le_bytes());
serialized.extend(&(self.params.t as u64).to_le_bytes());
serialized.extend(&(self.params.i as u64).to_le_bytes());
serialized.extend(&self.params.n.to_le_bytes());
serialized.extend(&self.params.t.to_le_bytes());
serialized.extend(&self.params.i.to_le_bytes());
serialized.extend(&C::F_to_bytes(&self.secret_share));
serialized.extend(&C::G_to_bytes(&self.group_key));
for i in 1 ..= self.params.n {
serialized.extend(&C::G_to_bytes(&self.verification_shares[i]));
for l in 1 ..= self.params.n.into() {
serialized.extend(&C::G_to_bytes(&self.verification_shares[&l]));
}
serialized
@@ -330,19 +348,16 @@ impl<C: Curve> MultisigKeys<C> {
Err(FrostError::InternalError("participant quantity wasn't included".to_string()))?;
}
let n = u64::from_le_bytes(serialized[cursor .. (cursor + 8)].try_into().unwrap()).try_into()
.map_err(|_| FrostError::InternalError("parameter doesn't fit into usize".to_string()))?;
cursor += 8;
let n = u16::from_le_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap());
cursor += 2;
if serialized.len() != MultisigKeys::<C>::serialized_len(n) {
Err(FrostError::InternalError("incorrect serialization length".to_string()))?;
}
let t = u64::from_le_bytes(serialized[cursor .. (cursor + 8)].try_into().unwrap()).try_into()
.map_err(|_| FrostError::InternalError("parameter doesn't fit into usize".to_string()))?;
cursor += 8;
let i = u64::from_le_bytes(serialized[cursor .. (cursor + 8)].try_into().unwrap()).try_into()
.map_err(|_| FrostError::InternalError("parameter doesn't fit into usize".to_string()))?;
cursor += 8;
let t = u16::from_le_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap());
cursor += 2;
let i = u16::from_le_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap());
cursor += 2;
let secret_share = C::F_from_slice(&serialized[cursor .. (cursor + C::F_len())])
.map_err(|_| FrostError::InternalError("invalid secret share".to_string()))?;
@@ -351,10 +366,10 @@ impl<C: Curve> MultisigKeys<C> {
.map_err(|_| FrostError::InternalError("invalid group key".to_string()))?;
cursor += C::G_len();
let mut verification_shares = vec![C::G::identity()];
verification_shares.reserve_exact(n + 1);
for _ in 0 .. n {
verification_shares.push(
let mut verification_shares = HashMap::new();
for l in 1 ..= n {
verification_shares.insert(
l,
C::G_from_slice(&serialized[cursor .. (cursor + C::G_len())])
.map_err(|_| FrostError::InternalError("invalid verification share".to_string()))?
);
@@ -373,3 +388,24 @@ impl<C: Curve> MultisigKeys<C> {
)
}
}
// Validate a map of serialized values to have the expected included participants
pub(crate) fn validate_map<T>(
map: &mut HashMap<u16, T>,
included: &[u16],
ours: (u16, T)
) -> Result<(), FrostError> {
map.insert(ours.0, ours.1);
if map.len() != included.len() {
Err(FrostError::InvalidParticipantQuantity(included.len(), map.len()))?;
}
for included in included {
if !map.contains_key(included) {
Err(FrostError::MissingParticipant(*included))?;
}
}
Ok(())
}