From 9a42391b7544d52f4b8661a7fb7d2a50df90ae98 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Tue, 3 May 2022 08:49:46 -0400 Subject: [PATCH] Cleanup which makes transcript optional, only required for multisig --- coins/monero/Cargo.toml | 7 +- coins/monero/src/clsag/mod.rs | 4 +- coins/monero/src/clsag/multisig.rs | 5 +- coins/monero/src/frost.rs | 5 +- coins/monero/src/lib.rs | 22 ++- coins/monero/src/transaction/mixins.rs | 2 +- coins/monero/src/transaction/mod.rs | 170 +++++++++-------------- coins/monero/src/transaction/multisig.rs | 66 ++++++--- coins/monero/tests/clsag.rs | 5 +- crypto/dalek-ff-group/src/lib.rs | 13 +- crypto/frost/src/lib.rs | 4 +- crypto/transcript/src/lib.rs | 25 +++- 12 files changed, 167 insertions(+), 161 deletions(-) diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index dd695541..390f34e1 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -17,12 +17,11 @@ blake2 = "0.10" curve25519-dalek = { version = "3.2", features = ["std", "simd_backend"] } -transcript = { path = "../../crypto/transcript" } - ff = { version = "0.11", optional = true } group = { version = "0.11", optional = true } -dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true } +transcript = { path = "../../crypto/transcript", optional = true } frost = { path = "../../crypto/frost", optional = true } +dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true } # Locked to this specific patch version due to a bug we compensate for monero = { version = "0.16.0", features = ["experimental"] } @@ -34,7 +33,7 @@ monero-epee-bin-serde = "1.0" reqwest = { version = "0.11", features = ["json"] } [features] -multisig = ["ff", "group", "dalek-ff-group", "frost"] +multisig = ["ff", "group", "transcript", "frost", "dalek-ff-group"] [dev-dependencies] rand = "0.8" diff --git a/coins/monero/src/clsag/mod.rs b/coins/monero/src/clsag/mod.rs index a19bd574..cf93378c 100644 --- a/coins/monero/src/clsag/mod.rs +++ b/coins/monero/src/clsag/mod.rs @@ -113,8 +113,8 @@ pub(crate) fn sign_core( let mut to_hash = vec![]; to_hash.reserve_exact(((2 * n) + 4) * 32); const PREFIX: &str = "CLSAG_"; - const AGG_0: &str = "CLSAG_agg_0"; - const ROUND: &str = "round"; + const AGG_0: &str = "CLSAG_agg_0"; + const ROUND: &str = "round"; to_hash.extend(AGG_0.bytes()); to_hash.extend([0; 32 - AGG_0.len()]); diff --git a/coins/monero/src/clsag/multisig.rs b/coins/monero/src/clsag/multisig.rs index d75a8bac..bda12ced 100644 --- a/coins/monero/src/clsag/multisig.rs +++ b/coins/monero/src/clsag/multisig.rs @@ -14,14 +14,13 @@ use monero::util::ringct::{Key, Clsag}; use group::Group; -use dalek_ff_group as dfg; use transcript::Transcript as TranscriptTrait; use frost::{Curve, FrostError, algorithm::Algorithm, MultisigView}; +use dalek_ff_group as dfg; use crate::{ - Transcript, hash_to_point, - frost::{MultisigError, Ed25519, DLEqProof}, + frost::{Transcript, MultisigError, Ed25519, DLEqProof}, key_image, clsag::{Input, sign_core, verify} }; diff --git a/coins/monero/src/frost.rs b/coins/monero/src/frost.rs index 413ac95a..addbfee2 100644 --- a/coins/monero/src/frost.rs +++ b/coins/monero/src/frost.rs @@ -16,11 +16,14 @@ use curve25519_dalek::{ use ff::PrimeField; use group::Group; -use dalek_ff_group as dfg; +use transcript::DigestTranscript; use frost::{CurveError, Curve}; +use dalek_ff_group as dfg; use crate::random_scalar; +pub(crate) type Transcript = DigestTranscript::; + #[derive(Error, Debug)] pub enum MultisigError { #[error("internal error ({0})")] diff --git a/coins/monero/src/lib.rs b/coins/monero/src/lib.rs index d7cefbd8..6fe8dafe 100644 --- a/coins/monero/src/lib.rs +++ b/coins/monero/src/lib.rs @@ -12,8 +12,6 @@ use curve25519_dalek::{ use monero::util::key::H; -use transcript::DigestTranscript; - #[cfg(feature = "multisig")] pub mod frost; @@ -39,19 +37,10 @@ extern "C" { ) -> bool; } -// Allows using a modern rand as dalek's is notoriously dated -pub fn random_scalar(rng: &mut R) -> Scalar { - let mut r = [0; 64]; - rng.fill_bytes(&mut r); - Scalar::from_bytes_mod_order_wide(&r) -} - lazy_static! { static ref H_TABLE: EdwardsBasepointTable = EdwardsBasepointTable::create(&H.point.decompress().unwrap()); } -pub(crate) type Transcript = DigestTranscript::; - #[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Commitment { @@ -73,6 +62,13 @@ impl Commitment { } } +// Allows using a modern rand as dalek's is notoriously dated +pub fn random_scalar(rng: &mut R) -> Scalar { + let mut r = [0; 64]; + rng.fill_bytes(&mut r); + Scalar::from_bytes_mod_order_wide(&r) +} + pub fn hash(data: &[u8]) -> [u8; 32] { let mut keccak = Keccak::v256(); keccak.update(data); @@ -87,8 +83,6 @@ pub fn hash_to_scalar(data: &[u8]) -> Scalar { pub fn hash_to_point(point: &EdwardsPoint) -> EdwardsPoint { let mut bytes = point.compress().to_bytes(); - unsafe { - c_hash_to_point(bytes.as_mut_ptr()); - } + unsafe { c_hash_to_point(bytes.as_mut_ptr()); } CompressedEdwardsY::from_slice(&bytes).decompress().unwrap() } diff --git a/coins/monero/src/transaction/mixins.rs b/coins/monero/src/transaction/mixins.rs index abdc76fe..ec7cec0d 100644 --- a/coins/monero/src/transaction/mixins.rs +++ b/coins/monero/src/transaction/mixins.rs @@ -1,4 +1,4 @@ -// TOOD +// TODO pub(crate) fn select(o: u64) -> (u8, Vec) { let mut mixins: Vec = (o .. o + 11).into_iter().collect(); mixins.sort(); diff --git a/coins/monero/src/transaction/mod.rs b/coins/monero/src/transaction/mod.rs index 298d37d2..3987b267 100644 --- a/coins/monero/src/transaction/mod.rs +++ b/coins/monero/src/transaction/mod.rs @@ -24,13 +24,10 @@ use monero::{ } }; -use transcript::Transcript as TranscriptTrait; - #[cfg(feature = "multisig")] use frost::FrostError; use crate::{ - Transcript, Commitment, random_scalar, hash, hash_to_scalar, @@ -46,8 +43,6 @@ mod multisig; #[derive(Error, Debug)] pub enum TransactionError { - #[error("invalid preparation ({0})")] - InvalidPreparation(String), #[error("no inputs")] NoInputs, #[error("no outputs")] @@ -196,17 +191,14 @@ impl Output { } } -enum Preparation<'a, R: RngCore + CryptoRng> { - Leader(&'a mut R), - Follower([u8; 32], Bulletproof) -} - async fn prepare_inputs( rpc: &Rpc, spend: &Scalar, inputs: &[SpendableOutput], tx: &mut Transaction ) -> Result, TransactionError> { + // TODO sort inputs + let mut signable = Vec::with_capacity(inputs.len()); for (i, input) in inputs.iter().enumerate() { // Select mixins @@ -238,7 +230,10 @@ pub struct SignableTransaction { inputs: Vec, payments: Vec<(Address, u64)>, change: Address, - fee_per_byte: u64 + fee_per_byte: u64, + + fee: u64, + outputs: Vec } impl SignableTransaction { @@ -260,25 +255,25 @@ impl SignableTransaction { inputs, payments, change, - fee_per_byte + fee_per_byte, + + fee: 0, + outputs: vec![] } ) } - // This could be refactored so prep, a multisig-required variable, is used only by multisig - // Not shimmed by the single signer API as well - // This would enable moving Transcript as a whole to the multisig feature - fn prepare_outputs<'a, R: RngCore + CryptoRng>( - &self, - prep: &mut Preparation<'a, R> - ) -> Result<(Vec, Scalar, Transaction), TransactionError> { - let fee = self.fee_per_byte * 2000; // TODO + fn prepare_outputs( + &mut self, + rng: &mut R + ) -> Result<(Vec, Scalar), TransactionError> { + self.fee = self.fee_per_byte * 2000; // TODO // TODO TX MAX SIZE // Make sure we have enough funds let in_amount = self.inputs.iter().map(|input| input.commitment.amount).sum(); - let out_amount = fee + self.payments.iter().map(|payment| payment.1).sum::(); + let out_amount = self.fee + self.payments.iter().map(|payment| payment.1).sum::(); if in_amount < out_amount { Err(TransactionError::NotEnoughFunds(in_amount, out_amount))?; } @@ -287,122 +282,83 @@ impl SignableTransaction { let mut payments = self.payments.clone(); payments.push((self.change, in_amount - out_amount)); - // Grab the prep - let mut entropy = [0; 32]; - let mut bp = None; - match prep { - Preparation::Leader(ref mut rng) => { - // The Leader generates the entropy for the one time keys and the bulletproof - // This prevents de-anonymization via recalculation of the randomness which is deterministic - rng.fill_bytes(&mut entropy); - }, - Preparation::Follower(e, b) => { - entropy = e.clone(); - bp = Some(b.clone()); - } - } + // TODO randomly sort outputs - let mut transcript = Transcript::new(b"StealthAddress"); - // This output can only be spent once. Therefore, it forces all one time keys used here to be - // unique, even if the leader reuses entropy. While another transaction could use a different - // input ordering to swap which 0 is, that input set can't contain this input without being a - // double spend - transcript.append_message(b"hash", &self.inputs[0].tx.0); - transcript.append_message(b"index", &u64::try_from(self.inputs[0].o).unwrap().to_le_bytes()); - let mut rng = transcript.seeded_rng(b"tx_keys", Some(entropy)); - - let mut outputs = Vec::with_capacity(payments.len()); + self.outputs.clear(); + self.outputs = Vec::with_capacity(payments.len()); let mut commitments = Vec::with_capacity(payments.len()); for o in 0 .. payments.len() { - outputs.push(Output::new(&mut rng, payments[o], o)?); - commitments.push(Commitment::new(outputs[o].mask, payments[o].1)); + self.outputs.push(Output::new(rng, payments[o], o)?); + commitments.push(Commitment::new(self.outputs[o].mask, payments[o].1)); } - if bp.is_none() { - // Generate the bulletproof if leader - bp = Some(bulletproofs::generate(&commitments)?); - } else { - // Verify the bulletproof if follower - if !bulletproofs::verify( - bp.as_ref().unwrap(), - &commitments.iter().map(|c| c.calculate()).collect::>() - ) { - Err(TransactionError::InvalidPreparation("invalid bulletproof".to_string()))?; - } - } + Ok((commitments, self.outputs.iter().map(|output| output.mask).sum())) + } + fn prepare_transaction( + &self, + commitments: &[Commitment], + bp: Bulletproof + ) -> Transaction { // Create the TX extra let mut extra = ExtraField(vec![ - SubField::TxPublicKey(PublicKey { point: outputs[0].R.compress() }) + SubField::TxPublicKey(PublicKey { point: self.outputs[0].R.compress() }) ]); extra.0.push(SubField::AdditionalPublickKey( - outputs[1 .. outputs.len()].iter().map(|output| PublicKey { point: output.R.compress() }).collect() + self.outputs[1 .. self.outputs.len()].iter().map(|output| PublicKey { point: output.R.compress() }).collect() )); // Format it for monero-rs - let mut mrs_outputs = Vec::with_capacity(outputs.len()); - let mut out_pk = Vec::with_capacity(outputs.len()); - let mut ecdh_info = Vec::with_capacity(outputs.len()); - for o in 0 .. outputs.len() { + let mut mrs_outputs = Vec::with_capacity(self.outputs.len()); + let mut out_pk = Vec::with_capacity(self.outputs.len()); + let mut ecdh_info = Vec::with_capacity(self.outputs.len()); + for o in 0 .. self.outputs.len() { mrs_outputs.push(TxOut { amount: VarInt(0), - target: TxOutTarget::ToKey { key: PublicKey { point: outputs[o].dest.compress() } } + target: TxOutTarget::ToKey { key: PublicKey { point: self.outputs[o].dest.compress() } } }); out_pk.push(CtKey { mask: Key { key: commitments[o].calculate().compress().to_bytes() } }); - ecdh_info.push(EcdhInfo::Bulletproof { amount: outputs[o].amount }); + ecdh_info.push(EcdhInfo::Bulletproof { amount: self.outputs[o].amount }); } - Ok(( - match prep { - // Encode the prep - Preparation::Leader(..) => { - let mut prep = entropy.to_vec(); - bp.as_ref().unwrap().consensus_encode(&mut prep).expect("Couldn't encode bulletproof"); - prep - }, - Preparation::Follower(..) => { - vec![] - } + Transaction { + prefix: TransactionPrefix { + version: VarInt(2), + unlock_time: VarInt(0), + inputs: vec![], + outputs: mrs_outputs, + extra }, - outputs.iter().map(|output| output.mask).sum(), - Transaction { - prefix: TransactionPrefix { - version: VarInt(2), - unlock_time: VarInt(0), - inputs: vec![], - outputs: mrs_outputs, - extra - }, - signatures: vec![], - rct_signatures: RctSig { - sig: Some(RctSigBase { - rct_type: RctType::Clsag, - txn_fee: VarInt(fee), - pseudo_outs: vec![], - ecdh_info, - out_pk - }), - p: Some(RctSigPrunable { - range_sigs: vec![], - bulletproofs: vec![bp.unwrap()], - MGs: vec![], - Clsags: vec![], - pseudo_outs: vec![] - }) - } + signatures: vec![], + rct_signatures: RctSig { + sig: Some(RctSigBase { + rct_type: RctType::Clsag, + txn_fee: VarInt(self.fee), + pseudo_outs: vec![], + ecdh_info, + out_pk + }), + p: Some(RctSigPrunable { + range_sigs: vec![], + bulletproofs: vec![bp], + MGs: vec![], + Clsags: vec![], + pseudo_outs: vec![] + }) } - )) + } } pub async fn sign( - &self, + &mut self, rng: &mut R, rpc: &Rpc, spend: &Scalar ) -> Result { - let (_, mask_sum, mut tx) = self.prepare_outputs(&mut Preparation::Leader(rng))?; + let (commitments, mask_sum) = self.prepare_outputs(rng)?; + let mut tx = self.prepare_transaction(&commitments, bulletproofs::generate(&commitments)?); let signable = prepare_inputs(rpc, spend, &self.inputs, &mut tx).await?; diff --git a/coins/monero/src/transaction/multisig.rs b/coins/monero/src/transaction/multisig.rs index d1350568..916b79da 100644 --- a/coins/monero/src/transaction/multisig.rs +++ b/coins/monero/src/transaction/multisig.rs @@ -6,7 +6,7 @@ use curve25519_dalek::{scalar::Scalar, edwards::{EdwardsPoint, CompressedEdwards use monero::{ Hash, VarInt, - consensus::deserialize, + consensus::{Encodable, deserialize}, util::ringct::Key, blockdata::transaction::{KeyImage, TxIn, Transaction} }; @@ -15,12 +15,10 @@ use transcript::Transcript as TranscriptTrait; use frost::{FrostError, MultisigKeys, MultisigParams, sign::{State, StateMachine, AlgorithmMachine}}; use crate::{ - Transcript, - frost::Ed25519, - key_image, - clsag, + frost::{Transcript, Ed25519}, + key_image, bulletproofs, clsag, rpc::Rpc, - transaction::{TransactionError, Preparation, SignableTransaction, mixins} + transaction::{TransactionError, SignableTransaction, mixins} }; pub struct TransactionMachine { @@ -36,7 +34,7 @@ pub struct TransactionMachine { impl SignableTransaction { pub async fn multisig( - self, + mut self, rng: &mut R, rpc: &Rpc, keys: Rc>, @@ -83,7 +81,7 @@ impl SignableTransaction { } // Verify these outputs by a dummy prep - self.prepare_outputs(&mut Preparation::Leader(rng))?; + self.prepare_outputs(rng)?; Ok(TransactionMachine { leader: keys.params().i() == included[0], @@ -98,6 +96,18 @@ impl SignableTransaction { } } +// Seeded RNG so multisig participants agree on one time keys to use, preventing burning attacks +fn outputs_rng(tx: &SignableTransaction, entropy: [u8; 32]) -> ::SeededRng { + let mut transcript = Transcript::new(b"StealthAddress"); + // This output can only be spent once. Therefore, it forces all one time keys used here to be + // unique, even if the entropy is reused. While another transaction could use a different input + // ordering to swap which 0 is, that input set can't contain this input without being a double + // spend + transcript.append_message(b"hash", &tx.inputs[0].tx.0); + transcript.append_message(b"index", &u64::try_from(tx.inputs[0].o).unwrap().to_le_bytes()); + transcript.seeded_rng(b"tx_keys", Some(entropy)) +} + impl StateMachine for TransactionMachine { type Signature = Transaction; @@ -116,11 +126,20 @@ impl StateMachine for TransactionMachine { } if self.leader { - let (prep, mask_sum, tx) = self.signable.prepare_outputs(&mut Preparation::Leader(rng)).unwrap(); - self.mask_sum.replace(mask_sum); - self.tx = Some(tx); + let mut entropy = [0; 32]; + rng.fill_bytes(&mut entropy); + serialized.extend(&entropy); - serialized.extend(&prep); + let mut rng = outputs_rng(&self.signable, entropy); + // Safe to unwrap thanks to the dummy prepare + let (commitments, mask_sum) = self.signable.prepare_outputs(&mut rng).unwrap(); + self.mask_sum.replace(mask_sum); + + let bp = bulletproofs::generate(&commitments).unwrap(); + bp.consensus_encode(&mut serialized).unwrap(); + + let tx = self.signable.prepare_transaction(&commitments, bp); + self.tx = Some(tx); } Ok(serialized) @@ -150,14 +169,21 @@ impl StateMachine for TransactionMachine { } let prep = prep.as_ref().unwrap(); - // Handle the prep with a seeded RNG type to make rustc happy - let (_, mask_sum, tx_inner) = self.signable.prepare_outputs::<::SeededRng>( - &mut Preparation::Follower( - prep[clsag_lens .. (clsag_lens + 32)].try_into().map_err(|_| FrostError::InvalidCommitment(l))?, - deserialize(&prep[(clsag_lens + 32) .. prep.len()]).map_err(|_| FrostError::InvalidCommitment(l))? - ) - ).map_err(|_| FrostError::InvalidShare(l))?; // Not invalid outputs due to doing a dummy prep as leader + let mut rng = outputs_rng( + &self.signable, + prep[clsag_lens .. (clsag_lens + 32)].try_into().map_err(|_| FrostError::InvalidShare(l))? + ); + // Not invalid outputs due to doing a dummy prep as leader + let (commitments, mask_sum) = self.signable.prepare_outputs(&mut rng).map_err(|_| FrostError::InvalidShare(l))?; self.mask_sum.replace(mask_sum); + + // Verify the provided bulletproofs if not leader + let bp = deserialize(&prep[(clsag_lens + 32) .. prep.len()]).map_err(|_| FrostError::InvalidShare(l))?; + if !bulletproofs::verify(&bp, &commitments.iter().map(|c| c.calculate()).collect::>()) { + Err(FrostError::InvalidShare(l))?; + } + + let tx_inner = self.signable.prepare_transaction(&commitments, bp); tx = Some(tx_inner); break; } @@ -188,6 +214,8 @@ impl StateMachine for TransactionMachine { }; } + // TODO sort inputs + let mut tx = tx.unwrap(); tx.prefix.inputs = self.inputs.clone(); self.msg.replace(tx.signature_hash().unwrap().0); diff --git a/coins/monero/tests/clsag.rs b/coins/monero/tests/clsag.rs index 1866da64..8028c44a 100644 --- a/coins/monero/tests/clsag.rs +++ b/coins/monero/tests/clsag.rs @@ -1,10 +1,13 @@ +#[cfg(feature = "multisig")] use std::{rc::Rc, cell::RefCell}; use rand::{RngCore, rngs::OsRng}; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar}; -use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, clsag}; +use monero_serai::{random_scalar, Commitment, key_image, clsag}; +#[cfg(feature = "multisig")] +use monero_serai::frost::MultisigError; #[cfg(feature = "multisig")] mod frost; diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index f1f0b3cf..f2d5588b 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -120,7 +120,9 @@ impl Field for Scalar { fn one() -> Self { Self(DScalar::one()) } fn square(&self) -> Self { *self * self } fn double(&self) -> Self { *self + self } - fn invert(&self) -> CtOption { CtOption::new(Self(self.0.invert()), Choice::from(1 as u8)) } + fn invert(&self) -> CtOption { + CtOption::new(Self(self.0.invert()), Choice::from(1 as u8)) + } fn sqrt(&self) -> CtOption { unimplemented!() } fn is_zero(&self) -> Choice { Choice::from(if self.0 == DScalar::zero() { 1 } else { 0 }) } fn cube(&self) -> Self { *self * self * self } @@ -137,7 +139,10 @@ impl PrimeField for Scalar { const CAPACITY: u32 = 252; fn from_repr(bytes: [u8; 32]) -> CtOption { let scalar = DScalar::from_canonical_bytes(bytes).map(|x| Scalar(x)); - CtOption::new(scalar.unwrap_or(Scalar::zero()), Choice::from(if scalar.is_some() { 1 } else { 0 })) + CtOption::new( + scalar.unwrap_or(Scalar::zero()), + Choice::from(if scalar.is_some() { 1 } else { 0 }) + ) } fn to_repr(&self) -> [u8; 32] { self.0.to_bytes() } @@ -285,7 +290,9 @@ impl EdwardsPoint { } pub struct EdwardsBasepointTable(pub DTable); -pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = EdwardsBasepointTable(constants::ED25519_BASEPOINT_TABLE); +pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = EdwardsBasepointTable( + constants::ED25519_BASEPOINT_TABLE +); impl Deref for EdwardsBasepointTable { type Target = DTable; diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index 917931cf..2818afa9 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -54,8 +54,8 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { fn generator_table() -> Self::T; /// Multiexponentation function, presumably Straus or Pippenger - /// This library does provide an implementation of Straus which should increase key generation - /// performance by around 4x, also named multiexp_vartime, with the same API. However, if a more + /// This library does forward an implementation of Straus which should increase key generation + /// performance by around 4x, also named multiexp_vartime, with a similar API. However, if a more /// performant implementation is available, that should be used instead // This could also be written as -> Option with None for not implemented fn multiexp_vartime(scalars: &[Self::F], points: &[Self::G]) -> Self::G; diff --git a/crypto/transcript/src/lib.rs b/crypto/transcript/src/lib.rs index a3e0bd31..a82cd58e 100644 --- a/crypto/transcript/src/lib.rs +++ b/crypto/transcript/src/lib.rs @@ -16,7 +16,11 @@ pub trait Transcript { fn new(label: &'static [u8]) -> Self; fn append_message(&mut self, label: &'static [u8], message: &[u8]); fn challenge(&mut self, label: &'static [u8], len: usize) -> Vec; - fn seeded_rng(&self, label: &'static [u8], additional_entropy: Option<[u8; 32]>) -> Self::SeededRng; + fn seeded_rng( + &self, + label: &'static [u8], + additional_entropy: Option<[u8; 32]> + ) -> Self::SeededRng; } #[derive(Clone, Debug)] @@ -40,15 +44,28 @@ impl Transcript for DigestTranscript { self.0.extend(label); let mut challenge = Vec::with_capacity(len); - challenge.extend(&D::new().chain_update(&self.0).chain_update(&0u64.to_le_bytes()).finalize()); + challenge.extend( + &D::new() + .chain_update(&self.0) + .chain_update(&0u64.to_le_bytes()).finalize() + ); for i in 0 .. (len / challenge.len()) { - challenge.extend(&D::new().chain_update(&self.0).chain_update(&u64::try_from(i).unwrap().to_le_bytes()).finalize()); + challenge.extend( + &D::new() + .chain_update(&self.0) + .chain_update(&u64::try_from(i).unwrap().to_le_bytes()) + .finalize() + ); } challenge.truncate(len); challenge } - fn seeded_rng(&self, label: &'static [u8], additional_entropy: Option<[u8; 32]>) -> Self::SeededRng { + fn seeded_rng( + &self, + label: &'static [u8], + additional_entropy: Option<[u8; 32]> + ) -> Self::SeededRng { let mut transcript = DigestTranscript::(self.0.clone(), PhantomData); if additional_entropy.is_some() { transcript.append_message(b"additional_entropy", &additional_entropy.unwrap());