diff --git a/coins/monero/tests/clsag.rs b/coins/monero/tests/clsag.rs index a4ba6022..eb6bcf9b 100644 --- a/coins/monero/tests/clsag.rs +++ b/coins/monero/tests/clsag.rs @@ -7,7 +7,7 @@ use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, c #[cfg(feature = "multisig")] mod frost; #[cfg(feature = "multisig")] -use crate::frost::{generate_keys, sign}; +use crate::frost::{THRESHOLD, generate_keys, sign}; const RING_INDEX: u8 = 3; const RING_LEN: u64 = 11; @@ -86,17 +86,21 @@ fn test_multisig() -> Result<(), MultisigError> { ring.push([&dest * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]); } - let mut algorithms = Vec::with_capacity(t); - for _ in 1 ..= t { - algorithms.push( - clsag::InputMultisig::new( - clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(), - Msg(msg) + let mut machines = Vec::with_capacity(t); + for i in 1 ..= t { + machines.push( + sign::AlgorithmMachine::new( + clsag::InputMultisig::new( + clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(), + Msg(msg) + ).unwrap(), + keys[i - 1].clone(), + &(1 ..= THRESHOLD).collect::>() ).unwrap() ); } - let mut signatures = sign(algorithms, keys); + let mut signatures = sign(&mut machines, keys); let signature = signatures.swap_remove(0); for s in 0 .. (t - 1) { // Verify the commitments and the non-decoy s scalar are identical to every other signature diff --git a/coins/monero/tests/frost.rs b/coins/monero/tests/frost.rs index d4c0e71c..7eb856e5 100644 --- a/coins/monero/tests/frost.rs +++ b/coins/monero/tests/frost.rs @@ -8,7 +8,7 @@ use rand::rngs::OsRng; use ff::Field; use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint}; -use frost::{ +pub use frost::{ FrostError, MultisigParams, MultisigKeys, key_gen, algorithm::Algorithm, sign::{self, lagrange} }; @@ -113,26 +113,16 @@ pub fn generate_keys() -> (Vec>>, Scalar) { } #[allow(dead_code)] // Currently has some false positive -pub fn sign>( - algorithms: Vec, +pub fn sign>( + machines: &mut Vec, keys: Vec>> ) -> Vec { - assert!(algorithms.len() >= THRESHOLD); - assert!(keys.len() >= algorithms.len()); + assert!(machines.len() >= THRESHOLD); + assert!(keys.len() >= machines.len()); - let mut machines = vec![]; let mut commitments = Vec::with_capacity(PARTICIPANTS + 1); commitments.resize(PARTICIPANTS + 1, None); for i in 1 ..= THRESHOLD { - machines.push( - sign::StateMachine::new( - sign::Params::new( - algorithms[i - 1].clone(), - keys[i - 1].clone(), - &(1 ..= THRESHOLD).collect::>() - ).unwrap() - ) - ); commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap()); } diff --git a/sign/frost/src/sign.rs b/sign/frost/src/sign.rs index 6dcf0732..40daf922 100644 --- a/sign/frost/src/sign.rs +++ b/sign/frost/src/sign.rs @@ -65,6 +65,7 @@ pub struct Params> { view: ParamsView, } +// Currently public to enable more complex operations as desired, yet solely used in testing impl> Params { pub fn new( algorithm: A, @@ -400,30 +401,70 @@ impl fmt::Display for State { } } -/// State machine which manages signing +pub trait StateMachine { + type Signature; + + /// Perform the preprocessing round required in order to sign + /// Returns a byte vector which must be transmitted to all parties selected for this signing + /// process, over an authenticated channel + fn preprocess( + &mut self, + rng: &mut R + ) -> Result, FrostError>; + + /// Sign a message + /// Takes in the participant's commitments, which are expected to be in a Vec where participant + /// index = Vec index. None is expected at index 0 to allow for this. None is also expected at + /// index i which is locally handled. Returns a byte vector representing a share of the signature + /// for every other participant to receive, over an authenticated channel + fn sign( + &mut self, + commitments: &[Option>], + msg: &[u8], + ) -> Result, FrostError>; + + /// Complete signing + /// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index = + /// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized + /// signature + fn complete(&mut self, shares: &[Option>]) -> Result; + + fn multisig_params(&self) -> MultisigParams; + + fn state(&self) -> State; +} + +/// State machine which manages signing for an arbitrary signature algorithm #[allow(non_snake_case)] -pub struct StateMachine> { +pub struct AlgorithmMachine> { params: Params, state: State, preprocess: Option>, sign: Option>, } -impl> StateMachine { +impl> AlgorithmMachine { /// Creates a new machine to generate a key for the specified curve in the specified multisig - pub fn new(params: Params) -> StateMachine { - StateMachine { - params, - state: State::Fresh, - preprocess: None, - sign: None, - } + pub fn new( + algorithm: A, + keys: Rc>, + included: &[usize], + ) -> Result, FrostError> { + Ok( + AlgorithmMachine { + params: Params::new(algorithm, keys, included)?, + state: State::Fresh, + preprocess: None, + sign: None, + } + ) } +} - /// Perform the preprocessing round required in order to sign - /// Returns a byte vector which must be transmitted to all parties selected for this signing - /// process, over an authenticated channel - pub fn preprocess( +impl> StateMachine for AlgorithmMachine { + type Signature = A::Signature; + + fn preprocess( &mut self, rng: &mut R ) -> Result, FrostError> { @@ -437,12 +478,7 @@ impl> StateMachine { Ok(serialized) } - /// Sign a message - /// Takes in the participant's commitments, which are expected to be in a Vec where participant - /// index = Vec index. None is expected at index 0 to allow for this. None is also expected at - /// index i which is locally handled. Returns a byte vector representing a share of the signature - /// for every other participant to receive, over an authenticated channel - pub fn sign( + fn sign( &mut self, commitments: &[Option>], msg: &[u8], @@ -463,11 +499,7 @@ impl> StateMachine { Ok(serialized) } - /// Complete signing - /// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index = - /// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized - /// signature - pub fn complete(&mut self, shares: &[Option>]) -> Result { + fn complete(&mut self, shares: &[Option>]) -> Result { if self.state != State::Signed { Err(FrostError::InvalidSignTransition(State::Signed, self.state))?; } @@ -482,11 +514,11 @@ impl> StateMachine { Ok(signature) } - pub fn multisig_params(&self) -> MultisigParams { + fn multisig_params(&self) -> MultisigParams { self.params.multisig_params().clone() } - pub fn state(&self) -> State { + fn state(&self) -> State { self.state } } diff --git a/sign/frost/tests/key_gen_and_sign.rs b/sign/frost/tests/key_gen_and_sign.rs index 382cc3b5..08f08bff 100644 --- a/sign/frost/tests/key_gen_and_sign.rs +++ b/sign/frost/tests/key_gen_and_sign.rs @@ -10,7 +10,7 @@ use frost::{ MultisigParams, MultisigKeys, key_gen, algorithm::{Algorithm, Schnorr, SchnorrSignature}, - sign + sign::{StateMachine, AlgorithmMachine} }; mod common; @@ -28,13 +28,11 @@ fn sign>>( commitments.resize(PARTICIPANTS + 1, None); for i in 1 ..= t { machines.push( - sign::StateMachine::new( - sign::Params::new( - algorithm.clone(), - keys[i - 1].clone(), - &(1 ..= t).collect::>() - ).unwrap() - ) + AlgorithmMachine::new( + algorithm.clone(), + keys[i - 1].clone(), + &(1 ..= t).collect::>() + ).unwrap() ); commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap()); }