From 19410d138d04a1f7c2a72518b9c6686ff1748cda Mon Sep 17 00:00:00 2001 From: LLFourn Date: Thu, 1 Aug 2024 11:07:24 +1000 Subject: [PATCH 1/5] [fun] Clean up hash traits (Hash32) Stop using Digest since it's meant to be used as a convenience thing and that was getting in the way. We now have Hash32 as a simple way of saying "sha256" --- ecdsa_fun/src/lib.rs | 9 ++---- schnorr_fun/src/adaptor/mod.rs | 13 +++------ schnorr_fun/src/binonce.rs | 2 +- schnorr_fun/src/frost/mod.rs | 30 +++++++++---------- schnorr_fun/src/message.rs | 13 ++++++--- schnorr_fun/src/musig.rs | 22 +++++++------- schnorr_fun/src/schnorr.rs | 12 ++++---- secp256kfun/src/hash.rs | 53 ++++++++++++++++++++++------------ secp256kfun/src/macros.rs | 4 +-- secp256kfun/src/nonce.rs | 11 ++++--- secp256kfun/src/point.rs | 4 +-- secp256kfun/src/scalar.rs | 10 +++---- secp256kfun/src/slice.rs | 2 +- 13 files changed, 95 insertions(+), 90 deletions(-) diff --git a/ecdsa_fun/src/lib.rs b/ecdsa_fun/src/lib.rs index 2129ae1c..d8df6a97 100755 --- a/ecdsa_fun/src/lib.rs +++ b/ecdsa_fun/src/lib.rs @@ -132,12 +132,7 @@ impl ECDSA { /// /// ``` /// use ecdsa_fun::{ - /// fun::{ - /// digest::{Digest, Update}, - /// g, - /// marker::*, - /// Scalar, G, - /// }, + /// fun::{digest::Digest, prelude::*}, /// nonce, ECDSA, /// }; /// use rand::rngs::ThreadRng; @@ -149,7 +144,7 @@ impl ECDSA { /// let message_hash = { /// let message = b"Attack at dawn"; /// let mut message_hash = [0u8; 32]; - /// let hash = Sha256::default().chain(message); + /// let hash = Sha256::default().chain_update(message); /// message_hash.copy_from_slice(hash.finalize().as_ref()); /// message_hash /// }; diff --git a/schnorr_fun/src/adaptor/mod.rs b/schnorr_fun/src/adaptor/mod.rs index a188e478..a4504cb5 100644 --- a/schnorr_fun/src/adaptor/mod.rs +++ b/schnorr_fun/src/adaptor/mod.rs @@ -48,17 +48,12 @@ //! } //! ``` use crate::{ - fun::{ - derive_nonce, - digest::{generic_array::typenum::U32, Digest}, - g, - marker::*, - nonce, s, KeyPair, Point, Scalar, G, - }, + fun::{derive_nonce, nonce, prelude::*, KeyPair}, Message, Schnorr, Signature, }; mod encrypted_signature; pub use encrypted_signature::EncryptedSignature; +use secp256kfun::hash::Hash32; /// Extension trait for [`Schnorr`] to add the encrypted signing algorithm. /// @@ -79,7 +74,7 @@ pub trait EncryptedSign { impl EncryptedSign for Schnorr where - CH: Digest + Clone, + CH: Hash32, NG: nonce::NonceGen, { fn encrypted_sign( @@ -190,7 +185,7 @@ pub trait Adaptor { impl Adaptor for Schnorr where - CH: Digest + Clone, + CH: Hash32, { fn encryption_key_for(&self, decryption_key: &Scalar) -> Point { g!(decryption_key * G).normalize() diff --git a/schnorr_fun/src/binonce.rs b/schnorr_fun/src/binonce.rs index 1d7d27ce..3d4e4318 100644 --- a/schnorr_fun/src/binonce.rs +++ b/schnorr_fun/src/binonce.rs @@ -55,7 +55,7 @@ impl Nonce { } impl HashInto for Nonce { - fn hash_into(self, hash: &mut impl secp256kfun::digest::Digest) { + fn hash_into(self, hash: &mut impl secp256kfun::digest::Update) { self.0.hash_into(hash) } } diff --git a/schnorr_fun/src/frost/mod.rs b/schnorr_fun/src/frost/mod.rs index 7beaae46..89ebe211 100644 --- a/schnorr_fun/src/frost/mod.rs +++ b/schnorr_fun/src/frost/mod.rs @@ -203,10 +203,8 @@ use alloc::{ }; use core::num::NonZeroU32; use secp256kfun::{ - derive_nonce_rng, - digest::{generic_array::typenum::U32, Digest}, - g, - hash::{HashAdd, Tag}, + derive_nonce_rng, g, + hash::{Hash32, HashAdd, Tag}, marker::*, nonce::{self, NonceGen}, poly, @@ -245,7 +243,7 @@ pub struct Frost { impl Default for Frost where - H: Default + Tag + Digest, + H: Hash32, NG: Default + Tag + Clone, { fn default() -> Self { @@ -384,8 +382,8 @@ impl core::fmt::Display for FinishKeyGenError { #[cfg(feature = "std")] impl std::error::Error for FinishKeyGenError {} -impl + Clone, NG: NonceGen> Frost { - /// Convenience method to generate secret shares and proof-of-possession to be shared with other +impl Frost { + /// Convienence method to generate secret shares and proof-of-possession to be shared with other /// participants. Each secret share needs to be securely communicated to the intended /// participant but the proof of possession (schnorr signature) can be publically shared with /// everyone. @@ -535,19 +533,19 @@ impl + Clone, NG: NonceGen> Frost { } } -impl + Clone, NG> Frost { - /// Generate an id for the key generation by hashing the party indices and their point +impl Frost { + /// Generate an id for the key generation by hashing the party indicies and their point /// polynomials pub fn keygen_id(&self, keygen: &KeyGen) -> [u8; 32] { let mut keygen_hash = self.keygen_id_hash.clone(); - keygen_hash.update((keygen.point_polys.len() as u32).to_be_bytes()); + keygen_hash.update((keygen.point_polys.len() as u32).to_be_bytes().as_ref()); for (index, poly) in &keygen.point_polys { - keygen_hash.update(index.to_bytes()); + keygen_hash.update(index.to_bytes().as_ref()); for point in poly { - keygen_hash.update(point.to_bytes()); + keygen_hash.update(point.to_bytes().as_ref()); } } - keygen_hash.finalize().into() + keygen_hash.finalize_fixed().into() } /// Collect all the public polynomials commitments into a [`KeyGen`] to produce a [`SharedKey`]. @@ -803,7 +801,7 @@ impl + Clone, NG> Frost { /// ``` pub fn new_with_deterministic_nonces() -> Frost> where - H: Tag + Digest + Default + Clone, + H: Hash32, { Frost::default() } @@ -821,7 +819,7 @@ where /// ``` pub fn new_with_synthetic_nonces() -> Frost>> where - H: Tag + Digest + Default + Clone, + H: Hash32, R: RngCore + Default + Clone, { Frost::default() @@ -832,7 +830,7 @@ where /// You can still sign with this instance but you you will have to generate nonces in your own way. pub fn new_without_nonce_generation() -> Frost where - H: Tag + Digest + Default + Clone, + H: Hash32, { Frost::default() } diff --git a/schnorr_fun/src/message.rs b/schnorr_fun/src/message.rs index 6eb580a0..375739fd 100644 --- a/schnorr_fun/src/message.rs +++ b/schnorr_fun/src/message.rs @@ -1,4 +1,9 @@ -use secp256kfun::{digest::Digest, hash::HashInto, marker::*, Slice}; +use secp256kfun::{ + digest::{self}, + hash::HashInto, + marker::*, + Slice, +}; /// A message to be signed. /// @@ -57,11 +62,11 @@ impl<'a, S: Secrecy> Message<'a, S> { } impl HashInto for Message<'_, S> { - fn hash_into(self, hash: &mut impl Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { if let Some(prefix) = self.app_tag { let mut padded_prefix = [0u8; 64]; padded_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes()); - hash.update(padded_prefix); + hash.update(&padded_prefix); } hash.update(<&[u8]>::from(self.bytes)); } @@ -70,7 +75,7 @@ impl HashInto for Message<'_, S> { #[cfg(test)] mod test { use super::*; - use sha2::Sha256; + use sha2::{Digest, Sha256}; #[test] fn message_hash_into() { diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 78142478..5791a37c 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -74,13 +74,11 @@ use crate::{adaptor::EncryptedSignature, binonce, Message, Schnorr, Signature}; use alloc::vec::Vec; use secp256kfun::{ - digest::{generic_array::typenum::U32, Digest}, - g, - hash::{HashAdd, Tag}, - marker::*, + hash::{Hash32, HashAdd, Tag}, nonce::{self, NoNonces, NonceGen}, + prelude::*, rand_core::{RngCore, SeedableRng}, - s, KeyPair, Point, Scalar, G, + KeyPair, }; /// The MuSig context. @@ -265,7 +263,7 @@ impl AggKey { } } -impl + Clone, NG> MuSig { +impl MuSig { /// Generates a new aggregated key from a list of individual keys. /// /// Each party can be local (you know the secret key) or remote (you only know the public key). @@ -290,7 +288,7 @@ impl + Clone, NG> MuSig { /// ``` pub fn new_agg_key(&self, keys: Vec) -> AggKey { let coeff_hash = { - let L = self.pk_hash.clone().add(&keys[..]).finalize(); + let L = self.pk_hash.clone().add(&keys[..]).finalize_fixed(); self.coeff_hash.clone().add(L.as_slice()) }; @@ -326,7 +324,7 @@ impl + Clone, NG> MuSig { impl MuSig where - H: Digest + Clone, + H: Hash32, NG: NonceGen, { /// Seed a random number generator to be used for MuSig nonces. @@ -437,7 +435,7 @@ pub struct SignSession { signing_type: T, } -impl + Clone, NG> MuSig { +impl MuSig { /// Start a signing session. /// /// You must provide the public nonces for this signing session in the correct order. @@ -674,7 +672,7 @@ impl + Clone, NG> MuSig { /// ``` pub fn new_with_deterministic_nonces() -> MuSig> where - H: Tag + Digest + Default + Clone, + H: Hash32, { MuSig::default() } @@ -692,7 +690,7 @@ where /// ``` pub fn new_with_synthetic_nonces() -> MuSig>> where - H: Tag + Digest + Default + Clone, + H: Hash32, R: RngCore + Default + Clone, { MuSig::default() @@ -703,7 +701,7 @@ where /// You can still sign with this instance but you you will have to generate nonces in your own way. pub fn new_without_nonce_generation() -> MuSig where - H: Tag + Digest + Default, + H: Hash32, { MuSig::default() } diff --git a/schnorr_fun/src/schnorr.rs b/schnorr_fun/src/schnorr.rs index c42e5b8d..c1f44590 100644 --- a/schnorr_fun/src/schnorr.rs +++ b/schnorr_fun/src/schnorr.rs @@ -1,10 +1,8 @@ -use secp256kfun::nonce::NoNonces; +use secp256kfun::{hash::Hash32, nonce::NoNonces}; use crate::{ fun::{ - derive_nonce, - digest::{generic_array::typenum::U32, Digest}, - g, + derive_nonce, g, hash::{HashAdd, Tag}, marker::*, nonce::NonceGen, @@ -32,7 +30,7 @@ pub struct Schnorr { challenge_hash: CH, } -impl + Tag + Default> Schnorr { +impl Schnorr { /// Create a new instance that can only verify signatures. /// /// # Example @@ -110,7 +108,7 @@ where impl Schnorr where - CH: Digest + Clone, + CH: Hash32, NG: NonceGen, { /// Sign a message using a secret key and a particular nonce derivation scheme. @@ -148,7 +146,7 @@ where } } -impl + Clone> Schnorr { +impl Schnorr { /// Returns the challenge hash being used to sign/verify signatures pub fn challenge_hash(&self) -> CH { self.challenge_hash.clone() diff --git a/secp256kfun/src/hash.rs b/secp256kfun/src/hash.rs index 305ce2a8..47538c78 100644 --- a/secp256kfun/src/hash.rs +++ b/secp256kfun/src/hash.rs @@ -1,13 +1,13 @@ //! Generally useful utilities related to hashing. //! -//! In general, things in here are defined against the [`Digest`] trait from the [`RustCrypto`] project. +//! In general, things in here are defined against the [`digest`] crate from the [`RustCrypto`] project. //! -//! [`Digest`]: digest::Digest //! [`RustCrypto`]: https://github.com/RustCrypto/hashes +use digest::FixedOutput; + use crate::digest::{ crypto_common::BlockSizeUser, - generic_array::typenum::{PartialDiv, Unsigned}, - Digest, + generic_array::typenum::{PartialDiv, Unsigned, U32}, }; /// Extension trait for some cryptotraphic function that can be domain separated by a tag. /// Used for hashes and [nonce generators][`NonceGen`]. @@ -52,7 +52,7 @@ pub trait Tag: Sized { fn tag_vectored<'a>(self, tag_components: impl Iterator + Clone) -> Self; } -impl Tag for H +impl Tag for H where ::BlockSize: PartialDiv, <::BlockSize as PartialDiv>::Output: Unsigned, @@ -63,7 +63,7 @@ where for component in tag_components { hash.update(component); } - hash.finalize() + hash.finalize_fixed() }; let fill_block = <>::Output as Unsigned>::to_usize(); @@ -82,13 +82,15 @@ where /// # Example /// /// ``` -/// use digest::Digest; -/// use secp256kfun::hash::{HashAdd, HashInto}; +/// use secp256kfun::{ +/// digest::{self, Digest}, +/// hash::{HashAdd, HashInto}, +/// }; /// #[derive(Clone, Copy)] /// struct CryptoData([u8; 42]); /// /// impl HashInto for CryptoData { -/// fn hash_into(self, hash: &mut impl digest::Digest) { +/// fn hash_into(self, hash: &mut impl digest::Update) { /// hash.update(&self.0[..]) /// } /// } @@ -98,17 +100,17 @@ where /// ``` pub trait HashInto { /// Asks the item to convert itself to bytes and add itself to `hash`. - fn hash_into(self, hash: &mut impl digest::Digest); + fn hash_into(self, hash: &mut impl digest::Update); } impl HashInto for u8 { - fn hash_into(self, hash: &mut impl digest::Digest) { - hash.update([self]) + fn hash_into(self, hash: &mut impl digest::Update) { + hash.update(&[self]) } } impl<'a, T: HashInto + Clone> HashInto for &'a T { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { self.clone().hash_into(hash) } } @@ -117,7 +119,7 @@ impl<'a, T> HashInto for &'a [T] where &'a T: HashInto, { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { for item in self { item.hash_into(hash) } @@ -125,28 +127,43 @@ where } impl HashInto for &str { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { hash.update(self.as_bytes()) } } impl HashInto for [T; N] { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { for item in self { item.hash_into(hash) } } } -/// Extension trait for [`digest::Digest`] to make adding things to the hash convenient. +#[cfg(feature = "alloc")] +impl HashInto for alloc::collections::BTreeMap { + fn hash_into(self, hash: &mut impl digest::Update) { + for (key, value) in self { + key.hash_into(hash); + value.hash_into(hash); + } + } +} + +/// Extension trait for [`digest::Update`] to make adding things to the hash convenient. pub trait HashAdd { /// Converts something that implements [`HashInto`] to bytes and then incorporate the result into the digest (`self`). fn add(self, data: HI) -> Self; } -impl HashAdd for D { +impl HashAdd for D { fn add(mut self, data: HI) -> Self { data.hash_into(&mut self); self } } + +/// A convienient basket of trait impls +pub trait Hash32: digest::FixedOutput + Clone + Default + Tag {} + +impl Hash32 for H where H: digest::FixedOutput + Clone + Default + Tag {} diff --git a/secp256kfun/src/macros.rs b/secp256kfun/src/macros.rs index 13797b44..8458411f 100644 --- a/secp256kfun/src/macros.rs +++ b/secp256kfun/src/macros.rs @@ -179,10 +179,10 @@ macro_rules! derive_nonce_rng { use core::borrow::Borrow; use $crate::nonce::NonceGen; use $crate::rand_core::SeedableRng; - use $crate::digest::Digest; + use $crate::digest::FixedOutput; let hash = $nonce_gen.begin_derivation($secret.borrow())$(.add($public))+; - <$rng>::from_seed(hash.finalize().into()) + <$rng>::from_seed(hash.finalize_fixed().into()) }} } diff --git a/secp256kfun/src/nonce.rs b/secp256kfun/src/nonce.rs index c51ac9da..52c0954e 100644 --- a/secp256kfun/src/nonce.rs +++ b/secp256kfun/src/nonce.rs @@ -19,7 +19,6 @@ //! [`derive_nonce!`]: crate::derive_nonce! use crate::{hash::*, marker::*, Scalar}; use core::marker::PhantomData; -use digest::{generic_array::typenum::U32, Digest}; use rand_core::RngCore; /// A helper trait over RNGs that handle internal mutablility. @@ -167,7 +166,7 @@ pub struct Deterministic { /// [`derive_nonce`]: crate::derive_nonce pub trait NonceGen { /// The type of hash that `begin_derivation` will return. - type Hash: Digest; + type Hash: Hash32; /// Takes a secret [`Scalar`] and outputs a hash. Before turining this hash into the nonce, you /// must add a secret input and all the public inputs from the scheme into the hash. So for a @@ -175,7 +174,7 @@ pub trait NonceGen { fn begin_derivation(&self, secret: &Scalar) -> Self::Hash; } -impl + Clone> NonceGen for Deterministic { +impl NonceGen for Deterministic { type Hash = H; fn begin_derivation(&self, secret: &Scalar) -> Self::Hash { self.nonce_hash.clone().add(secret) @@ -204,7 +203,7 @@ impl Tag for NoNonces { impl NonceGen for Synthetic where - H: Tag + Digest + Clone, + H: Hash32, R: NonceRng, { type Hash = H; @@ -213,9 +212,9 @@ where let mut aux_bytes = [0u8; 32]; self.rng.fill_bytes(&mut aux_bytes[..]); let mut aux_hash = self.aux_hash.clone(); - aux_hash.update(aux_bytes); + digest::Update::update(&mut aux_hash, &aux_bytes); let mut bytes = [0u8; 32]; - bytes.copy_from_slice(aux_hash.finalize().as_ref()); + bytes.copy_from_slice(aux_hash.finalize_fixed().as_ref()); // bitwise xor the hashed randomness with secret for (i, byte) in bytes.iter_mut().enumerate() { diff --git a/secp256kfun/src/point.rs b/secp256kfun/src/point.rs index 71491ebc..28728831 100644 --- a/secp256kfun/src/point.rs +++ b/secp256kfun/src/point.rs @@ -498,13 +498,13 @@ impl Point { } impl HashInto for Point { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { hash.update(self.to_bytes().as_ref()) } } impl HashInto for Point { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { hash.update(self.to_xonly_bytes().as_ref()) } } diff --git a/secp256kfun/src/scalar.rs b/secp256kfun/src/scalar.rs index 693e9b46..620b5c28 100644 --- a/secp256kfun/src/scalar.rs +++ b/secp256kfun/src/scalar.rs @@ -4,7 +4,7 @@ use core::{ marker::PhantomData, ops::{AddAssign, MulAssign, SubAssign}, }; -use digest::{generic_array::typenum::U32, Digest}; +use digest::{self, generic_array::typenum::U32}; use rand_core::RngCore; /// A secp256k1 scalar (an integer mod the curve order) @@ -255,9 +255,9 @@ impl Scalar { /// # secp256kfun::hex::decode_array("8131e6f4b45754f2c90bd06688ceeabc0c45055460729928b4eecf11026a9e2d").unwrap() /// # ); /// ``` - pub fn from_hash(hash: impl Digest) -> Self { + pub fn from_hash(hash: impl digest::FixedOutput) -> Self { let mut bytes = [0u8; 32]; - bytes.copy_from_slice(hash.finalize().as_slice()); + bytes.copy_from_slice(hash.finalize_fixed().as_slice()); Scalar::from_bytes_mod_order(bytes) .non_zero() .expect("computationally unreachable") @@ -383,8 +383,8 @@ impl core::ops::Neg for &Scalar { } impl HashInto for Scalar { - fn hash_into(self, hash: &mut impl digest::Digest) { - hash.update(self.to_bytes()) + fn hash_into(self, hash: &mut impl digest::Update) { + hash.update(&self.to_bytes()) } } diff --git a/secp256kfun/src/slice.rs b/secp256kfun/src/slice.rs index 00b9f06e..e98c75e7 100644 --- a/secp256kfun/src/slice.rs +++ b/secp256kfun/src/slice.rs @@ -76,7 +76,7 @@ impl<'a, S> From<&'a [u8]> for Slice<'a, S> { } impl<'a, S> HashInto for Slice<'a, S> { - fn hash_into(self, hash: &mut impl digest::Digest) { + fn hash_into(self, hash: &mut impl digest::Update) { hash.update(self.inner) } } From b9b2d0b5187c1e4b7caab2ffd2bc8b6a014c5c20 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Fri, 9 Aug 2024 10:55:33 +1000 Subject: [PATCH 2/5] =?UTF-8?q?[=E2=9D=84]=20Give=20PairedSecretShare=20a?= =?UTF-8?q?=20default=20for=20T?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schnorr_fun/src/frost/share.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/schnorr_fun/src/frost/share.rs b/schnorr_fun/src/frost/share.rs index b1c0f374..ccb22ab4 100644 --- a/schnorr_fun/src/frost/share.rs +++ b/schnorr_fun/src/frost/share.rs @@ -105,7 +105,7 @@ secp256kfun::impl_display_debug_serialize! { } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug)] #[cfg_attr( feature = "bincode", derive(crate::fun::bincode::Encode, crate::fun::bincode::Decode), @@ -131,11 +131,17 @@ secp256kfun::impl_display_debug_serialize! { /// /// This is useful so you can keep track of tweaks to the secret value and tweaks to the shared key /// in tandem. -pub struct PairedSecretShare { +pub struct PairedSecretShare { secret_share: SecretShare, public_key: Point, } +impl PartialEq for PairedSecretShare { + fn eq(&self, other: &Self) -> bool { + self.secret_share == other.secret_share && self.public_key == other.public_key + } +} + impl PairedSecretShare { /// The index of the secret share pub fn index(&self) -> PartyIndex { From 0dae3e20a251bb816b44c1194fc34c7a6f2b908a Mon Sep 17 00:00:00 2001 From: LLFourn Date: Fri, 9 Aug 2024 10:56:04 +1000 Subject: [PATCH 3/5] =?UTF-8?q?[=E2=9D=84]=20Make=20ShareKey=20PartialEq?= =?UTF-8?q?=20more=20general?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schnorr_fun/src/frost/shared_key.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/schnorr_fun/src/frost/shared_key.rs b/schnorr_fun/src/frost/shared_key.rs index c16f368d..7cbbbbd2 100644 --- a/schnorr_fun/src/frost/shared_key.rs +++ b/schnorr_fun/src/frost/shared_key.rs @@ -1,14 +1,13 @@ -use core::{marker::PhantomData, ops::Deref}; - use super::{PairedSecretShare, PartyIndex, SecretShare, VerificationShare}; use alloc::vec::Vec; +use core::{marker::PhantomData, ops::Deref}; use secp256kfun::{poly, prelude::*}; /// A polynomial where the first coefficient (constant term) is the image of a secret `Scalar` that /// has been shared in a [Shamir's secret sharing] structure. /// /// [Shamir's secret sharing]: https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Eq)] #[cfg_attr( feature = "serde", derive(crate::fun::serde::Serialize), @@ -244,6 +243,12 @@ impl SharedKey { } } +impl PartialEq> for SharedKey { + fn eq(&self, other: &SharedKey) -> bool { + other.point_polynomial == self.point_polynomial + } +} + #[cfg(feature = "bincode")] impl crate::fun::bincode::Decode for SharedKey { fn decode( From b0303c1798e7fd2c66e35d684d0afb7cc6eea467 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Fri, 9 Aug 2024 10:56:33 +1000 Subject: [PATCH 4/5] Disambiguate KeyPair::::new to ::new_xonly --- secp256kfun/src/keypair.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/secp256kfun/src/keypair.rs b/secp256kfun/src/keypair.rs index d7af6b88..efd57f31 100644 --- a/secp256kfun/src/keypair.rs +++ b/secp256kfun/src/keypair.rs @@ -4,10 +4,10 @@ use crate::{g, marker::*, Point, Scalar, G}; /// ## Synopsis /// /// ``` -/// use secp256kfun::{marker::*, KeyPair, Scalar}; +/// use secp256kfun::{prelude::*, KeyPair}; /// let my_secret_key = Scalar::random(&mut rand::thread_rng()); -/// let my_keypair = KeyPair::::new(my_secret_key.clone()); -/// let my_xonly_keypair = KeyPair::::new(my_secret_key); +/// let my_keypair: KeyPair = KeyPair::new(my_secret_key.clone()); +/// let my_xonly_keypair: KeyPair = KeyPair::new_xonly(my_secret_key); /// /// if my_keypair.public_key().is_y_even() { /// assert_eq!(my_keypair, my_xonly_keypair); @@ -32,7 +32,7 @@ use crate::{g, marker::*, Point, Scalar, G}; /// [`Point`]: crate::Point /// [`Normal`]: crate::marker::Normal /// [`EvenY`]: crate::marker::EvenY -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct KeyPair { sk: Scalar, pk: Point, @@ -71,7 +71,7 @@ impl KeyPair { /// use secp256kfun::{g, marker::*, s, KeyPair, Scalar, G}; /// /// let original_secret_key = Scalar::random(&mut rand::thread_rng()); - /// let keypair = KeyPair::::new(original_secret_key.clone()); + /// let keypair = KeyPair::new_xonly(original_secret_key.clone()); /// /// assert!( /// &original_secret_key == keypair.secret_key() @@ -83,7 +83,7 @@ impl KeyPair { /// /// [`Point`]: crate::Point /// [`EvenY`]: crate::marker::EvenY - pub fn new(mut secret_key: Scalar) -> Self { + pub fn new_xonly(mut secret_key: Scalar) -> Self { let pk = Point::even_y_from_scalar_mul(G, &mut secret_key); Self { sk: secret_key, pk } } @@ -108,7 +108,7 @@ impl KeyPair { /// # Example /// ``` /// use secp256kfun::{KeyPair, Scalar, marker::*}; - /// let keypair = KeyPair::::new(Scalar::random(&mut rand::thread_rng())); + /// let keypair = KeyPair::new(Scalar::random(&mut rand::thread_rng())); /// let (secret_key, public_key) = keypair.as_tuple(); pub fn as_tuple(&self) -> (&Scalar, Point) where @@ -154,6 +154,6 @@ crate::impl_fromstr_deserialize! { name => "secp256k1 scalar", fn from_bytes(bytes: [u8;32]) -> Option> { let sk = Scalar::from_bytes(bytes)?.non_zero()?; - Some(KeyPair::::new(sk)) + Some(KeyPair::new_xonly(sk)) } } From 9dcb91c5cc77523a513e1177bb7442d9af4e9ef8 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Fri, 9 Aug 2024 10:58:41 +1000 Subject: [PATCH 5/5] [schnorr] Make easy to use constructors --- .../src/adaptor/encrypted_signature.rs | 2 +- schnorr_fun/src/adaptor/mod.rs | 2 +- schnorr_fun/src/lib.rs | 8 --- schnorr_fun/src/schnorr.rs | 56 ++++++++++++++----- schnorr_fun/src/signature.rs | 2 +- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/schnorr_fun/src/adaptor/encrypted_signature.rs b/schnorr_fun/src/adaptor/encrypted_signature.rs index 89b6c0dc..46e03b6d 100644 --- a/schnorr_fun/src/adaptor/encrypted_signature.rs +++ b/schnorr_fun/src/adaptor/encrypted_signature.rs @@ -48,7 +48,7 @@ mod test { fn encrypted_signature_serialization_roundtrip() { use super::*; use crate::{adaptor::*, fun::Scalar, Message}; - let schnorr = crate::test_instance!(); + let schnorr = crate::new_with_deterministic_nonces::(); let kp = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); let encryption_key = Point::random(&mut rand::thread_rng()); let encrypted_signature = schnorr.encrypted_sign( diff --git a/schnorr_fun/src/adaptor/mod.rs b/schnorr_fun/src/adaptor/mod.rs index a4504cb5..560ac281 100644 --- a/schnorr_fun/src/adaptor/mod.rs +++ b/schnorr_fun/src/adaptor/mod.rs @@ -123,7 +123,7 @@ pub trait Adaptor { /// # Example /// ``` /// # use schnorr_fun::{adaptor::Adaptor, fun::Scalar, Schnorr}; - /// # let schnorr = schnorr_fun::test_instance!(); + /// let schnorr = schnorr_fun::new_with_deterministic_nonces::(); /// let decryption_key = Scalar::random(&mut rand::thread_rng()); /// let encryption_key = schnorr.encryption_key_for(&decryption_key); fn encryption_key_for(&self, decryption_key: &Scalar) -> Point; diff --git a/schnorr_fun/src/lib.rs b/schnorr_fun/src/lib.rs index cfd4bd4b..9f66137c 100755 --- a/schnorr_fun/src/lib.rs +++ b/schnorr_fun/src/lib.rs @@ -35,11 +35,3 @@ mod message; pub use message::*; mod libsecp_compat; - -#[macro_export] -#[doc(hidden)] -macro_rules! test_instance { - () => { - $crate::Schnorr::>::default() - }; -} diff --git a/schnorr_fun/src/schnorr.rs b/schnorr_fun/src/schnorr.rs index c1f44590..429521ba 100644 --- a/schnorr_fun/src/schnorr.rs +++ b/schnorr_fun/src/schnorr.rs @@ -2,11 +2,11 @@ use secp256kfun::{hash::Hash32, nonce::NoNonces}; use crate::{ fun::{ - derive_nonce, g, + derive_nonce, hash::{HashAdd, Tag}, - marker::*, - nonce::NonceGen, - s, KeyPair, Point, Scalar, G, + nonce::{self, NonceGen}, + prelude::*, + rand_core, KeyPair, }, Message, Signature, }; @@ -120,7 +120,7 @@ where /// # Message, /// # fun::{marker::*, Scalar}, /// # }; - /// # let schnorr = schnorr_fun::test_instance!(); + /// let schnorr = schnorr_fun::new_with_deterministic_nonces::(); /// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); /// let message = Message::::plain( /// "times-of-london", @@ -156,7 +156,7 @@ impl Schnorr { /// /// [`KeyPair`]: crate::fun::KeyPair pub fn new_keypair(&self, sk: Scalar) -> KeyPair { - KeyPair::::new(sk) + KeyPair::new_xonly(sk) } /// Produces the Fiat-Shamir challenge for a Schnorr signature in the form specified by [BIP-340]. @@ -169,11 +169,8 @@ impl Schnorr { /// Here's how you could use this to roll your own signatures. /// /// ``` - /// use schnorr_fun::{ - /// fun::{marker::*, s, Point, Scalar, G}, - /// Message, Schnorr, Signature, - /// }; - /// # let schnorr = schnorr_fun::test_instance!(); + /// use schnorr_fun::{fun::prelude::*, Message, Schnorr, Signature}; + /// let schnorr = schnorr_fun::new_with_deterministic_nonces::(); /// let message = Message::::plain("my-app", b"we rolled our own schnorr!"); /// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); /// let mut r = Scalar::random(&mut rand::thread_rng()); @@ -252,6 +249,39 @@ impl Schnorr { } } +/// Create a new [`Schnorr`] instance with deterministic nonce generation from a given hash as a type +/// paramater. +/// +/// This exists to avoid having to write out the right type parameters +/// +/// # Example +/// +/// ``` +/// let schnorr = schnorr_fun::new_with_deterministic_nonces::(); +/// ``` +pub fn new_with_deterministic_nonces() -> Schnorr> +where + H: Hash32, +{ + Schnorr::default() +} + +/// Create a new [`Schnorr`] instance with synthetic nonce generation from a given hash and rng as a +/// type parameter. +/// +/// # Example +/// +/// ``` +/// let schnorr = schnorr_fun::new_with_synthetic_nonces::(); +/// ``` +pub fn new_with_synthetic_nonces() -> Schnorr>> +where + H: Hash32, + R: rand_core::RngCore + Default + Clone, +{ + Schnorr::default() +} + #[cfg(test)] pub mod test { use crate::fun::nonce::Deterministic; @@ -294,7 +324,7 @@ pub mod test { #[test] fn anticipated_signature_on_should_correspond_to_actual_signature(sk in any::()) { - let schnorr = crate::test_instance!(); + let schnorr = crate::new_with_deterministic_nonces::(); let keypair = schnorr.new_keypair(sk); let msg = Message::::plain( "test", @@ -316,7 +346,7 @@ pub mod test { #[test] fn sign_deterministic(s1 in any::(), s2 in any::()) { - let schnorr = crate::test_instance!(); + let schnorr = crate::new_with_deterministic_nonces::(); let keypair_1 = schnorr.new_keypair(s1); let keypair_2 = schnorr.new_keypair(s2); let msg_atkdwn = Message::::plain("test", b"attack at dawn"); diff --git a/schnorr_fun/src/signature.rs b/schnorr_fun/src/signature.rs index fc519d78..2a3872bc 100644 --- a/schnorr_fun/src/signature.rs +++ b/schnorr_fun/src/signature.rs @@ -130,7 +130,7 @@ mod test { fn signature_serialization_roundtrip() { use super::*; use crate::{fun::Scalar, Message}; - let schnorr = crate::test_instance!(); + let schnorr = crate::new_with_deterministic_nonces::(); let kp = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); let signature = schnorr.sign(&kp, Message::::plain("test", b"foo")); let serialized = bincode::serialize(&signature).unwrap();