mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
3.3.3 (cont) Add a dedicated Participant type
This commit is contained in:
@@ -6,7 +6,7 @@ use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use transcript::Transcript;
|
||||
|
||||
use crate::{Curve, FrostError, ThresholdKeys, ThresholdView};
|
||||
use crate::{Curve, Participant, FrostError, ThresholdKeys, ThresholdView};
|
||||
pub use schnorr::SchnorrSignature;
|
||||
|
||||
/// Write an addendum to a writer.
|
||||
@@ -55,7 +55,7 @@ pub trait Algorithm<C: Curve>: Clone {
|
||||
fn process_addendum(
|
||||
&mut self,
|
||||
params: &ThresholdView<C>,
|
||||
l: u16,
|
||||
l: Participant,
|
||||
reader: Self::Addendum,
|
||||
) -> Result<(), FrostError>;
|
||||
|
||||
@@ -161,7 +161,12 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_addendum(&mut self, _: &ThresholdView<C>, _: u16, _: ()) -> Result<(), FrostError> {
|
||||
fn process_addendum(
|
||||
&mut self,
|
||||
_: &ThresholdView<C>,
|
||||
_: Participant,
|
||||
_: (),
|
||||
) -> Result<(), FrostError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Distributed key generation protocol.
|
||||
pub use dkg::{self, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
|
||||
pub use dkg::{self, Participant, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
|
||||
|
||||
/// Curve trait and provided curves/HRAMs, forming various ciphersuites.
|
||||
pub mod curve;
|
||||
@@ -38,21 +38,21 @@ pub mod tests;
|
||||
/// Various errors possible during signing.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
|
||||
pub enum FrostError {
|
||||
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")]
|
||||
InvalidParticipantIndex(u16, u16),
|
||||
#[error("invalid participant (0 < participant <= {0}, yet participant is {1})")]
|
||||
InvalidParticipant(u16, Participant),
|
||||
#[error("invalid signing set ({0})")]
|
||||
InvalidSigningSet(&'static str),
|
||||
#[error("invalid participant quantity (expected {0}, got {1})")]
|
||||
InvalidParticipantQuantity(usize, usize),
|
||||
#[error("duplicated participant index ({0})")]
|
||||
DuplicatedIndex(u16),
|
||||
#[error("duplicated participant ({0})")]
|
||||
DuplicatedParticipant(Participant),
|
||||
#[error("missing participant {0}")]
|
||||
MissingParticipant(u16),
|
||||
MissingParticipant(Participant),
|
||||
|
||||
#[error("invalid preprocess (participant {0})")]
|
||||
InvalidPreprocess(u16),
|
||||
InvalidPreprocess(Participant),
|
||||
#[error("invalid share (participant {0})")]
|
||||
InvalidShare(u16),
|
||||
InvalidShare(Participant),
|
||||
|
||||
#[error("internal error ({0})")]
|
||||
InternalError(&'static str),
|
||||
@@ -60,9 +60,9 @@ pub enum FrostError {
|
||||
|
||||
// Validate a map of values to have the expected included participants
|
||||
pub fn validate_map<T>(
|
||||
map: &HashMap<u16, T>,
|
||||
included: &[u16],
|
||||
ours: u16,
|
||||
map: &HashMap<Participant, T>,
|
||||
included: &[Participant],
|
||||
ours: Participant,
|
||||
) -> Result<(), FrostError> {
|
||||
if (map.len() + 1) != included.len() {
|
||||
Err(FrostError::InvalidParticipantQuantity(included.len(), map.len() + 1))?;
|
||||
@@ -71,7 +71,7 @@ pub fn validate_map<T>(
|
||||
for included in included {
|
||||
if *included == ours {
|
||||
if map.contains_key(included) {
|
||||
Err(FrostError::DuplicatedIndex(*included))?;
|
||||
Err(FrostError::DuplicatedParticipant(*included))?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ use multiexp::multiexp_vartime;
|
||||
|
||||
use dleq::MultiDLEqProof;
|
||||
|
||||
use crate::curve::Curve;
|
||||
use crate::{curve::Curve, Participant};
|
||||
|
||||
// Transcript used to aggregate binomial nonces for usage within a single DLEq proof.
|
||||
fn aggregation_transcript<T: Transcript>(context: &[u8]) -> T {
|
||||
@@ -247,17 +247,17 @@ pub(crate) struct IndividualBinding<C: Curve> {
|
||||
binding_factors: Option<Vec<C::F>>,
|
||||
}
|
||||
|
||||
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<u16, IndividualBinding<C>>);
|
||||
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<Participant, IndividualBinding<C>>);
|
||||
|
||||
impl<C: Curve> BindingFactor<C> {
|
||||
pub(crate) fn insert(&mut self, i: u16, commitments: Commitments<C>) {
|
||||
pub(crate) fn insert(&mut self, i: Participant, commitments: Commitments<C>) {
|
||||
self.0.insert(i, IndividualBinding { commitments, binding_factors: None });
|
||||
}
|
||||
|
||||
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
|
||||
for (l, binding) in self.0.iter_mut() {
|
||||
let mut transcript = transcript.clone();
|
||||
transcript.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
|
||||
transcript.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
||||
// It *should* be perfectly fine to reuse a binding factor for multiple nonces
|
||||
// This generates a binding factor per nonce just to ensure it never comes up as a question
|
||||
binding.binding_factors = Some(
|
||||
@@ -268,12 +268,12 @@ impl<C: Curve> BindingFactor<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn binding_factors(&self, i: u16) -> &[C::F] {
|
||||
pub(crate) fn binding_factors(&self, i: Participant) -> &[C::F] {
|
||||
self.0[&i].binding_factors.as_ref().unwrap()
|
||||
}
|
||||
|
||||
// Get the bound nonces for a specific party
|
||||
pub(crate) fn bound(&self, l: u16) -> Vec<Vec<C::G>> {
|
||||
pub(crate) fn bound(&self, l: Participant) -> Vec<Vec<C::G>> {
|
||||
let mut res = vec![];
|
||||
for (i, (nonce, rho)) in
|
||||
self.0[&l].commitments.nonces.iter().zip(self.binding_factors(l).iter()).enumerate()
|
||||
|
||||
@@ -19,7 +19,7 @@ use multiexp::BatchVerifier;
|
||||
|
||||
use crate::{
|
||||
curve::Curve,
|
||||
FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
|
||||
Participant, FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
|
||||
algorithm::{WriteAddendum, Addendum, Algorithm},
|
||||
validate_map,
|
||||
};
|
||||
@@ -239,7 +239,7 @@ pub trait SignMachine<S>: Sized {
|
||||
/// become the signing set for this session.
|
||||
fn sign(
|
||||
self,
|
||||
commitments: HashMap<u16, Self::Preprocess>,
|
||||
commitments: HashMap<Participant, Self::Preprocess>,
|
||||
msg: &[u8],
|
||||
) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>;
|
||||
}
|
||||
@@ -291,7 +291,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
||||
|
||||
fn sign(
|
||||
mut self,
|
||||
mut preprocesses: HashMap<u16, Preprocess<C, A::Addendum>>,
|
||||
mut preprocesses: HashMap<Participant, Preprocess<C, A::Addendum>>,
|
||||
msg: &[u8],
|
||||
) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> {
|
||||
let multisig_params = self.params.multisig_params();
|
||||
@@ -307,18 +307,14 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
||||
if included.len() < usize::from(multisig_params.t()) {
|
||||
Err(FrostError::InvalidSigningSet("not enough signers"))?;
|
||||
}
|
||||
// Invalid index
|
||||
if included[0] == 0 {
|
||||
Err(FrostError::InvalidParticipantIndex(included[0], multisig_params.n()))?;
|
||||
}
|
||||
// OOB index
|
||||
if included[included.len() - 1] > multisig_params.n() {
|
||||
Err(FrostError::InvalidParticipantIndex(included[included.len() - 1], multisig_params.n()))?;
|
||||
if u16::from(included[included.len() - 1]) > multisig_params.n() {
|
||||
Err(FrostError::InvalidParticipant(multisig_params.n(), included[included.len() - 1]))?;
|
||||
}
|
||||
// Same signer included multiple times
|
||||
for i in 0 .. (included.len() - 1) {
|
||||
if included[i] == included[i + 1] {
|
||||
Err(FrostError::DuplicatedIndex(included[i]))?;
|
||||
Err(FrostError::DuplicatedParticipant(included[i]))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +328,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
||||
|
||||
let nonces = self.params.algorithm.nonces();
|
||||
#[allow(non_snake_case)]
|
||||
let mut B = BindingFactor(HashMap::<u16, _>::with_capacity(included.len()));
|
||||
let mut B = BindingFactor(HashMap::<Participant, _>::with_capacity(included.len()));
|
||||
{
|
||||
// Parse the preprocesses
|
||||
for l in &included {
|
||||
@@ -341,7 +337,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
||||
.params
|
||||
.algorithm
|
||||
.transcript()
|
||||
.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
|
||||
.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
||||
}
|
||||
|
||||
if *l == self.params.keys.params().i() {
|
||||
@@ -449,7 +445,7 @@ pub trait SignatureMachine<S> {
|
||||
|
||||
/// Complete signing.
|
||||
/// Takes in everyone elses' shares. Returns the signature.
|
||||
fn complete(self, shares: HashMap<u16, Self::SignatureShare>) -> Result<S, FrostError>;
|
||||
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
|
||||
}
|
||||
|
||||
/// Final step of the state machine for the signing process.
|
||||
@@ -472,7 +468,7 @@ impl<C: Curve, A: Algorithm<C>> SignatureMachine<A::Signature> for AlgorithmSign
|
||||
|
||||
fn complete(
|
||||
self,
|
||||
mut shares: HashMap<u16, SignatureShare<C>>,
|
||||
mut shares: HashMap<Participant, SignatureShare<C>>,
|
||||
) -> Result<A::Signature, FrostError> {
|
||||
let params = self.params.multisig_params();
|
||||
validate_map(&shares, self.view.included(), params.i())?;
|
||||
|
||||
@@ -5,7 +5,7 @@ use rand_core::{RngCore, CryptoRng};
|
||||
pub use dkg::tests::{key_gen, recover_key};
|
||||
|
||||
use crate::{
|
||||
Curve, ThresholdKeys,
|
||||
Curve, Participant, ThresholdKeys,
|
||||
algorithm::Algorithm,
|
||||
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
|
||||
};
|
||||
@@ -36,11 +36,14 @@ pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
|
||||
pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
|
||||
rng: &mut R,
|
||||
algorithm: A,
|
||||
keys: &HashMap<u16, ThresholdKeys<C>>,
|
||||
) -> HashMap<u16, AlgorithmMachine<C, A>> {
|
||||
keys: &HashMap<Participant, ThresholdKeys<C>>,
|
||||
) -> HashMap<Participant, AlgorithmMachine<C, A>> {
|
||||
let mut included = vec![];
|
||||
while included.len() < usize::from(keys[&1].params().t()) {
|
||||
let n = u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap();
|
||||
while included.len() < usize::from(keys[&Participant::new(1).unwrap()].params().t()) {
|
||||
let n = Participant::new(
|
||||
u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
if included.contains(&n) {
|
||||
continue;
|
||||
}
|
||||
@@ -64,15 +67,15 @@ pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
|
||||
pub(crate) fn commit_and_shares<
|
||||
R: RngCore + CryptoRng,
|
||||
M: PreprocessMachine,
|
||||
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
|
||||
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
|
||||
>(
|
||||
rng: &mut R,
|
||||
mut machines: HashMap<u16, M>,
|
||||
mut machines: HashMap<Participant, M>,
|
||||
mut cache: F,
|
||||
msg: &[u8],
|
||||
) -> (
|
||||
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
|
||||
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
|
||||
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
|
||||
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
|
||||
) {
|
||||
let mut commitments = HashMap::new();
|
||||
let mut machines = machines
|
||||
@@ -110,10 +113,10 @@ pub(crate) fn commit_and_shares<
|
||||
fn sign_internal<
|
||||
R: RngCore + CryptoRng,
|
||||
M: PreprocessMachine,
|
||||
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
|
||||
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
|
||||
>(
|
||||
rng: &mut R,
|
||||
machines: HashMap<u16, M>,
|
||||
machines: HashMap<Participant, M>,
|
||||
cache: F,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
@@ -135,7 +138,7 @@ fn sign_internal<
|
||||
/// caching.
|
||||
pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
||||
rng: &mut R,
|
||||
machines: HashMap<u16, M>,
|
||||
machines: HashMap<Participant, M>,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
sign_internal(rng, machines, |_, _| {}, msg)
|
||||
@@ -146,8 +149,8 @@ pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
||||
pub fn sign<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
||||
rng: &mut R,
|
||||
params: <M::SignMachine as SignMachine<M::Signature>>::Params,
|
||||
mut keys: HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
|
||||
machines: HashMap<u16, M>,
|
||||
mut keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
|
||||
machines: HashMap<Participant, M>,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
sign_internal(
|
||||
|
||||
@@ -13,7 +13,7 @@ use dkg::tests::key_gen;
|
||||
|
||||
use crate::{
|
||||
curve::Curve,
|
||||
ThresholdCore, ThresholdKeys, FrostError,
|
||||
Participant, ThresholdCore, ThresholdKeys, FrostError,
|
||||
algorithm::{Schnorr, Hram},
|
||||
sign::{
|
||||
Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine,
|
||||
@@ -30,7 +30,7 @@ pub struct Vectors {
|
||||
pub shares: Vec<String>,
|
||||
|
||||
pub msg: String,
|
||||
pub included: Vec<u16>,
|
||||
pub included: Vec<Participant>,
|
||||
pub nonces: Vec<[String; 2]>,
|
||||
|
||||
pub sig_shares: Vec<String>,
|
||||
@@ -58,8 +58,11 @@ impl From<serde_json::Value> for Vectors {
|
||||
included: to_str(&value["round_one_outputs"]["participant_list"])
|
||||
.split(',')
|
||||
.map(u16::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap(),
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|i| Participant::new(*i).unwrap())
|
||||
.collect(),
|
||||
nonces: value["round_one_outputs"]["participants"]
|
||||
.as_object()
|
||||
.unwrap()
|
||||
@@ -80,7 +83,7 @@ impl From<serde_json::Value> for Vectors {
|
||||
}
|
||||
|
||||
// Load these vectors into ThresholdKeys using a custom serialization it'll deserialize
|
||||
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, ThresholdKeys<C>> {
|
||||
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<Participant, ThresholdKeys<C>> {
|
||||
let shares = vectors
|
||||
.shares
|
||||
.iter()
|
||||
@@ -92,11 +95,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
|
||||
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
|
||||
// Manually re-implement the serialization for ThresholdCore to import this data
|
||||
let mut serialized = vec![];
|
||||
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_be_bytes());
|
||||
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_le_bytes());
|
||||
serialized.extend(C::ID);
|
||||
serialized.extend(vectors.threshold.to_be_bytes());
|
||||
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes());
|
||||
serialized.extend(i.to_be_bytes());
|
||||
serialized.extend(vectors.threshold.to_le_bytes());
|
||||
serialized.extend(u16::try_from(shares.len()).unwrap().to_le_bytes());
|
||||
serialized.extend(i.to_le_bytes());
|
||||
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
|
||||
for share in &verification_shares {
|
||||
serialized.extend(share.to_bytes().as_ref());
|
||||
@@ -105,10 +108,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
|
||||
let these_keys = ThresholdCore::<C>::deserialize::<&[u8]>(&mut serialized.as_ref()).unwrap();
|
||||
assert_eq!(these_keys.params().t(), vectors.threshold);
|
||||
assert_eq!(usize::from(these_keys.params().n()), shares.len());
|
||||
assert_eq!(these_keys.params().i(), i);
|
||||
let participant = Participant::new(i).unwrap();
|
||||
assert_eq!(these_keys.params().i(), participant);
|
||||
assert_eq!(these_keys.secret_share().deref(), &shares[usize::from(i - 1)]);
|
||||
assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
|
||||
keys.insert(i, ThresholdKeys::new(these_keys));
|
||||
keys.insert(participant, ThresholdKeys::new(these_keys));
|
||||
}
|
||||
|
||||
keys
|
||||
@@ -124,7 +128,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
||||
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
||||
const MSG: &[u8] = b"Hello, World!";
|
||||
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG);
|
||||
assert!(sig.verify(keys[&1].group_key(), H::hram(&sig.R, &keys[&1].group_key(), MSG)));
|
||||
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
||||
assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
|
||||
}
|
||||
|
||||
// Test blame on an invalid Schnorr signature share
|
||||
|
||||
Reference in New Issue
Block a user