From 4cc535c0c9322a9ed787ee5ef39cfbe07d8a02f6 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Wed, 2 Oct 2024 17:43:45 +0000
Subject: [PATCH 01/13] modify frost-core traits to enable taproot
 compatibility

This commit contains changes to the frost-core crate which
allow ciphersuites to better customize how signatures are computed.
This will enable taproot support without requiring major changes
to existing frost ciphersuites.

Co-authored by @zebra-lucky and @mimoo

This work sponsored by dlcbtc.com and lightspark.com
---
 frost-core/src/batch.rs                 |  20 ++-
 frost-core/src/keys.rs                  |  10 ++
 frost-core/src/keys/dkg.rs              |  31 +++-
 frost-core/src/lib.rs                   | 122 +++++++++----
 frost-core/src/round1.rs                |  19 ++
 frost-core/src/round2.rs                |  22 ++-
 frost-core/src/signature.rs             |  41 +----
 frost-core/src/signing_key.rs           |  26 ++-
 frost-core/src/traits.rs                | 224 +++++++++++++++++++++++-
 frost-core/src/verifying_key.rs         |  35 +++-
 frost-ed25519/src/lib.rs                |  10 ++
 frost-ed25519/tests/serde_tests.rs      |  20 ++-
 frost-ed448/src/lib.rs                  |  10 ++
 frost-ed448/tests/serde_tests.rs        |  20 ++-
 frost-p256/src/lib.rs                   |  10 ++
 frost-p256/tests/serde_tests.rs         |  20 ++-
 frost-ristretto255/src/lib.rs           |  10 ++
 frost-ristretto255/tests/serde_tests.rs |  20 ++-
 frost-secp256k1/src/lib.rs              |  10 ++
 frost-secp256k1/tests/serde_tests.rs    |  20 ++-
 20 files changed, 560 insertions(+), 140 deletions(-)

diff --git a/frost-core/src/batch.rs b/frost-core/src/batch.rs
index 3aa1e806..fb5ada0e 100644
--- a/frost-core/src/batch.rs
+++ b/frost-core/src/batch.rs
@@ -20,6 +20,7 @@ use crate::{scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, *};
 pub struct Item<C: Ciphersuite> {
     vk: VerifyingKey<C>,
     sig: Signature<C>,
+    sig_params: C::SigningParameters,
     c: Challenge<C>,
 }
 
@@ -33,10 +34,15 @@ where
     where
         M: AsRef<[u8]>,
     {
-        // Compute c now to avoid dependency on the msg lifetime.
-        let c = crate::challenge(&sig.R, &vk, msg.as_ref())?;
-
-        Ok(Self { vk, sig, c })
+        let sig_target = SigningTarget::from_message(msg);
+        let c = <C>::challenge(&sig.R, &vk, &sig_target)?;
+
+        Ok(Self {
+            vk,
+            sig,
+            sig_params: sig_target.sig_params,
+            c,
+        })
     }
 }
 
@@ -52,7 +58,8 @@ where
     /// requires borrowing the message data, the `Item` type is unlinked
     /// from the lifetime of the message.
     pub fn verify_single(self) -> Result<(), Error<C>> {
-        self.vk.verify_prehashed(self.c, &self.sig)
+        self.vk
+            .verify_prehashed(self.c, &self.sig, &self.sig_params)
     }
 }
 
@@ -121,6 +128,7 @@ where
         for item in self.signatures.iter() {
             let z = item.sig.z;
             let R = item.sig.R;
+            let vk = <C>::effective_pubkey_element(&item.vk, &item.sig_params);
 
             let blind = <<C::Group as Group>::Field>::random(&mut rng);
 
@@ -131,7 +139,7 @@ where
             Rs.push(R);
 
             VK_coeffs.push(<<C::Group as Group>::Field>::zero() + (blind * item.c.0));
-            VKs.push(item.vk.to_element());
+            VKs.push(vk);
         }
 
         let scalars = core::iter::once(&P_coeff_acc)
diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs
index 9112e108..f9cf0f08 100644
--- a/frost-core/src/keys.rs
+++ b/frost-core/src/keys.rs
@@ -119,6 +119,11 @@ where
     pub(crate) fn from_coefficients(coefficients: &[Scalar<C>], peer: Identifier<C>) -> Self {
         Self::new(evaluate_polynomial(peer, coefficients))
     }
+
+    /// Returns negated SigningShare
+    pub fn negate(&mut self) {
+        self.0 .0 = <<C::Group as Group>::Field>::negate(&self.to_scalar());
+    }
 }
 
 impl<C> Debug for SigningShare<C>
@@ -630,6 +635,11 @@ where
             min_signers,
         }
     }
+
+    /// Negate `SigningShare`.
+    pub fn negate_signing_share(&mut self) {
+        self.signing_share.negate();
+    }
 }
 
 #[cfg(feature = "serialization")]
diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs
index aa3a56ae..77e29d41 100644
--- a/frost-core/src/keys/dkg.rs
+++ b/frost-core/src/keys/dkg.rs
@@ -38,7 +38,7 @@ use rand_core::{CryptoRng, RngCore};
 
 use crate::{
     Challenge, Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, Signature,
-    SigningKey, VerifyingKey,
+    SigningKey,
 };
 
 #[cfg(feature = "serialization")]
@@ -322,7 +322,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
 /// Generates the challenge for the proof of knowledge to a secret for the DKG.
 fn challenge<C>(
     identifier: Identifier<C>,
-    verifying_key: &VerifyingKey<C>,
+    verifying_key: &Element<C>,
     R: &Element<C>,
 ) -> Result<Challenge<C>, Error<C>>
 where
@@ -331,7 +331,7 @@ where
     let mut preimage = vec![];
 
     preimage.extend_from_slice(identifier.serialize().as_ref());
-    preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
+    preimage.extend_from_slice(<C::Group>::serialize(&verifying_key)?.as_ref());
     preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
 
     Ok(Challenge(
@@ -354,13 +354,23 @@ pub(crate) fn compute_proof_of_knowledge<C: Ciphersuite, R: RngCore + CryptoRng>
     // > a_{i0} by calculating σ_i = (R_i, μ_i), such that k ← Z_q, R_i = g^k,
     // > c_i = H(i, Φ, g^{a_{i0}} , R_i), μ_i = k + a_{i0} · c_i, with Φ being
     // > a context string to prevent replay attacks.
-    let k = <<C::Group as Group>::Field>::random(&mut rng);
-    let R_i = <C::Group>::generator() * k;
-    let c_i = challenge::<C>(identifier, &commitment.verifying_key()?, &R_i)?;
+    let mut k = <<C::Group as Group>::Field>::random(&mut rng);
+    let mut R_i = <C::Group>::generator() * k;
+    k = <C>::effective_nonce_secret(k, &R_i);
+    R_i = <C>::effective_nonce_element(R_i);
+
+    let verifying_key = commitment.verifying_key()?;
+    let sig_params = Default::default();
+
+    let phi_ell0 = <C>::effective_pubkey_element(&verifying_key, &sig_params);
+
+    let c_i = challenge::<C>(identifier, &phi_ell0, &R_i)?;
     let a_i0 = *coefficients
         .first()
         .expect("coefficients must have at least one element");
-    let mu_i = k + a_i0 * c_i.0;
+    let a_i0_effective = <C>::effective_secret_key(a_i0, &verifying_key, &sig_params);
+
+    let mu_i = k + a_i0_effective * c_i.0;
     Ok(Signature { R: R_i, z: mu_i })
 }
 
@@ -380,9 +390,12 @@ pub(crate) fn verify_proof_of_knowledge<C: Ciphersuite>(
     let ell = identifier;
     let R_ell = proof_of_knowledge.R;
     let mu_ell = proof_of_knowledge.z;
-    let phi_ell0 = commitment.verifying_key()?;
+
+    let verifying_key = commitment.verifying_key()?;
+    let phi_ell0 = <C>::effective_pubkey_element(&verifying_key, &Default::default());
     let c_ell = challenge::<C>(ell, &phi_ell0, &R_ell)?;
-    if R_ell != <C::Group>::generator() * mu_ell - phi_ell0.to_element() * c_ell.0 {
+
+    if R_ell != <C::Group>::generator() * mu_ell - phi_ell0 * c_ell.0 {
         return Err(Error::InvalidProofOfKnowledge { culprit: ell });
     }
     Ok(())
diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs
index fbecfe07..434a3816 100644
--- a/frost-core/src/lib.rs
+++ b/frost-core/src/lib.rs
@@ -57,18 +57,14 @@ use scalar_mul::VartimeMultiscalarMul;
 pub use serde;
 pub use signature::Signature;
 pub use signing_key::SigningKey;
-pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
+pub use traits::{Ciphersuite, Element, Field, Group, Scalar, SigningParameters};
 pub use verifying_key::VerifyingKey;
 
 /// A type refinement for the scalar field element representing the per-message _[challenge]_.
 ///
 /// [challenge]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-challenge-computa
 #[derive(Copy, Clone)]
-#[cfg_attr(feature = "internals", visibility::make(pub))]
-#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
-pub(crate) struct Challenge<C: Ciphersuite>(
-    pub(crate) <<C::Group as Group>::Field as Field>::Scalar,
-);
+pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);
 
 impl<C> Challenge<C>
 where
@@ -192,9 +188,7 @@ where
 ///
 /// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
 #[derive(Clone, PartialEq, Eq)]
-#[cfg_attr(feature = "internals", visibility::make(pub))]
-#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
-pub(crate) struct BindingFactor<C: Ciphersuite>(Scalar<C>);
+pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
 
 impl<C> BindingFactor<C>
 where
@@ -357,6 +351,54 @@ fn derive_interpolating_value<C: Ciphersuite>(
     )
 }
 
+/// The data which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
+#[derive(Clone, Debug, PartialEq, Eq, Getters)]
+pub struct SigningTarget<C: Ciphersuite> {
+    #[cfg_attr(
+        feature = "serde",
+        serde(
+            serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
+            deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
+        )
+    )]
+    message: Vec<u8>,
+
+    #[cfg_attr(feature = "serde", serde(default))]
+    sig_params: C::SigningParameters,
+}
+
+impl<C: Ciphersuite> SigningTarget<C> {
+    /// Construct a signing target from a message and additional signing parameters.
+    pub fn new<T: AsRef<[u8]>, P: Into<C::SigningParameters>>(
+        message: T,
+        sig_params: P,
+    ) -> SigningTarget<C> {
+        SigningTarget {
+            message: message.as_ref().to_vec(),
+            sig_params: sig_params.into(),
+        }
+    }
+
+    /// Constructs a signing target from an arbitrary message.
+    /// This populates [the `sig_params` field][SigningTarget::sig_params] with
+    /// the [`Default`] instance of the [`Ciphersuite::SigningParameters`].
+    pub fn from_message<T: AsRef<[u8]>>(message: T) -> SigningTarget<C> {
+        SigningTarget {
+            message: message.as_ref().to_vec(),
+            sig_params: C::SigningParameters::default(),
+        }
+    }
+}
+
+impl<C: Ciphersuite, T: AsRef<[u8]>> From<T> for SigningTarget<C> {
+    fn from(message: T) -> Self {
+        Self::from_message(message)
+    }
+}
+
 /// Generated by the coordinator of the signing operation and distributed to
 /// each signing party
 #[derive(Clone, Debug, PartialEq, Eq, Getters)]
@@ -370,18 +412,9 @@ pub struct SigningPackage<C: Ciphersuite> {
     /// The set of commitments participants published in the first round of the
     /// protocol.
     signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
-    /// Message which each participant will sign.
-    ///
-    /// Each signer should perform protocol-specific verification on the
-    /// message.
-    #[cfg_attr(
-        feature = "serde",
-        serde(
-            serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
-            deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
-        )
-    )]
-    message: Vec<u8>,
+    /// The message and parameters which each participant will use to sign.
+    /// Each signer should perform protocol-specific verification on the signing target.
+    sig_target: SigningTarget<C>,
 }
 
 impl<C> SigningPackage<C>
@@ -391,14 +424,19 @@ where
     /// Create a new `SigningPackage`
     ///
     /// The `signing_commitments` are sorted by participant `identifier`.
+    ///
+    /// The `sig_target` can be any bytes-like type that implements `AsRef<[u8]>`.
+    /// Some ciphersuites like `frost-secp256k1-tr` allow customization of the signing
+    /// process by embedding additional parameters into a [`SigningTarget`], but this
+    /// is optional and not required by most ciphersuites.
     pub fn new(
         signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
-        message: &[u8],
+        sig_target: impl Into<SigningTarget<C>>,
     ) -> SigningPackage<C> {
         SigningPackage {
             header: Header::default(),
             signing_commitments,
-            message: message.to_vec(),
+            sig_target: sig_target.into(),
         }
     }
 
@@ -410,6 +448,11 @@ where
         self.signing_commitments.get(identifier).copied()
     }
 
+    /// Returns the message to be signed.
+    pub fn message(&self) -> &[u8] {
+        &self.sig_target.message
+    }
+
     /// Compute the preimages to H1 to compute the per-signer binding factors
     // We separate this out into its own method so it can be tested
     #[cfg_attr(feature = "internals", visibility::make(pub))]
@@ -430,7 +473,7 @@ where
         // The message is hashed with H4 to force the variable-length message
         // into a fixed-length byte string, same for hashing the variable-sized
         // (between runs of the protocol) set of group commitments, but with H5.
-        binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
+        binding_factor_input_prefix.extend_from_slice(C::H4(self.message()).as_ref());
         binding_factor_input_prefix.extend_from_slice(
             C::H5(&round1::encode_group_commitments(self.signing_commitments())?[..]).as_ref(),
         );
@@ -469,9 +512,7 @@ where
 /// The product of all signers' individual commitments, published as part of the
 /// final signature.
 #[derive(Clone, PartialEq, Eq)]
-#[cfg_attr(feature = "internals", visibility::make(pub))]
-#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
-pub(crate) struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
+pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
 
 impl<C> GroupCommitment<C>
 where
@@ -482,6 +523,12 @@ where
     pub fn to_element(self) -> <C::Group as Group>::Element {
         self.0
     }
+
+    /// Check if group commitment is odd
+    #[cfg(feature = "internals")]
+    pub fn y_is_odd(&self) -> bool {
+        <C::Group as Group>::y_is_odd(&self.0)
+    }
 }
 
 /// Generates the group commitment which is published as part of the joint
@@ -589,6 +636,7 @@ where
         compute_binding_factor_list(signing_package, &pubkeys.verifying_key, &[])?;
     // Compute the group commitment from signing commitments produced in round one.
     let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
+    let R = <C>::effective_nonce_element(group_commitment.0);
 
     // The aggregation of the signature shares by summing them up, resulting in
     // a plain Schnorr signature.
@@ -602,15 +650,13 @@ where
         z = z + signature_share.to_scalar();
     }
 
-    let signature = Signature {
-        R: group_commitment.0,
-        z,
-    };
+    let signature: Signature<C> =
+        <C>::aggregate_sig_finalize(z, R, &pubkeys.verifying_key, &signing_package.sig_target)?;
 
     // Verify the aggregate signature
     let verification_result = pubkeys
         .verifying_key
-        .verify(signing_package.message(), &signature);
+        .verify(signing_package.sig_target.clone(), &signature);
 
     // Only if the verification of the aggregate signature failed; verify each share to find the cheater.
     // This approach is more efficient since we don't need to verify all shares
@@ -619,6 +665,7 @@ where
     if verification_result.is_err() {
         detect_cheater(
             group_commitment,
+            &R,
             pubkeys,
             signing_package,
             signature_shares,
@@ -631,21 +678,21 @@ where
 
     Ok(signature)
 }
-
 /// Optional cheater detection feature
 /// Each share is verified to find the cheater
 fn detect_cheater<C: Ciphersuite>(
     group_commitment: GroupCommitment<C>,
+    effective_group_commitment: &Element<C>,
     pubkeys: &keys::PublicKeyPackage<C>,
     signing_package: &SigningPackage<C>,
     signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
     binding_factor_list: &BindingFactorList<C>,
 ) -> Result<(), Error<C>> {
     // Compute the per-message challenge.
-    let challenge = crate::challenge::<C>(
-        &group_commitment.0,
+    let challenge = <C>::challenge(
+        effective_group_commitment,
         &pubkeys.verifying_key,
-        signing_package.message().as_slice(),
+        &signing_package.sig_target,
     )?;
 
     // Verify the signature shares.
@@ -677,6 +724,9 @@ fn detect_cheater<C: Ciphersuite>(
             signer_pubkey,
             lambda_i,
             &challenge,
+            &group_commitment,
+            &pubkeys.verifying_key,
+            &signing_package.sig_target.sig_params,
         )?;
     }
 
diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs
index 27e1c229..ce5e28a5 100644
--- a/frost-core/src/round1.rs
+++ b/frost-core/src/round1.rs
@@ -64,6 +64,11 @@ where
         self.0 .0
     }
 
+    /// Negate `Nonce`.
+    pub fn negate(&mut self) {
+        self.0 .0 = <<C::Group as Group>::Field>::negate(&self.to_scalar());
+    }
+
     /// Generates a nonce from the given random bytes.
     /// This function allows testing and MUST NOT be made public.
     pub(crate) fn nonce_generate_from_random_bytes(
@@ -281,6 +286,12 @@ where
     pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
         Deserialize::deserialize(bytes)
     }
+
+    /// Negate `SigningNonces`.
+    pub fn negate_nonces(&mut self) {
+        self.binding.negate();
+        self.hiding.negate();
+    }
 }
 
 /// Published by each participant in the first round of the signing protocol.
@@ -358,6 +369,14 @@ where
 #[derive(Clone, Copy, PartialEq)]
 pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) Element<C>);
 
+impl<C: Ciphersuite> GroupCommitmentShare<C> {
+    /// Return the underlying element.
+    #[cfg_attr(feature = "internals", visibility::make(pub))]
+    pub(crate) fn to_element(self) -> Element<C> {
+        self.0
+    }
+}
+
 /// Encode the list of group signing commitments.
 ///
 /// Implements [`encode_group_commitment_list()`] from the spec.
diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs
index 7b0fd450..06cfec29 100644
--- a/frost-core/src/round2.rs
+++ b/frost-core/src/round2.rs
@@ -4,7 +4,7 @@ use core::fmt::{self, Debug};
 
 use crate as frost;
 use crate::{
-    challenge, Challenge, Ciphersuite, Error, Field, Group, {round1, *},
+    Challenge, Ciphersuite, Error, Field, Group, {round1, *},
 };
 
 /// A participant's signature share, which the coordinator will aggregate with all other signer's
@@ -62,6 +62,7 @@ where
     #[cfg(any(feature = "cheater-detection", feature = "internals"))]
     #[cfg_attr(feature = "internals", visibility::make(pub))]
     #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
+    #[allow(clippy::too_many_arguments)]
     pub(crate) fn verify(
         &self,
         identifier: Identifier<C>,
@@ -69,9 +70,16 @@ where
         verifying_share: &frost::keys::VerifyingShare<C>,
         lambda_i: Scalar<C>,
         challenge: &Challenge<C>,
+        group_commitment: &frost::GroupCommitment<C>,
+        verifying_key: &frost::VerifyingKey<C>,
+        sig_params: &C::SigningParameters,
     ) -> Result<(), Error<C>> {
+        let commitment_share =
+            <C>::effective_commitment_share(group_commitment_share.clone(), &group_commitment);
+        let vsh = <C>::effective_verifying_share(&verifying_share, &verifying_key, &sig_params);
+
         if (<C::Group>::generator() * self.to_scalar())
-            != (group_commitment_share.0 + (verifying_share.to_element() * challenge.0 * lambda_i))
+            != (commitment_share + (vsh * challenge.0 * lambda_i))
         {
             return Err(Error::InvalidSignatureShare {
                 culprit: identifier,
@@ -96,7 +104,7 @@ where
 /// Compute the signature share for a signing operation.
 #[cfg_attr(feature = "internals", visibility::make(pub))]
 #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
-fn compute_signature_share<C: Ciphersuite>(
+pub(super) fn compute_signature_share<C: Ciphersuite>(
     signer_nonces: &round1::SigningNonces<C>,
     binding_factor: BindingFactor<C>,
     lambda_i: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
@@ -158,19 +166,21 @@ pub fn sign<C: Ciphersuite>(
     let lambda_i = frost::derive_interpolating_value(key_package.identifier(), signing_package)?;
 
     // Compute the per-message challenge.
-    let challenge = challenge::<C>(
+    let challenge = <C>::challenge(
         &group_commitment.0,
         &key_package.verifying_key,
-        signing_package.message.as_slice(),
+        &signing_package.sig_target,
     )?;
 
     // Compute the Schnorr signature share.
-    let signature_share = compute_signature_share(
+    let signature_share = <C>::compute_signature_share(
         signer_nonces,
         binding_factor,
+        group_commitment,
         lambda_i,
         key_package,
         challenge,
+        &signing_package.sig_target.sig_params,
     );
 
     Ok(signature_share)
diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs
index 7150b085..2d085493 100644
--- a/frost-core/src/signature.rs
+++ b/frost-core/src/signature.rs
@@ -31,49 +31,12 @@ where
 
     /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
     pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
-        // To compute the expected length of the encoded point, encode the generator
-        // and get its length. Note that we can't use the identity because it can be encoded
-        // shorter in some cases (e.g. P-256, which uses SEC1 encoding).
-        let generator = <C::Group>::generator();
-        let mut R_bytes = Vec::from(<C::Group>::serialize(&generator)?.as_ref());
-        let R_bytes_len = R_bytes.len();
-
-        let one = <<C::Group as Group>::Field as Field>::zero();
-        let mut z_bytes =
-            Vec::from(<<C::Group as Group>::Field as Field>::serialize(&one).as_ref());
-        let z_bytes_len = z_bytes.len();
-
-        if bytes.len() != R_bytes_len + z_bytes_len {
-            return Err(Error::MalformedSignature);
-        }
-
-        R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?);
-
-        let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
-
-        // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]`
-        z_bytes[..].copy_from_slice(
-            bytes
-                .get(R_bytes_len..R_bytes_len + z_bytes_len)
-                .ok_or(Error::MalformedSignature)?,
-        );
-
-        let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
-
-        Ok(Self {
-            R: <C::Group>::deserialize(R_serialization)?,
-            z: <<C::Group as Group>::Field>::deserialize(z_serialization)?,
-        })
+        C::deserialize_signature(bytes)
     }
 
     /// Converts this signature to its byte serialization.
     pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
-        let mut bytes = Vec::<u8>::new();
-
-        bytes.extend(<C::Group>::serialize(&self.R)?.as_ref());
-        bytes.extend(<<C::Group as Group>::Field>::serialize(&self.z).as_ref());
-
-        Ok(bytes)
+        <C>::serialize_signature(self)
     }
 }
 
diff --git a/frost-core/src/signing_key.rs b/frost-core/src/signing_key.rs
index 4ba306ed..c562c057 100644
--- a/frost-core/src/signing_key.rs
+++ b/frost-core/src/signing_key.rs
@@ -5,8 +5,8 @@ use alloc::vec::Vec;
 use rand_core::{CryptoRng, RngCore};
 
 use crate::{
-    random_nonzero, serialization::SerializableScalar, Ciphersuite, Error, Field, Group, Scalar,
-    Signature, VerifyingKey,
+    random_nonzero, serialization::SerializableScalar, Challenge, Ciphersuite, Error, Field, Group,
+    Scalar, Signature, SigningTarget, VerifyingKey,
 };
 
 /// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`].
@@ -40,17 +40,25 @@ where
     }
 
     /// Create a signature `msg` using this `SigningKey`.
-    pub fn sign<R: RngCore + CryptoRng>(&self, mut rng: R, msg: &[u8]) -> Signature<C> {
-        let k = random_nonzero::<C, R>(&mut rng);
+    pub fn sign<R: RngCore + CryptoRng>(
+        &self,
+        mut rng: R,
+        sig_target: impl Into<SigningTarget<C>>,
+    ) -> Signature<C> {
+        let sig_target = sig_target.into();
 
-        let R = <C::Group>::generator() * k;
+        let public = VerifyingKey::<C>::from(*self);
+        let secret = <C>::effective_secret_key(self.scalar, &public, &sig_target.sig_params);
 
-        // Generate Schnorr challenge
-        let c = crate::challenge::<C>(&R, &VerifyingKey::<C>::from(*self), msg).expect("should not return error since that happens only if one of the inputs is the identity. R is not since k is nonzero. The verifying_key is not because signing keys are not allowed to be zero.");
+        let mut k = random_nonzero::<C, R>(&mut rng);
+        let mut R = <C::Group>::generator() * k;
+        k = <C>::effective_nonce_secret(k, &R);
+        R = <C>::effective_nonce_element(R);
 
-        let z = k + (c.0 * self.scalar);
+        // Generate Schnorr challenge
+        let c: Challenge<C> = <C>::challenge(&R, &public, &sig_target).expect("should not return error since that happens only if one of the inputs is the identity. R is not since k is nonzero. The verifying_key is not because signing keys are not allowed to be zero.");
 
-        Signature { R, z }
+        <C>::single_sig_finalize(k, R, secret, &c, &public, &sig_target.sig_params)
     }
 
     /// Creates a SigningKey from a scalar. Returns an error if the scalar is zero.
diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs
index 3e0d9365..5e21a2f0 100644
--- a/frost-core/src/traits.rs
+++ b/frost-core/src/traits.rs
@@ -8,7 +8,12 @@ use core::{
 use alloc::vec::Vec;
 use rand_core::{CryptoRng, RngCore};
 
-use crate::{Error, FieldError, GroupError, Signature, VerifyingKey};
+use crate::{
+    challenge,
+    keys::{KeyPackage, VerifyingShare},
+    round1, round2, BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError,
+    Signature, SigningTarget, VerifyingKey,
+};
 
 /// A prime order finite field GF(q) over which all scalar values for our prime order group can be
 /// multiplied are defined.
@@ -41,6 +46,11 @@ pub trait Field: Copy + Clone {
     /// element is zero.
     fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError>;
 
+    /// Computes the negation of the element of the scalar field
+    fn negate(_scalar: &Self::Scalar) -> Self::Scalar {
+        panic!("Not implemented");
+    }
+
     /// Generate a random scalar from the entire space [0, l-1]
     ///
     /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.6>
@@ -114,6 +124,11 @@ pub trait Group: Copy + Clone + PartialEq {
     /// [`ScalarBaseMult()`]: https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.10
     fn generator() -> Self::Element;
 
+    /// Check if element is odd
+    fn y_is_odd(_element: &Self::Element) -> bool {
+        panic!("Not implemented");
+    }
+
     /// A member function of a group _G_ that maps an [`Element`] to a unique
     /// byte array buf of fixed length Ne. This function raises an error if the
     /// element is the identity element of the group.
@@ -134,6 +149,25 @@ pub trait Group: Copy + Clone + PartialEq {
 /// An element of the [`Ciphersuite`] `C`'s [`Group`].
 pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
 
+/// This is a marker trait for types which are passed in to modify the signing logic of a [`Ciphersuite`].
+///
+/// If the `serde` feature is enabled, any type implementing this trait must also implement
+/// [`serde::Serialize`] and [`serde::Deserialize`].
+#[cfg(feature = "serde")]
+pub trait SigningParameters:
+    Clone + Debug + Eq + PartialEq + Default + serde::Serialize + for<'d> serde::Deserialize<'d>
+{
+}
+
+/// This is a marker trait for types which are passed in to modify the signing logic of a [`Ciphersuite`].
+///
+/// If the `serde` feature is enabled, any type implementing this trait must also implement
+/// [`serde::Serialize`] and [`serde::Deserialize`].
+#[cfg(not(feature = "serde"))]
+pub trait SigningParameters: Clone + Debug + Eq + PartialEq + Default {}
+
+impl SigningParameters for () {}
+
 /// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
 /// function.
 ///
@@ -156,6 +190,10 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// `Group::ScalarSerialization`
     type SignatureSerialization: AsRef<[u8]> + TryFrom<Vec<u8>>;
 
+    /// Additional parameters which should be provided to the ciphersuite's signing code
+    /// to produce an effective signature. Most ciphersuites will just set this to `()`.
+    type SigningParameters: SigningParameters;
+
     /// [H1] for a FROST ciphersuite.
     ///
     /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
@@ -223,12 +261,190 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// (see [`crate::batch::Verifier`]) also uses the default implementation regardless whether a
     /// tailored implementation was provided.
     fn verify_signature(
-        msg: &[u8],
+        sig_target: &SigningTarget<Self>,
         signature: &Signature<Self>,
         public_key: &VerifyingKey<Self>,
     ) -> Result<(), Error<Self>> {
-        let c = crate::challenge::<Self>(&signature.R, public_key, msg)?;
+        let c = <Self>::challenge(&signature.R, public_key, sig_target)?;
 
-        public_key.verify_prehashed(c, signature)
+        public_key.verify_prehashed(c, signature, &sig_target.sig_params)
+    }
+
+    /// Generates the challenge as is required for Schnorr signatures.
+    ///
+    /// Deals in bytes, so that [FROST] and singleton signing and verification can use it with different
+    /// types.
+    ///
+    /// This is the only invocation of the H2 hash function from the [RFC].
+    ///
+    /// [FROST]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-challenge-computa
+    /// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-3.2
+    fn challenge(
+        R: &Element<Self>,
+        verifying_key: &VerifyingKey<Self>,
+        sig_target: &SigningTarget<Self>,
+    ) -> Result<Challenge<Self>, Error<Self>> {
+        challenge(R, verifying_key, &sig_target.message)
+    }
+
+    /// Finalize an aggregated group signature. This is used by frost-sepc256k1-tr
+    /// to ensure the signature is valid under BIP340.
+    fn aggregate_sig_finalize(
+        z: <<Self::Group as Group>::Field as Field>::Scalar,
+        R: Element<Self>,
+        _verifying_key: &VerifyingKey<Self>,
+        _sig_target: &SigningTarget<Self>,
+    ) -> Result<Signature<Self>, Error<Self>> {
+        Ok(Signature { R, z })
+    }
+
+    /// Finalize and output a single-signer Schnorr signature.
+    fn single_sig_finalize(
+        k: <<Self::Group as Group>::Field as Field>::Scalar,
+        R: Element<Self>,
+        secret: <<Self::Group as Group>::Field as Field>::Scalar,
+        challenge: &Challenge<Self>,
+        _verifying_key: &VerifyingKey<Self>,
+        _sig_params: &Self::SigningParameters,
+    ) -> Signature<Self> {
+        let z = k + (challenge.0 * secret);
+        Signature { R, z }
+    }
+
+    /// Converts a signature to its [`Ciphersuite::SignatureSerialization`] in bytes.
+    ///
+    /// The default implementation serializes a signature by serializing its `R` point and
+    /// `z` component independently, and then concatenating them.
+    fn serialize_signature(signature: &Signature<Self>) -> Result<Vec<u8>, Error<Self>> {
+        let mut bytes = vec![];
+        bytes.extend(<Self::Group>::serialize(&signature.R)?.as_ref());
+        bytes.extend(<<Self::Group as Group>::Field>::serialize(&signature.z).as_ref());
+        Ok(bytes)
+    }
+
+    /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
+    ///
+    /// The default implementation assumes the serialization is a serialized `R` point
+    /// followed by a serialized `z` component with no padding or extra fields.
+    fn deserialize_signature(bytes: &[u8]) -> Result<Signature<Self>, Error<Self>> {
+        // To compute the expected length of the encoded point, encode the generator
+        // and get its length. Note that we can't use the identity because it can be encoded
+        // shorter in some cases (e.g. P-256, which uses SEC1 encoding).
+        let generator = <Self::Group>::generator();
+        let mut R_bytes = Vec::from(<Self::Group>::serialize(&generator)?.as_ref());
+        let R_bytes_len = R_bytes.len();
+
+        let one = <<Self::Group as Group>::Field as Field>::zero();
+        let mut z_bytes =
+            Vec::from(<<Self::Group as Group>::Field as Field>::serialize(&one).as_ref());
+        let z_bytes_len = z_bytes.len();
+
+        if bytes.len() != R_bytes_len + z_bytes_len {
+            return Err(Error::MalformedSignature);
+        }
+
+        R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?);
+
+        let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
+
+        // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]`
+        z_bytes[..].copy_from_slice(
+            bytes
+                .get(R_bytes_len..R_bytes_len + z_bytes_len)
+                .ok_or(Error::MalformedSignature)?,
+        );
+
+        let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
+
+        Ok(Signature {
+            R: <Self::Group>::deserialize(R_serialization)?,
+            z: <<Self::Group as Group>::Field>::deserialize(z_serialization)?,
+        })
+    }
+
+    /// Compute the signature share for a particular signer on a given challenge.
+    fn compute_signature_share(
+        signer_nonces: &round1::SigningNonces<Self>,
+        binding_factor: BindingFactor<Self>,
+        _group_commitment: GroupCommitment<Self>,
+        lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
+        key_package: &KeyPackage<Self>,
+        challenge: Challenge<Self>,
+        _sig_params: &Self::SigningParameters,
+    ) -> round2::SignatureShare<Self> {
+        round2::compute_signature_share(
+            signer_nonces,
+            binding_factor,
+            lambda_i,
+            key_package,
+            challenge,
+        )
+    }
+
+    /// Compute the effective group element which should be used for signature operations
+    /// for the given verifying key.
+    ///
+    /// In frost-sepc256k1-tr, this is used to commit the key to taptree merkle root hashes.
+    fn effective_pubkey_element(
+        verifying_key: &VerifyingKey<Self>,
+        _sig_params: &Self::SigningParameters,
+    ) -> <Self::Group as Group>::Element {
+        verifying_key.to_element()
+    }
+
+    /// Compute the effective nonce element which should be used for signature operations.
+    ///
+    /// In frost-sepc256k1-tr, this negates the nonce if it has an odd parity.
+    fn effective_nonce_element(
+        R: <Self::Group as Group>::Element,
+    ) -> <Self::Group as Group>::Element {
+        R
+    }
+
+    /// Compute the effective secret key which should be used for signature operations
+    /// for the given verifying key.
+    ///
+    /// In frost-sepc256k1-tr, this is used to commit the key to taptree merkle root hashes.
+    fn effective_secret_key(
+        secret: <<Self::Group as Group>::Field as Field>::Scalar,
+        _public: &VerifyingKey<Self>,
+        _sig_params: &Self::SigningParameters,
+    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        secret
+    }
+
+    /// Compute the effective nonce secret which should be used for signature operations.
+    ///
+    /// In frost-sepc256k1-tr, this negates the nonce if it has an odd parity.
+    fn effective_nonce_secret(
+        nonce: <<Self::Group as Group>::Field as Field>::Scalar,
+        _R: &Element<Self>,
+    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        nonce
+    }
+
+    /// Compute the effective nonce commitment share which should be used for
+    /// FROST signing.
+    ///
+    /// In frost-sepc256k1-tr, this negates the commitment share if the group's final
+    /// commitment has an odd parity.
+    fn effective_commitment_share(
+        group_commitment_share: round1::GroupCommitmentShare<Self>,
+        _group_commitment: &GroupCommitment<Self>,
+    ) -> <Self::Group as Group>::Element {
+        group_commitment_share.to_element()
+    }
+
+    /// Compute the effective verifying share which should be used for FROST
+    /// partial signature verification.
+    ///
+    /// In frost-sepc256k1-tr, this negates the verifying share if the group's final
+    /// verifying key has an odd parity.
+    fn effective_verifying_share(
+        verifying_share: &VerifyingShare<Self>,
+        _verifying_key: &VerifyingKey<Self>,
+        _sig_params: &Self::SigningParameters,
+    ) -> <Self::Group as Group>::Element {
+        verifying_share.to_element()
     }
 }
diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs
index c3a1cfc5..b472d484 100644
--- a/frost-core/src/verifying_key.rs
+++ b/frost-core/src/verifying_key.rs
@@ -5,7 +5,10 @@ use alloc::{string::ToString, vec::Vec};
 #[cfg(any(test, feature = "test-impl"))]
 use hex::FromHex;
 
-use crate::{serialization::SerializableElement, Challenge, Ciphersuite, Error, Group, Signature};
+use crate::{
+    serialization::SerializableElement, Challenge, Ciphersuite, Error, Group, Signature,
+    SigningTarget,
+};
 
 /// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -39,6 +42,18 @@ where
         self.element.0
     }
 
+    /// Return the effective verifying key given the specific signing parameters
+    /// to be verified against. For most ciphersuites, this simply returns the
+    /// same verifying key unchanged.
+    pub fn effective_key(self, sig_params: &C::SigningParameters) -> Self {
+        VerifyingKey::new(<C>::effective_pubkey_element(&self, sig_params))
+    }
+
+    /// Check if VerifyingKey is odd
+    pub fn y_is_odd(&self) -> bool {
+        <C::Group as Group>::y_is_odd(&self.to_element())
+    }
+
     /// Deserialize from bytes
     pub fn deserialize(bytes: &[u8]) -> Result<VerifyingKey<C>, Error<C>> {
         Ok(Self::new(SerializableElement::deserialize(bytes)?.0))
@@ -55,14 +70,18 @@ where
         &self,
         challenge: Challenge<C>,
         signature: &Signature<C>,
+        sig_params: &C::SigningParameters,
     ) -> Result<(), Error<C>> {
         // Verify check is h * ( - z * B + R  + c * A) == 0
         //                 h * ( z * B - c * A - R) == 0
         //
         // where h is the cofactor
+        let R = signature.R;
+        let vk = C::effective_pubkey_element(&self, sig_params);
+
         let zB = C::Group::generator() * signature.z;
-        let cA = self.element.0 * challenge.0;
-        let check = (zB - cA - signature.R) * C::Group::cofactor();
+        let cA = vk * challenge.0;
+        let check = (zB - cA - R) * C::Group::cofactor();
 
         if check == C::Group::identity() {
             Ok(())
@@ -71,9 +90,13 @@ where
         }
     }
 
-    /// Verify a purported `signature` over `msg` made by this verification key.
-    pub fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<(), Error<C>> {
-        C::verify_signature(msg, signature, self)
+    /// Verify a purported `signature` over `sig_target` made by this verification key.
+    pub fn verify(
+        &self,
+        sig_target: impl Into<SigningTarget<C>>,
+        signature: &Signature<C>,
+    ) -> Result<(), Error<C>> {
+        C::verify_signature(&sig_target.into(), signature, self)
     }
 
     /// Computes the group public key given the group commitment.
diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs
index 1e33b2d9..42a25933 100644
--- a/frost-ed25519/src/lib.rs
+++ b/frost-ed25519/src/lib.rs
@@ -163,6 +163,14 @@ const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ed25519Sha512;
 
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+pub type SigningParameters = ();
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<Ed25519Sha512>;
+
 impl Ciphersuite for Ed25519Sha512 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -172,6 +180,8 @@ impl Ciphersuite for Ed25519Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
+    type SigningParameters = ();
+
     /// H1 for FROST(Ed25519, SHA-512)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.1-2.4.2.2
diff --git a/frost-ed25519/tests/serde_tests.rs b/frost-ed25519/tests/serde_tests.rs
index 9f722797..1a2a6a4f 100644
--- a/frost-ed25519/tests/serde_tests.rs
+++ b/frost-ed25519/tests/serde_tests.rs
@@ -112,7 +112,9 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -133,7 +135,9 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -153,7 +157,9 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -172,7 +178,9 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -192,7 +200,9 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "message": "68656c6c6f20776f726c64",
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
       "extra": 1
     }
     "#;
diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs
index 4ceb707e..f4f5bbd2 100644
--- a/frost-ed448/src/lib.rs
+++ b/frost-ed448/src/lib.rs
@@ -157,6 +157,14 @@ const CONTEXT_STRING: &str = "FROST-ED448-SHAKE256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ed448Shake256;
 
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+pub type SigningParameters = ();
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<Ed448Shake256>;
+
 impl Ciphersuite for Ed448Shake256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -166,6 +174,8 @@ impl Ciphersuite for Ed448Shake256 {
 
     type SignatureSerialization = [u8; 114];
 
+    type SigningParameters = ();
+
     /// H1 for FROST(Ed448, SHAKE256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.3-2.4.2.2
diff --git a/frost-ed448/tests/serde_tests.rs b/frost-ed448/tests/serde_tests.rs
index 3b5c667a..71de30c2 100644
--- a/frost-ed448/tests/serde_tests.rs
+++ b/frost-ed448/tests/serde_tests.rs
@@ -112,7 +112,9 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -133,7 +135,9 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -153,7 +157,9 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -172,7 +178,9 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -192,7 +200,9 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "message": "68656c6c6f20776f726c64",
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
       "extra": 1
     }
     "#;
diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs
index 3e798f4b..b8b61b87 100644
--- a/frost-p256/src/lib.rs
+++ b/frost-p256/src/lib.rs
@@ -175,6 +175,14 @@ const CONTEXT_STRING: &str = "FROST-P256-SHA256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct P256Sha256;
 
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+pub type SigningParameters = ();
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<P256Sha256>;
+
 impl Ciphersuite for P256Sha256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -184,6 +192,8 @@ impl Ciphersuite for P256Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
+    type SigningParameters = ();
+
     /// H1 for FROST(P-256, SHA-256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.4-2.4.2.2
diff --git a/frost-p256/tests/serde_tests.rs b/frost-p256/tests/serde_tests.rs
index c1475814..23e7507f 100644
--- a/frost-p256/tests/serde_tests.rs
+++ b/frost-p256/tests/serde_tests.rs
@@ -112,7 +112,9 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -133,7 +135,9 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -153,7 +157,9 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -172,7 +178,9 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -192,7 +200,9 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "message": "68656c6c6f20776f726c64",
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
       "extra": 1
     }
     "#;
diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs
index 0929c228..3785d827 100644
--- a/frost-ristretto255/src/lib.rs
+++ b/frost-ristretto255/src/lib.rs
@@ -149,6 +149,14 @@ const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ristretto255Sha512;
 
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+pub type SigningParameters = ();
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<Ristretto255Sha512>;
+
 impl Ciphersuite for Ristretto255Sha512 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -158,6 +166,8 @@ impl Ciphersuite for Ristretto255Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
+    type SigningParameters = ();
+
     /// H1 for FROST(ristretto255, SHA-512)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.2-2.4.2.2
diff --git a/frost-ristretto255/tests/serde_tests.rs b/frost-ristretto255/tests/serde_tests.rs
index faf1769a..1bc680e4 100644
--- a/frost-ristretto255/tests/serde_tests.rs
+++ b/frost-ristretto255/tests/serde_tests.rs
@@ -112,7 +112,9 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -133,7 +135,9 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -153,7 +157,9 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -172,7 +178,9 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -192,7 +200,9 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "message": "68656c6c6f20776f726c64",
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
       "extra": 1
     }
     "#;
diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs
index 4d25266b..5489b48d 100644
--- a/frost-secp256k1/src/lib.rs
+++ b/frost-secp256k1/src/lib.rs
@@ -175,6 +175,14 @@ const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Secp256K1Sha256;
 
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+pub type SigningParameters = ();
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<Secp256K1Sha256>;
+
 impl Ciphersuite for Secp256K1Sha256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -184,6 +192,8 @@ impl Ciphersuite for Secp256K1Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
+    type SigningParameters = ();
+
     /// H1 for FROST(secp256k1, SHA-256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.5-2.4.2.2
diff --git a/frost-secp256k1/tests/serde_tests.rs b/frost-secp256k1/tests/serde_tests.rs
index 82a0735d..a092d930 100644
--- a/frost-secp256k1/tests/serde_tests.rs
+++ b/frost-secp256k1/tests/serde_tests.rs
@@ -112,7 +112,9 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -133,7 +135,9 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -153,7 +157,9 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -172,7 +178,9 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "message": "68656c6c6f20776f726c64"
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -192,7 +200,9 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "message": "68656c6c6f20776f726c64",
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
       "extra": 1
     }
     "#;

From f9264387e471fa90d70d0c3a68e992d3443e5497 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Wed, 2 Oct 2024 19:23:52 +0000
Subject: [PATCH 02/13] add frost-secp256k1-tr crate and ciphersuite

Co-authored by @zebra-lucky and @mimoo

This work sponsored by dlcbtc.com and lightspark.com
---
 Cargo.toml                                |   1 +
 frost-core/src/signature.rs               |   3 +-
 frost-secp256k1-tr/Cargo.toml             |  67 ++
 frost-secp256k1-tr/README.md              | 121 ++++
 frost-secp256k1-tr/dkg.md                 | 168 +++++
 frost-secp256k1-tr/src/keys/dkg.rs        | 103 +++
 frost-secp256k1-tr/src/keys/refresh.rs    |  35 +
 frost-secp256k1-tr/src/keys/repairable.rs | 101 +++
 frost-secp256k1-tr/src/lib.rs             | 755 ++++++++++++++++++++++
 9 files changed, 1353 insertions(+), 1 deletion(-)
 create mode 100644 frost-secp256k1-tr/Cargo.toml
 create mode 100644 frost-secp256k1-tr/README.md
 create mode 100644 frost-secp256k1-tr/dkg.md
 create mode 100644 frost-secp256k1-tr/src/keys/dkg.rs
 create mode 100644 frost-secp256k1-tr/src/keys/refresh.rs
 create mode 100644 frost-secp256k1-tr/src/keys/repairable.rs
 create mode 100644 frost-secp256k1-tr/src/lib.rs

diff --git a/Cargo.toml b/Cargo.toml
index cbf054ff..69771765 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ members = [
     "frost-p256",
     "frost-ristretto255",
     "frost-secp256k1",
+    "frost-secp256k1-tr",
     "frost-rerandomized",
     "gencode"
 ]
diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs
index 2d085493..f03d1e45 100644
--- a/frost-core/src/signature.rs
+++ b/frost-core/src/signature.rs
@@ -1,11 +1,12 @@
 //! Schnorr signatures over prime order groups (or subgroups)
 
 use alloc::{string::ToString, vec::Vec};
+use derive_getters::Getters;
 
 use crate::{Ciphersuite, Element, Error, Field, Group, Scalar};
 
 /// A Schnorr signature over some prime order group (or subgroup).
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Getters)]
 pub struct Signature<C: Ciphersuite> {
     /// The commitment `R` to the signature nonce.
     pub(crate) R: Element<C>,
diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml
new file mode 100644
index 00000000..ac0f26ae
--- /dev/null
+++ b/frost-secp256k1-tr/Cargo.toml
@@ -0,0 +1,67 @@
+[package]
+name = "frost-secp256k1-tr"
+edition = "2021"
+# When releasing to crates.io:
+# - Update CHANGELOG.md
+# - Create git tag.
+version = "2.0.0-rc.0"
+authors = [
+    "Deirdre Connolly <durumcrustulum@gmail.com>",
+    "Chelsea Komlo <me@chelseakomlo.com>",
+    "Conrado Gouvea <conradoplg@gmail.com>"
+]
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/ZcashFoundation/frost"
+categories = ["cryptography"]
+keywords = ["cryptography", "crypto", "threshold", "signature"]
+description = "A Schnorr signature scheme over the secp256k1 curve that supports FROST and Taproot."
+
+[package.metadata.docs.rs]
+features = ["serde"]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[dependencies]
+document-features = "0.2.7"
+frost-core = { path = "../frost-core", version = "2.0.0-rc.0", default-features = false }
+frost-rerandomized = { path = "../frost-rerandomized", version = "2.0.0-rc.0", default-features = false }
+k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"], default-features = false }
+serde = { version = "1.0.160", features = ["derive"], optional = true }
+rand_core = "0.6"
+sha2 = { version = "0.10.2", default-features = false }
+
+[dev-dependencies]
+criterion = "0.5"
+frost-core = { path = "../frost-core", version = "2.0.0-rc.0", features = ["test-impl"] }
+frost-rerandomized = { path = "../frost-rerandomized", version = "2.0.0-rc.0", features = ["test-impl"] }
+insta = { version = "1.31.0", features = ["yaml"] }
+hex = { version  = "0.4.3", default-features = false, features = ["alloc"] }
+lazy_static = "1.4"
+proptest = "1.0"
+rand = "0.8"
+rand_chacha = "0.3"
+serde_json = "1.0"
+
+[features]
+nightly = []
+default = ["serialization", "cheater-detection", "std"]
+#! ## Features
+## Enable standard library support.
+std = ["frost-core/std"]
+## Enable `serde` support for types that need to be communicated. You
+## can use `serde` to serialize structs with any encoder that supports
+## `serde` (e.g. JSON with `serde_json`).
+serde = ["frost-core/serde", "dep:serde"]
+## Enable a default serialization format. Enables `serde`.
+serialization = ["serde", "frost-core/serialization", "frost-rerandomized/serialization"]
+## Enable cheater detection
+cheater-detection = ["frost-core/cheater-detection", "frost-rerandomized/cheater-detection"]
+
+[lib]
+# Disables non-criterion benchmark which is not used; prevents errors
+# when using criterion-specific flags
+bench = false
+
+[[bench]]
+name = "bench"
+harness = false
diff --git a/frost-secp256k1-tr/README.md b/frost-secp256k1-tr/README.md
new file mode 100644
index 00000000..9022e25c
--- /dev/null
+++ b/frost-secp256k1-tr/README.md
@@ -0,0 +1,121 @@
+An implementation of Schnorr signatures on the secp256k1 curve for both single and threshold numbers
+of signers (FROST).
+
+## Example: key generation with trusted dealer and FROST signing
+
+Creating a key with a trusted dealer and splitting into shares; then signing a message
+and aggregating the signature. Note that the example just simulates a distributed
+scenario in a single thread and it abstracts away any communication between peers.
+
+
+```rust
+# // ANCHOR: tkg_gen
+use frost_secp256k1_tr as frost;
+use rand::thread_rng;
+use std::collections::BTreeMap;
+
+let mut rng = thread_rng();
+let max_signers = 5;
+let min_signers = 3;
+let (shares, pubkey_package) = frost::keys::generate_with_dealer(
+    max_signers,
+    min_signers,
+    frost::keys::IdentifierList::Default,
+    &mut rng,
+)?;
+# // ANCHOR_END: tkg_gen
+
+// Verifies the secret shares from the dealer and store them in a BTreeMap.
+// In practice, the KeyPackages must be sent to its respective participants
+// through a confidential and authenticated channel.
+let mut key_packages: BTreeMap<_, _> = BTreeMap::new();
+
+for (identifier, secret_share) in shares {
+    # // ANCHOR: tkg_verify
+    let key_package = frost::keys::KeyPackage::try_from(secret_share)?;
+    # // ANCHOR_END: tkg_verify
+    key_packages.insert(identifier, key_package);
+}
+
+let mut nonces_map = BTreeMap::new();
+let mut commitments_map = BTreeMap::new();
+
+////////////////////////////////////////////////////////////////////////////
+// Round 1: generating nonces and signing commitments for each participant
+////////////////////////////////////////////////////////////////////////////
+
+// In practice, each iteration of this loop will be executed by its respective participant.
+for participant_index in 1..=min_signers {
+    let participant_identifier = participant_index.try_into().expect("should be nonzero");
+    let key_package = &key_packages[&participant_identifier];
+    // Generate one (1) nonce and one SigningCommitments instance for each
+    // participant, up to _threshold_.
+    # // ANCHOR: round1_commit
+    let (nonces, commitments) = frost::round1::commit(
+        key_package.signing_share(),
+        &mut rng,
+    );
+    # // ANCHOR_END: round1_commit
+    // In practice, the nonces must be kept by the participant to use in the
+    // next round, while the commitment must be sent to the coordinator
+    // (or to every other participant if there is no coordinator) using
+    // an authenticated channel.
+    nonces_map.insert(participant_identifier, nonces);
+    commitments_map.insert(participant_identifier, commitments);
+}
+
+// This is what the signature aggregator / coordinator needs to do:
+// - decide what message to sign
+// - take one (unused) commitment per signing participant
+let mut signature_shares = BTreeMap::new();
+# // ANCHOR: round2_package
+let message = "message to sign".as_bytes();
+# // In practice, the SigningPackage must be sent to all participants
+# // involved in the current signing (at least min_signers participants),
+# // using an authenticate channel (and confidential if the message is secret).
+let signing_package = frost::SigningPackage::new(commitments_map, message);
+# // ANCHOR_END: round2_package
+
+////////////////////////////////////////////////////////////////////////////
+// Round 2: each participant generates their signature share
+////////////////////////////////////////////////////////////////////////////
+
+// In practice, each iteration of this loop will be executed by its respective participant.
+for participant_identifier in nonces_map.keys() {
+    let key_package = &key_packages[participant_identifier];
+
+    let nonces = &nonces_map[participant_identifier];
+
+    // Each participant generates their signature share.
+    # // ANCHOR: round2_sign
+    let signature_share = frost::round2::sign(&signing_package, nonces, key_package)?;
+    # // ANCHOR_END: round2_sign
+
+    // In practice, the signature share must be sent to the Coordinator
+    // using an authenticated channel.
+    signature_shares.insert(*participant_identifier, signature_share);
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Aggregation: collects the signing shares from all participants,
+// generates the final signature.
+////////////////////////////////////////////////////////////////////////////
+
+// Aggregate (also verifies the signature shares)
+# // ANCHOR: aggregate
+let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
+# // ANCHOR_END: aggregate
+
+
+// Check that the threshold signature can be verified by the group public
+// key (the verification key).
+# // ANCHOR: verify
+let is_signature_valid = pubkey_package
+    .verifying_key()
+    .verify(message, &group_signature)
+    .is_ok();
+# // ANCHOR_END: verify
+assert!(is_signature_valid);
+
+# Ok::<(), frost::Error>(())
+```
diff --git a/frost-secp256k1-tr/dkg.md b/frost-secp256k1-tr/dkg.md
new file mode 100644
index 00000000..217cbc99
--- /dev/null
+++ b/frost-secp256k1-tr/dkg.md
@@ -0,0 +1,168 @@
+# Distributed Key Generation (DKG)
+
+The DKG module supports generating FROST key shares in a distributed manner,
+without a trusted dealer.
+
+Before starting, each participant needs an unique identifier, which can be built from
+a `u16`. The process in which these identifiers are allocated is up to the application.
+
+The distributed key generation process has 3 parts, with 2 communication rounds
+between them, in which each participant needs to send a "package" to every other
+participant. In the first round, each participant sends the same package
+(a [`round1::Package`]) to every other. In the second round, each receiver gets
+their own package (a [`round2::Package`]).
+
+Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`]
+that MUST be kept secret. Between part 2 and 3, each participant needs to hold
+onto a [`round2::SecretPackage`].
+
+After the third part, each participant will get a [`KeyPackage`] with their
+long-term secret share that must be kept secret, and a [`PublicKeyPackage`]
+that is public (and will be the same between all participants). With those
+they can proceed to sign messages with FROST.
+
+
+## Example
+
+```rust
+# // ANCHOR: dkg_import
+use rand::thread_rng;
+use std::collections::BTreeMap;
+
+use frost_secp256k1_tr as frost;
+
+let mut rng = thread_rng();
+
+let max_signers = 5;
+let min_signers = 3;
+# // ANCHOR_END: dkg_import
+
+////////////////////////////////////////////////////////////////////////////
+// Key generation, Round 1
+////////////////////////////////////////////////////////////////////////////
+
+// Keep track of each participant's round 1 secret package.
+// In practice each participant will keep its copy; no one
+// will have all the participant's packages.
+let mut round1_secret_packages = BTreeMap::new();
+
+// Keep track of all round 1 packages sent to the given participant.
+// This is used to simulate the broadcast; in practice the packages
+// will be sent through some communication channel.
+let mut received_round1_packages = BTreeMap::new();
+
+// For each participant, perform the first part of the DKG protocol.
+// In practice, each participant will perform this on their own environments.
+for participant_index in 1..=max_signers {
+    let participant_identifier = participant_index.try_into().expect("should be nonzero");
+    # // ANCHOR: dkg_part1
+    let (round1_secret_package, round1_package) = frost::keys::dkg::part1(
+        participant_identifier,
+        max_signers,
+        min_signers,
+        &mut rng,
+    )?;
+    # // ANCHOR_END: dkg_part1
+
+    // Store the participant's secret package for later use.
+    // In practice each participant will store it in their own environment.
+    round1_secret_packages.insert(participant_identifier, round1_secret_package);
+
+    // "Send" the round 1 package to all other participants. In this
+    // test this is simulated using a BTreeMap; in practice this will be
+    // sent through some communication channel.
+    for receiver_participant_index in 1..=max_signers {
+        if receiver_participant_index == participant_index {
+            continue;
+        }
+        let receiver_participant_identifier: frost::Identifier = receiver_participant_index
+            .try_into()
+            .expect("should be nonzero");
+        received_round1_packages
+            .entry(receiver_participant_identifier)
+            .or_insert_with(BTreeMap::new)
+            .insert(participant_identifier, round1_package.clone());
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Key generation, Round 2
+////////////////////////////////////////////////////////////////////////////
+
+// Keep track of each participant's round 2 secret package.
+// In practice each participant will keep its copy; no one
+// will have all the participant's packages.
+let mut round2_secret_packages = BTreeMap::new();
+
+// Keep track of all round 2 packages sent to the given participant.
+// This is used to simulate the broadcast; in practice the packages
+// will be sent through some communication channel.
+let mut received_round2_packages = BTreeMap::new();
+
+// For each participant, perform the second part of the DKG protocol.
+// In practice, each participant will perform this on their own environments.
+for participant_index in 1..=max_signers {
+    let participant_identifier = participant_index.try_into().expect("should be nonzero");
+    let round1_secret_package = round1_secret_packages
+        .remove(&participant_identifier)
+        .unwrap();
+    let round1_packages = &received_round1_packages[&participant_identifier];
+    # // ANCHOR: dkg_part2
+    let (round2_secret_package, round2_packages) =
+        frost::keys::dkg::part2(round1_secret_package, round1_packages)?;
+    # // ANCHOR_END: dkg_part2
+
+    // Store the participant's secret package for later use.
+    // In practice each participant will store it in their own environment.
+    round2_secret_packages.insert(participant_identifier, round2_secret_package);
+
+    // "Send" the round 2 package to all other participants. In this
+    // test this is simulated using a BTreeMap; in practice this will be
+    // sent through some communication channel.
+    // Note that, in contrast to the previous part, here each other participant
+    // gets its own specific package.
+    for (receiver_identifier, round2_package) in round2_packages {
+        received_round2_packages
+            .entry(receiver_identifier)
+            .or_insert_with(BTreeMap::new)
+            .insert(participant_identifier, round2_package);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Key generation, final computation
+////////////////////////////////////////////////////////////////////////////
+
+// Keep track of each participant's long-lived key package.
+// In practice each participant will keep its copy; no one
+// will have all the participant's packages.
+let mut key_packages = BTreeMap::new();
+
+// Keep track of each participant's public key package.
+// In practice, if there is a Coordinator, only they need to store the set.
+// If there is not, then all candidates must store their own sets.
+// All participants will have the same exact public key package.
+let mut pubkey_packages = BTreeMap::new();
+
+// For each participant, perform the third part of the DKG protocol.
+// In practice, each participant will perform this on their own environments.
+for participant_index in 1..=max_signers {
+    let participant_identifier = participant_index.try_into().expect("should be nonzero");
+    let round2_secret_package = &round2_secret_packages[&participant_identifier];
+    let round1_packages = &received_round1_packages[&participant_identifier];
+    let round2_packages = &received_round2_packages[&participant_identifier];
+    # // ANCHOR: dkg_part3
+    let (key_package, pubkey_package) = frost::keys::dkg::part3(
+        round2_secret_package,
+        round1_packages,
+        round2_packages,
+    )?;
+    # // ANCHOR_END: dkg_part3
+    key_packages.insert(participant_identifier, key_package);
+    pubkey_packages.insert(participant_identifier, pubkey_package);
+}
+
+// With its own key package and the pubkey package, each participant can now proceed
+// to sign with FROST.
+# Ok::<(), frost::Error>(())
+```
diff --git a/frost-secp256k1-tr/src/keys/dkg.rs b/frost-secp256k1-tr/src/keys/dkg.rs
new file mode 100644
index 00000000..9ea40b26
--- /dev/null
+++ b/frost-secp256k1-tr/src/keys/dkg.rs
@@ -0,0 +1,103 @@
+#![doc = include_str!("../../dkg.md")]
+use super::*;
+
+/// DKG Round 1 structures.
+pub mod round1 {
+    use super::*;
+
+    /// The secret package that must be kept in memory by the participant
+    /// between the first and second parts of the DKG protocol (round 1).
+    ///
+    /// # Security
+    ///
+    /// This package MUST NOT be sent to other participants!
+    pub type SecretPackage = frost::keys::dkg::round1::SecretPackage<S>;
+
+    /// The package that must be broadcast by each participant to all other participants
+    /// between the first and second parts of the DKG protocol (round 1).
+    pub type Package = frost::keys::dkg::round1::Package<S>;
+}
+
+/// DKG Round 2 structures.
+pub mod round2 {
+    use super::*;
+
+    /// The secret package that must be kept in memory by the participant
+    /// between the second and third parts of the DKG protocol (round 2).
+    ///
+    /// # Security
+    ///
+    /// This package MUST NOT be sent to other participants!
+    pub type SecretPackage = frost::keys::dkg::round2::SecretPackage<S>;
+
+    /// A package that must be sent by each participant to some other participants
+    /// in Round 2 of the DKG protocol. Note that there is one specific package
+    /// for each specific recipient, in contrast to Round 1.
+    ///
+    /// # Security
+    ///
+    /// The package must be sent on an *confidential* and *authenticated* channel.
+    pub type Package = frost::keys::dkg::round2::Package<S>;
+}
+
+/// Performs the first part of the distributed key generation protocol
+/// for the given participant.
+///
+/// It returns the [`round1::SecretPackage`] that must be kept in memory
+/// by the participant for the other steps, and the [`round1::Package`] that
+/// must be sent to each other participant in the DKG run.
+pub fn part1<R: RngCore + CryptoRng>(
+    identifier: Identifier,
+    max_signers: u16,
+    min_signers: u16,
+    mut rng: R,
+) -> Result<(round1::SecretPackage, round1::Package), Error> {
+    frost::keys::dkg::part1(identifier, max_signers, min_signers, &mut rng)
+}
+
+/// Performs the second part of the distributed key generation protocol for the
+/// participant holding the given [`round1::SecretPackage`], given the received
+/// [`round1::Package`]s received from the other participants.
+///
+/// `round1_packages` maps the identifier of each other participant to the
+/// [`round1::Package`] they sent to the current participant (the owner of
+/// `secret_package`). These identifiers must come from whatever mapping the
+/// coordinator has between communication channels and participants, i.e. they
+/// must have assurance that the [`round1::Package`] came from the participant
+/// with that identifier.
+///
+/// It returns the [`round2::SecretPackage`] that must be kept in memory by the
+/// participant for the final step, and the map of [`round2::Package`]s that
+/// must be sent to each other participant who has the given identifier in the
+/// map key.
+pub fn part2(
+    secret_package: round1::SecretPackage,
+    round1_packages: &BTreeMap<Identifier, round1::Package>,
+) -> Result<(round2::SecretPackage, BTreeMap<Identifier, round2::Package>), Error> {
+    frost::keys::dkg::part2(secret_package, round1_packages)
+}
+
+/// Performs the third and final part of the distributed key generation protocol
+/// for the participant holding the given [`round2::SecretPackage`], given the
+/// received [`round1::Package`]s and [`round2::Package`]s received from the
+/// other participants.
+///
+/// `round1_packages` must be the same used in [`part2()`].
+///
+/// `round2_packages` maps the identifier of each other participant to the
+/// [`round2::Package`] they sent to the current participant (the owner of
+/// `secret_package`). These identifiers must come from whatever mapping the
+/// coordinator has between communication channels and participants, i.e. they
+/// must have assurance that the [`round2::Package`] came from the participant
+/// with that identifier.
+///
+/// It returns the [`KeyPackage`] that has the long-lived key share for the
+/// participant, and the [`PublicKeyPackage`]s that has public information about
+/// all participants; both of which are required to compute FROST signatures.
+pub fn part3(
+    round2_secret_package: &round2::SecretPackage,
+    round1_packages: &BTreeMap<Identifier, round1::Package>,
+    round2_packages: &BTreeMap<Identifier, round2::Package>,
+) -> Result<(KeyPackage, PublicKeyPackage), Error> {
+    frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
+}
diff --git a/frost-secp256k1-tr/src/keys/refresh.rs b/frost-secp256k1-tr/src/keys/refresh.rs
new file mode 100644
index 00000000..c270fc20
--- /dev/null
+++ b/frost-secp256k1-tr/src/keys/refresh.rs
@@ -0,0 +1,35 @@
+//! Refresh Shares
+//!
+//! Implements the functionality to refresh a share. This requires the participation
+//! of all the remaining signers. This can be done using a Trusted Dealer or
+//! DKG (not yet implemented)
+
+use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore};
+use alloc::vec::Vec;
+
+use super::{KeyPackage, PublicKeyPackage, SecretShare};
+
+/// Refreshes shares using a trusted dealer
+pub fn compute_refreshing_shares<C: Ciphersuite, R: RngCore + CryptoRng>(
+    old_pub_key_package: PublicKeyPackage,
+    max_signers: u16,
+    min_signers: u16,
+    identifiers: &[Identifier],
+    mut rng: &mut R,
+) -> Result<(Vec<SecretShare>, PublicKeyPackage), Error> {
+    frost::keys::refresh::compute_refreshing_shares(
+        old_pub_key_package,
+        max_signers,
+        min_signers,
+        identifiers,
+        &mut rng,
+    )
+}
+
+/// Each participant refreshed their shares
+pub fn refresh_share<C: Ciphersuite>(
+    zero_share: SecretShare,
+    current_share: &KeyPackage,
+) -> Result<KeyPackage, Error> {
+    frost::keys::refresh::refresh_share(zero_share, current_share)
+}
diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs
new file mode 100644
index 00000000..88bce01d
--- /dev/null
+++ b/frost-secp256k1-tr/src/keys/repairable.rs
@@ -0,0 +1,101 @@
+//! Repairable Threshold Scheme
+//!
+//! Implements the Repairable Threshold Scheme (RTS) from <https://eprint.iacr.org/2017/1155>.
+//! The RTS is used to help a signer (participant) repair their lost share. This is achieved
+//! using a subset of the other signers know here as `helpers`.
+
+use alloc::collections::BTreeMap;
+
+// This is imported separately to make `gencode` work.
+// (if it were below, the position of the import would vary between ciphersuites
+//  after `cargo fmt`)
+use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
+use crate::{Error, Secp256K1Sha256};
+
+use super::{SecretShare, VerifiableSecretSharingCommitment};
+
+/// Step 1 of RTS.
+///
+/// Generates the "delta" values from `helper_i` to help `participant` recover their share
+/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i`
+/// is the share of `helper_i`.
+///
+/// Returns a BTreeMap mapping which value should be sent to which participant.
+pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
+    helpers: &[Identifier],
+    share_i: &SecretShare,
+    rng: &mut R,
+    participant: Identifier,
+) -> Result<BTreeMap<Identifier, Scalar>, Error> {
+    frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
+}
+
+/// Step 2 of RTS.
+///
+/// Generates the `sigma` values from all `deltas` received from `helpers`
+/// to help `participant` recover their share.
+/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`.
+///
+/// Returns a scalar
+pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar {
+    frost::keys::repairable::repair_share_step_2::<Secp256K1Sha256>(deltas_j)
+}
+
+/// Step 3 of RTS
+///
+/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare`
+/// is made up of the `identifier`and `commitment` of the `participant` as well as the
+/// `value` which is the `SigningShare`.
+pub fn repair_share_step_3(
+    sigmas: &[Scalar],
+    identifier: Identifier,
+    commitment: &VerifiableSecretSharingCommitment,
+) -> SecretShare {
+    frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment)
+}
+
+#[cfg(test)]
+mod tests {
+
+    use lazy_static::lazy_static;
+    use rand::thread_rng;
+    use serde_json::Value;
+
+    use crate::Secp256K1Sha256;
+
+    lazy_static! {
+        pub static ref REPAIR_SHARE: Value =
+            serde_json::from_str(include_str!("../../tests/helpers/repair-share.json").trim())
+                .unwrap();
+    }
+
+    #[test]
+    fn check_repair_share_step_1() {
+        let rng = thread_rng();
+
+        frost_core::tests::repairable::check_repair_share_step_1::<Secp256K1Sha256, _>(rng);
+    }
+
+    #[test]
+    fn check_repair_share_step_2() {
+        frost_core::tests::repairable::check_repair_share_step_2::<Secp256K1Sha256>(&REPAIR_SHARE);
+    }
+
+    #[test]
+    fn check_repair_share_step_3() {
+        let rng = thread_rng();
+        frost_core::tests::repairable::check_repair_share_step_3::<Secp256K1Sha256, _>(
+            rng,
+            &REPAIR_SHARE,
+        );
+    }
+
+    #[test]
+    fn check_repair_share_step_1_fails_with_invalid_min_signers() {
+        let rng = thread_rng();
+        frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::<
+            Secp256K1Sha256,
+            _,
+        >(rng);
+    }
+}
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
new file mode 100644
index 00000000..e73333c5
--- /dev/null
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -0,0 +1,755 @@
+#![allow(non_snake_case)]
+#![deny(missing_docs)]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![doc = include_str!("../README.md")]
+#![doc = document_features::document_features!()]
+
+extern crate alloc;
+
+use alloc::borrow::ToOwned;
+use alloc::collections::BTreeMap;
+use alloc::vec::Vec;
+
+use frost_rerandomized::RandomizedCiphersuite;
+use k256::{
+    elliptic_curve::{
+        bigint::U256,
+        group::prime::PrimeCurveAffine,
+        hash2curve::{hash_to_field, ExpandMsgXmd},
+        point::AffineCoordinates,
+        sec1::{FromEncodedPoint, ToEncodedPoint},
+        Field as FFField, PrimeField, ScalarPrimitive,
+    },
+    AffinePoint, ProjectivePoint, Scalar,
+};
+use rand_core::{CryptoRng, RngCore};
+use sha2::{Digest, Sha256};
+
+use frost_core as frost;
+
+// TODO
+// #[cfg(test)]
+// mod tests;
+
+// Re-exports in our public API
+#[cfg(feature = "serde")]
+pub use frost_core::serde;
+pub use frost_core::{
+    Challenge, Ciphersuite, Element, Field, FieldError, Group, GroupCommitment, GroupError,
+};
+pub use rand_core;
+
+/// An error.
+pub type Error = frost_core::Error<Secp256K1Sha256>;
+
+/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field.
+#[derive(Clone, Copy)]
+pub struct Secp256K1ScalarField;
+
+impl Field for Secp256K1ScalarField {
+    type Scalar = Scalar;
+
+    type Serialization = [u8; 32];
+
+    fn zero() -> Self::Scalar {
+        Scalar::ZERO
+    }
+
+    fn one() -> Self::Scalar {
+        Scalar::ONE
+    }
+
+    fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
+        // [`Scalar`]'s Eq/PartialEq does a constant-time comparison
+        if *scalar == <Self as Field>::zero() {
+            Err(FieldError::InvalidZeroScalar)
+        } else {
+            Ok(scalar.invert().unwrap())
+        }
+    }
+
+    fn negate(scalar: &Self::Scalar) -> Self::Scalar {
+        -scalar
+    }
+
+    fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
+        Scalar::random(rng)
+    }
+
+    fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
+        scalar.to_bytes().into()
+    }
+
+    fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
+        let field_bytes: &k256::FieldBytes = buf.into();
+        match Scalar::from_repr(*field_bytes).into() {
+            Some(s) => Ok(s),
+            None => Err(FieldError::MalformedScalar),
+        }
+    }
+
+    fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
+        let mut array = Self::serialize(scalar);
+        array.reverse();
+        array
+    }
+}
+
+/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite group.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Secp256K1Group;
+
+impl Group for Secp256K1Group {
+    type Field = Secp256K1ScalarField;
+
+    type Element = ProjectivePoint;
+
+    /// [SEC 1][1] serialization of a compressed point in secp256k1 takes 33 bytes
+    /// (1-byte prefix and 32 bytes for the coordinate).
+    ///
+    /// Note that, in the SEC 1 spec, the identity is encoded as a single null byte;
+    /// but here we pad with zeroes. This is acceptable as the identity _should_ never
+    /// be serialized in FROST, else we error.
+    ///
+    /// [1]: https://secg.org/sec1-v2.pdf
+    type Serialization = [u8; 33];
+
+    fn cofactor() -> <Self::Field as Field>::Scalar {
+        Scalar::ONE
+    }
+
+    fn identity() -> Self::Element {
+        ProjectivePoint::IDENTITY
+    }
+
+    fn generator() -> Self::Element {
+        ProjectivePoint::GENERATOR
+    }
+
+    fn y_is_odd(element: &Self::Element) -> bool {
+        element.to_affine().y_is_odd().into()
+    }
+
+    fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
+        if *element == Self::identity() {
+            return Err(GroupError::InvalidIdentityElement);
+        }
+        let mut fixed_serialized = [0; 33];
+        let serialized_point = element.to_affine().to_encoded_point(true);
+        let serialized = serialized_point.as_bytes();
+        fixed_serialized.copy_from_slice(serialized);
+        Ok(fixed_serialized)
+    }
+
+    fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
+        let encoded_point =
+            k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
+
+        match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
+            Some(point) => {
+                if point.is_identity().into() {
+                    // This is actually impossible since the identity is encoded a a single byte
+                    // which will never happen since we receive a 33-byte buffer.
+                    // We leave the check for consistency.
+                    Err(GroupError::InvalidIdentityElement)
+                } else {
+                    Ok(ProjectivePoint::from(point))
+                }
+            }
+            None => Err(GroupError::MalformedElement),
+        }
+    }
+}
+
+fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] {
+    let mut h = Sha256::new();
+    for i in inputs {
+        h.update(i);
+    }
+    let mut output = [0u8; 32];
+    output.copy_from_slice(h.finalize().as_slice());
+    output
+}
+
+fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar {
+    let mut u = [Secp256K1ScalarField::zero()];
+    hash_to_field::<ExpandMsgXmd<Sha256>, Scalar>(&[msg], &[domain], &mut u)
+        .expect("should never return error according to error cases described in ExpandMsgXmd");
+    u[0]
+}
+
+/// Context string from the ciphersuite in the [spec].
+///
+/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-1
+const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-TR-v1";
+
+/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct Secp256K1Sha256;
+
+/// Digest the hasher to a Scalar
+fn hasher_to_scalar(hasher: Sha256) -> Scalar {
+    let sp = ScalarPrimitive::new(U256::from_be_slice(&hasher.finalize())).unwrap();
+    Scalar::from(&sp)
+}
+
+/// Create a BIP340 compliant tagged hash
+fn tagged_hash(tag: &str) -> Sha256 {
+    let mut hasher = Sha256::new();
+    let mut tag_hasher = Sha256::new();
+    tag_hasher.update(tag.as_bytes());
+    let tag_hash = tag_hasher.finalize();
+    hasher.update(tag_hash);
+    hasher.update(tag_hash);
+    hasher
+}
+
+/// Create a BIP341 compliant taproot tweak
+fn tweak<T: AsRef<[u8]>>(
+    public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
+    merkle_root: Option<T>,
+) -> Scalar {
+    match merkle_root {
+        None => Secp256K1ScalarField::zero(),
+        Some(root) => {
+            let mut hasher = tagged_hash("TapTweak");
+            hasher.update(public_key.to_affine().x());
+            hasher.update(root.as_ref());
+            hasher_to_scalar(hasher)
+        }
+    }
+}
+
+/// Create a BIP341 compliant tweaked public key
+fn tweaked_public_key<T: AsRef<[u8]>>(
+    public_key: &VerifyingKey,
+    merkle_root: Option<T>,
+) -> <<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element {
+    let mut pk = public_key.to_element();
+    if pk.to_affine().y_is_odd().into() {
+        pk = -pk;
+    }
+    ProjectivePoint::GENERATOR * tweak(&pk, merkle_root) + pk
+}
+
+/// The message target which the group's signature should commit to. Includes
+/// a message byte vector, and a set of ciphersuite-specific parameters.
+pub type SigningTarget = frost_core::SigningTarget<S>;
+
+/// The ciphersuite-specific signing parameters which are fed into
+/// signing code to ensure correctly compliant signatures are computed.
+#[derive(Debug, Clone, Eq, PartialEq, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct SigningParameters {
+    /// The tapscript merkle tree root which must be committed to and agreed upon
+    /// in advance by all participants in the signing round.
+    ///
+    /// If set to `None` (the default), then no taproot tweak will be committed to in the signature.
+    /// Best practice suggested by BIP341 is to commit to an empty merkle root in cases
+    /// where no tapscript tweak is needed, i.e. by supplying `&[0; u8]` as the merkle root.
+    /// This prevents hiding of taproot commitments inside a linearly aggregated key.
+    ///
+    /// However, for FROST, this is not strictly required as the group key cannot be
+    /// poisoned as long as the DKG procedure is conducted correctly.
+    /// Thus, the [`Default`] trait implementation of taproot `SigningParameters`
+    /// sets `tapscript_merkle_root` to `None`.
+    ///
+    /// If 3rd party observers outside the FROST group must be able to verify there
+    /// is no hidden script-spending path embedded in the FROST group's taproot output key,
+    /// then you should set `tapscript_merkle_root` to `Some(vec![])`, which proves
+    /// the tapscript commitment for the tweaked output key is unspendable.
+    pub tapscript_merkle_root: Option<Vec<u8>>,
+}
+
+impl frost_core::SigningParameters for SigningParameters {}
+
+impl Ciphersuite for Secp256K1Sha256 {
+    const ID: &'static str = CONTEXT_STRING;
+
+    type Group = Secp256K1Group;
+
+    type HashOutput = [u8; 32];
+
+    type SignatureSerialization = [u8; 64];
+
+    type SigningParameters = SigningParameters;
+
+    /// H1 for FROST(secp256k1, SHA-256)
+    ///
+    /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1
+    fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)
+    }
+
+    /// H2 for FROST(secp256k1, SHA-256)
+    ///
+    /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.2
+    fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        let mut hasher = tagged_hash("BIP0340/challenge");
+        hasher.update(m);
+        hasher_to_scalar(hasher)
+    }
+
+    /// H3 for FROST(secp256k1, SHA-256)
+    ///
+    /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.3
+    fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)
+    }
+
+    /// H4 for FROST(secp256k1, SHA-256)
+    ///
+    /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.4
+    fn H4(m: &[u8]) -> Self::HashOutput {
+        hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
+    }
+
+    /// H5 for FROST(secp256k1, SHA-256)
+    ///
+    /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.5
+    fn H5(m: &[u8]) -> Self::HashOutput {
+        hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
+    }
+
+    /// HDKG for FROST(secp256k1, SHA-256)
+    fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
+        Some(hash_to_scalar(
+            (CONTEXT_STRING.to_owned() + "dkg").as_bytes(),
+            m,
+        ))
+    }
+
+    /// HID for FROST(secp256k1, SHA-256)
+    fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
+        Some(hash_to_scalar(
+            (CONTEXT_STRING.to_owned() + "id").as_bytes(),
+            m,
+        ))
+    }
+
+    /// Generates the challenge as is required for Schnorr signatures.
+    fn challenge(
+        R: &Element<S>,
+        verifying_key: &VerifyingKey,
+        sig_target: &SigningTarget,
+    ) -> Result<Challenge<S>, Error> {
+        let mut preimage = vec![];
+        let tweaked_pk = tweaked_public_key(
+            &verifying_key,
+            sig_target.sig_params().tapscript_merkle_root.as_ref(),
+        );
+        preimage.extend_from_slice(&R.to_affine().x());
+        preimage.extend_from_slice(&tweaked_pk.to_affine().x());
+        preimage.extend_from_slice(sig_target.message().as_ref());
+        Ok(Challenge::from_scalar(S::H2(&preimage[..])))
+    }
+
+    /// Finalizes the signature by negating it depending on whether
+    /// the group [`VerifyingKey`] is even or odd parity.
+    fn aggregate_sig_finalize(
+        z_raw: <<Self::Group as Group>::Field as Field>::Scalar,
+        R: Element<Self>,
+        verifying_key: &VerifyingKey,
+        sig_target: &SigningTarget,
+    ) -> Result<Signature, Error> {
+        let challenge = Self::challenge(&R, verifying_key, &sig_target)?;
+
+        let t = tweak(
+            &verifying_key.to_element(),
+            sig_target.sig_params().tapscript_merkle_root.as_ref(),
+        );
+        let tc = t * challenge.clone().to_scalar();
+        let tweaked_pubkey = tweaked_public_key(
+            verifying_key,
+            sig_target.sig_params().tapscript_merkle_root.as_ref(),
+        );
+        let z_tweaked = if tweaked_pubkey.to_affine().y_is_odd().into() {
+            z_raw - tc
+        } else {
+            z_raw + tc
+        };
+        Ok(Signature::new(R, z_tweaked))
+    }
+
+    /// Finalize a single-signer BIP340 Schnorr signature.
+    fn single_sig_finalize(
+        k: <<Self::Group as Group>::Field as Field>::Scalar,
+        R: Element<Self>,
+        secret: <<Self::Group as Group>::Field as Field>::Scalar,
+        challenge: &Challenge<S>,
+        verifying_key: &VerifyingKey,
+        sig_params: &SigningParameters,
+    ) -> Signature {
+        let tweaked_pubkey =
+            tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref());
+        let c = challenge.clone().to_scalar();
+        let z = if tweaked_pubkey.to_affine().y_is_odd().into() {
+            k - (c * secret)
+        } else {
+            k + (c * secret)
+        };
+        Signature::new(R, z)
+    }
+
+    /// Serialize a signature in compact BIP340 format, with an x-only R point.
+    fn serialize_signature(signature: &Signature) -> Result<Vec<u8>, Error> {
+        let R_bytes = Self::Group::serialize(signature.R())?;
+        let z_bytes = <Self::Group as Group>::Field::serialize(signature.z());
+
+        let mut bytes = vec![0u8; 64];
+        bytes[..32].copy_from_slice(&R_bytes[1..]);
+        bytes[32..].copy_from_slice(&z_bytes);
+        Ok(bytes)
+    }
+
+    /// Deserialize a signature in compact BIP340 format, with an x-only R point.
+    fn deserialize_signature(bytes: &[u8]) -> Result<Signature, Error> {
+        if bytes.len() != 64 {
+            return Err(Error::MalformedSignature);
+        }
+
+        let mut R_bytes = [0u8; 33];
+        R_bytes[0] = 0x02; // taproot signatures always have an even R point
+        R_bytes[1..].copy_from_slice(&bytes[..32]);
+
+        let mut z_bytes = [0u8; 32];
+        z_bytes.copy_from_slice(&bytes[32..]);
+
+        let R = Self::Group::deserialize(&R_bytes)?;
+        let z = <Self::Group as Group>::Field::deserialize(&z_bytes)?;
+
+        Ok(Signature::new(R, z))
+    }
+
+    /// Compute a signature share, negating if required by BIP340.
+    fn compute_signature_share(
+        signer_nonces: &round1::SigningNonces,
+        binding_factor: frost::BindingFactor<S>,
+        group_commitment: GroupCommitment<S>,
+        lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
+        key_package: &frost::keys::KeyPackage<S>,
+        challenge: Challenge<S>,
+        sig_params: &SigningParameters,
+    ) -> round2::SignatureShare {
+        let mut sn = signer_nonces.clone();
+        if group_commitment.y_is_odd() {
+            sn.negate_nonces();
+        }
+
+        let mut kp = key_package.clone();
+        let public_key = key_package.verifying_key();
+        let pubkey_is_odd: bool = public_key.y_is_odd();
+        let tweaked_pubkey_is_odd: bool =
+            tweaked_public_key(public_key, sig_params.tapscript_merkle_root.as_ref())
+                .to_affine()
+                .y_is_odd()
+                .into();
+        if pubkey_is_odd != tweaked_pubkey_is_odd {
+            kp.negate_signing_share();
+        }
+
+        frost::round2::compute_signature_share(&sn, binding_factor, lambda_i, &kp, challenge)
+    }
+
+    /// Computes the effective pubkey point by tweaking the verifying key with a
+    /// provably unspendable taproot tweak.
+    fn effective_pubkey_element(
+        public_key: &VerifyingKey,
+        sig_params: &SigningParameters,
+    ) -> <Self::Group as Group>::Element {
+        let tweaked_pubkey =
+            tweaked_public_key(public_key, sig_params.tapscript_merkle_root.as_ref());
+        if Self::Group::y_is_odd(&tweaked_pubkey) {
+            -tweaked_pubkey
+        } else {
+            tweaked_pubkey
+        }
+    }
+
+    /// Ensures the nonce has an even Y coordinate.
+    fn effective_nonce_element(
+        R: <Self::Group as Group>::Element,
+    ) -> <Self::Group as Group>::Element {
+        if Self::Group::y_is_odd(&R) {
+            -R
+        } else {
+            R
+        }
+    }
+
+    /// Ensures the secret key is negated if the public key has odd parity.
+    fn effective_secret_key(
+        secret: <<Self::Group as Group>::Field as Field>::Scalar,
+        public_key: &VerifyingKey,
+        sig_params: &SigningParameters,
+    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        let t = tweak(
+            &public_key.to_element(),
+            sig_params.tapscript_merkle_root.as_ref(),
+        );
+        if Self::Group::y_is_odd(&public_key.to_element()) {
+            -secret + t
+        } else {
+            secret + t
+        }
+    }
+
+    /// Ensures the nonce secret is negated if the public nonce point has odd parity.
+    fn effective_nonce_secret(
+        nonce: <<Self::Group as Group>::Field as Field>::Scalar,
+        R: &Element<Self>,
+    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
+        if R.to_affine().y_is_odd().into() {
+            -nonce
+        } else {
+            nonce
+        }
+    }
+
+    /// Ensures the commitment share is negated if the group's commitment has odd parity.
+    fn effective_commitment_share(
+        group_commitment_share: frost::round1::GroupCommitmentShare<Self>,
+        group_commitment: &GroupCommitment<Self>,
+    ) -> Element<Self> {
+        if group_commitment
+            .clone()
+            .to_element()
+            .to_affine()
+            .y_is_odd()
+            .into()
+        {
+            -group_commitment_share.to_element()
+        } else {
+            group_commitment_share.to_element()
+        }
+    }
+
+    /// Calculate a verifying share compatible with taproot, depending on the parity
+    /// of the tweaked vs untweaked verifying key.
+    fn effective_verifying_share(
+        verifying_share: &keys::VerifyingShare,
+        verifying_key: &VerifyingKey,
+        sig_params: &SigningParameters,
+    ) -> <Self::Group as Group>::Element {
+        let pubkey_is_odd: bool = verifying_key.to_element().to_affine().y_is_odd().into();
+        let tweaked_pubkey_is_odd: bool =
+            tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref())
+                .to_affine()
+                .y_is_odd()
+                .into();
+
+        let vs = verifying_share.to_element();
+        if pubkey_is_odd != tweaked_pubkey_is_odd {
+            -vs
+        } else {
+            vs
+        }
+    }
+}
+
+impl RandomizedCiphersuite for Secp256K1Sha256 {
+    fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
+        Some(hash_to_scalar(
+            (CONTEXT_STRING.to_owned() + "randomizer").as_bytes(),
+            m,
+        ))
+    }
+}
+
+type S = Secp256K1Sha256;
+
+/// A FROST(secp256k1, SHA-256) participant identifier.
+pub type Identifier = frost::Identifier<S>;
+
+/// FROST(secp256k1, SHA-256) keys, key generation, key shares.
+pub mod keys {
+    use super::*;
+    use std::collections::BTreeMap;
+
+    /// The identifier list to use when generating key shares.
+    pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, S>;
+
+    /// Allows all participants' keys to be generated using a central, trusted
+    /// dealer.
+    pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
+        max_signers: u16,
+        min_signers: u16,
+        identifiers: IdentifierList,
+        mut rng: RNG,
+    ) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
+        frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
+    }
+
+    /// Splits an existing key into FROST shares.
+    ///
+    /// This is identical to [`generate_with_dealer`] but receives an existing key
+    /// instead of generating a fresh one. This is useful in scenarios where
+    /// the key needs to be generated externally or must be derived from e.g. a
+    /// seed phrase.
+    pub fn split<R: RngCore + CryptoRng>(
+        secret: &SigningKey,
+        max_signers: u16,
+        min_signers: u16,
+        identifiers: IdentifierList,
+        rng: &mut R,
+    ) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
+        frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
+    }
+
+    /// Recompute the secret from t-of-n secret shares using Lagrange interpolation.
+    ///
+    /// This can be used if for some reason the original key must be restored; e.g.
+    /// if threshold signing is not required anymore.
+    ///
+    /// This is NOT required to sign with FROST; the whole point of FROST is being
+    /// able to generate signatures only using the shares, without having to
+    /// reconstruct the original key.
+    ///
+    /// The caller is responsible for providing at least `min_signers` shares;
+    /// if less than that is provided, a different key will be returned.
+    pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
+        frost::keys::reconstruct(secret_shares)
+    }
+
+    /// Secret and public key material generated by a dealer performing
+    /// [`generate_with_dealer`].
+    ///
+    /// # Security
+    ///
+    /// To derive a FROST(secp256k1, SHA-256) keypair, the receiver of the [`SecretShare`] *must* call
+    /// .into(), which under the hood also performs validation.
+    pub type SecretShare = frost::keys::SecretShare<S>;
+
+    /// A secret scalar value representing a signer's share of the group secret.
+    pub type SigningShare = frost::keys::SigningShare<S>;
+
+    /// A public group element that represents a single signer's public verification share.
+    pub type VerifyingShare = frost::keys::VerifyingShare<S>;
+
+    /// A FROST(secp256k1, SHA-256) keypair, which can be generated either by a trusted dealer or using
+    /// a DKG.
+    ///
+    /// When using a central dealer, [`SecretShare`]s are distributed to
+    /// participants, who then perform verification, before deriving
+    /// [`KeyPackage`]s, which they store to later use during signing.
+    pub type KeyPackage = frost::keys::KeyPackage<S>;
+
+    /// Public data that contains all the signers' public keys as well as the
+    /// group public key.
+    ///
+    /// Used for verification purposes before publishing a signature.
+    pub type PublicKeyPackage = frost::keys::PublicKeyPackage<S>;
+
+    /// Contains the commitments to the coefficients for our secret polynomial _f_,
+    /// used to generate participants' key shares.
+    ///
+    /// [`VerifiableSecretSharingCommitment`] contains a set of commitments to the coefficients (which
+    /// themselves are scalars) for a secret polynomial f, where f is used to
+    /// generate each ith participant's key share f(i). Participants use this set of
+    /// commitments to perform verifiable secret sharing.
+    ///
+    /// Note that participants MUST be assured that they have the *same*
+    /// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using
+    /// some agreed-upon public location for publication, where each participant can
+    /// ensure that they received the correct (and same) value.
+    pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment<S>;
+
+    pub mod dkg;
+    pub mod repairable;
+}
+
+/// FROST(secp256k1, SHA-256) Round 1 functionality and types.
+pub mod round1 {
+    use crate::keys::SigningShare;
+
+    use super::*;
+
+    /// Comprised of FROST(secp256k1, SHA-256) hiding and binding nonces.
+    ///
+    /// Note that [`SigningNonces`] must be used *only once* for a signing
+    /// operation; re-using nonces will result in leakage of a signer's long-lived
+    /// signing key.
+    pub type SigningNonces = frost::round1::SigningNonces<S>;
+
+    /// Published by each participant in the first round of the signing protocol.
+    ///
+    /// This step can be batched if desired by the implementation. Each
+    /// SigningCommitment can be used for exactly *one* signature.
+    pub type SigningCommitments = frost::round1::SigningCommitments<S>;
+
+    /// A commitment to a signing nonce share.
+    pub type NonceCommitment = frost::round1::NonceCommitment<S>;
+
+    /// Performed once by each participant selected for the signing operation.
+    ///
+    /// Generates the signing nonces and commitments to be used in the signing
+    /// operation.
+    pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
+    where
+        RNG: CryptoRng + RngCore,
+    {
+        frost::round1::commit::<S, RNG>(secret, rng)
+    }
+}
+
+/// Generated by the coordinator of the signing operation and distributed to
+/// each signing party.
+pub type SigningPackage = frost::SigningPackage<S>;
+
+/// FROST(secp256k1, SHA-256) Round 2 functionality and types, for signature share generation.
+pub mod round2 {
+    use super::*;
+
+    /// A FROST(secp256k1, SHA-256) participant's signature share, which the Coordinator will aggregate with all other signer's
+    /// shares into the joint signature.
+    pub type SignatureShare = frost::round2::SignatureShare<S>;
+
+    /// Performed once by each participant selected for the signing operation.
+    ///
+    /// Receives the message to be signed and a set of signing commitments and a set
+    /// of randomizing commitments to be used in that signing operation, including
+    /// that for this participant.
+    ///
+    /// Assumes the participant has already determined which nonce corresponds with
+    /// the commitment that was assigned by the coordinator in the SigningPackage.
+    pub fn sign(
+        signing_package: &SigningPackage,
+        signer_nonces: &round1::SigningNonces,
+        key_package: &keys::KeyPackage,
+    ) -> Result<SignatureShare, Error> {
+        frost::round2::sign(signing_package, signer_nonces, key_package)
+    }
+}
+
+/// A Schnorr signature on FROST(secp256k1, SHA-256).
+pub type Signature = frost_core::Signature<S>;
+
+/// Verifies each FROST(secp256k1, SHA-256) participant's signature share, and if all are valid,
+/// aggregates the shares into a signature to publish.
+///
+/// Resulting signature is compatible with verification of a plain Schnorr
+/// signature.
+///
+/// This operation is performed by a coordinator that can communicate with all
+/// the signing participants before publishing the final signature. The
+/// coordinator can be one of the participants or a semi-trusted third party
+/// (who is trusted to not perform denial of service attacks, but does not learn
+/// any secret information). Note that because the coordinator is trusted to
+/// report misbehaving parties in order to avoid publishing an invalid
+/// signature, if the coordinator themselves is a signer and misbehaves, they
+/// can avoid that step. However, at worst, this results in a denial of
+/// service attack due to publishing an invalid signature.
+pub fn aggregate(
+    signing_package: &SigningPackage,
+    signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
+    pubkeys: &keys::PublicKeyPackage,
+) -> Result<Signature, Error> {
+    frost::aggregate(signing_package, signature_shares, pubkeys)
+}
+
+/// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256).
+pub type SigningKey = frost_core::SigningKey<S>;
+
+/// A valid verifying key for Schnorr signatures on FROST(secp256k1, SHA-256).
+pub type VerifyingKey = frost_core::VerifyingKey<S>;

From 11f43eb19f9e6e1b79cc5f997c098ea818f487f8 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Wed, 2 Oct 2024 20:37:03 +0000
Subject: [PATCH 03/13] test coverage for taproot crate

Co-authored by @zebra-lucky and @mimoo

This work sponsored by dlcbtc.com and lightspark.com
---
 frost-core/src/tests/ciphersuite_generic.rs   |  40 +-
 frost-core/src/tests/refresh.rs               |  13 +-
 frost-ed25519/tests/helpers/samples.json      |   3 +-
 frost-ed25519/tests/helpers/samples.rs        |  11 +-
 frost-ed25519/tests/integration_tests.rs      |  12 +-
 frost-ed25519/tests/interoperability_tests.rs |  10 +-
 frost-ed25519/tests/recreation_tests.rs       |   4 +-
 frost-ed448/tests/helpers/samples.json        |   3 +-
 frost-ed448/tests/helpers/samples.rs          |  11 +-
 frost-ed448/tests/integration_tests.rs        |  12 +-
 frost-ed448/tests/recreation_tests.rs         |   4 +-
 frost-p256/tests/helpers/samples.json         |   3 +-
 frost-p256/tests/helpers/samples.rs           |  11 +-
 frost-p256/tests/integration_tests.rs         |  11 +-
 frost-p256/tests/recreation_tests.rs          |   4 +-
 frost-ristretto255/tests/helpers/samples.json |   3 +-
 frost-ristretto255/tests/helpers/samples.rs   |  11 +-
 frost-ristretto255/tests/integration_tests.rs |  12 +-
 frost-ristretto255/tests/recreation_tests.rs  |   4 +-
 frost-secp256k1-tr/benches/bench.rs           |  19 +
 frost-secp256k1-tr/src/lib.rs                 |   5 +-
 frost-secp256k1-tr/src/tests.rs               |   5 +
 frost-secp256k1-tr/src/tests/batch.rs         |  24 +
 .../src/tests/coefficient_commitment.rs       |  46 ++
 frost-secp256k1-tr/src/tests/deserialize.rs   |  38 ++
 frost-secp256k1-tr/src/tests/proptests.rs     |  33 +
 .../src/tests/vss_commitment.rs               |  38 ++
 .../tests/common_traits_tests.rs              |  74 ++
 .../tests/helpers/elements.json               |   5 +
 frost-secp256k1-tr/tests/helpers/mod.rs       |   5 +
 .../tests/helpers/repair-share.json           |  15 +
 frost-secp256k1-tr/tests/helpers/samples.json |   7 +
 frost-secp256k1-tr/tests/helpers/samples.rs   | 128 ++++
 .../tests/helpers/vectors-big-identifier.json |  77 +++
 frost-secp256k1-tr/tests/helpers/vectors.json |  77 +++
 .../tests/helpers/vectors_dkg.json            |  51 ++
 frost-secp256k1-tr/tests/integration_tests.rs | 363 ++++++++++
 frost-secp256k1-tr/tests/recreation_tests.rs  | 133 ++++
 .../tests/rerandomized_tests.rs               |  10 +
 frost-secp256k1-tr/tests/serde_tests.rs       | 642 ++++++++++++++++++
 .../tests/serialization_tests.rs              | 105 +++
 ...ck_key_package_postcard_serialization.snap |   5 +
 ...ic_key_package_postcard_serialization.snap |   5 +
 ...round1_package_postcard_serialization.snap |   5 +
 ...round2_package_postcard_serialization.snap |   5 +
 ...k_secret_share_postcard_serialization.snap |   5 +
 ...ignature_share_postcard_serialization.snap |   5 +
 ...ng_commitments_postcard_serialization.snap |   5 +
 ...signing_nonces_postcard_serialization.snap |   5 +
 ...igning_package_postcard_serialization.snap |   5 +
 frost-secp256k1-tr/tests/tweaking_tests.rs    |  89 +++
 frost-secp256k1/tests/helpers/samples.json    |   3 +-
 frost-secp256k1/tests/helpers/samples.rs      |  11 +-
 frost-secp256k1/tests/integration_tests.rs    |  12 +-
 frost-secp256k1/tests/recreation_tests.rs     |   4 +-
 gencode/src/main.rs                           |  29 +-
 56 files changed, 2180 insertions(+), 95 deletions(-)
 create mode 100644 frost-secp256k1-tr/benches/bench.rs
 create mode 100644 frost-secp256k1-tr/src/tests.rs
 create mode 100644 frost-secp256k1-tr/src/tests/batch.rs
 create mode 100644 frost-secp256k1-tr/src/tests/coefficient_commitment.rs
 create mode 100644 frost-secp256k1-tr/src/tests/deserialize.rs
 create mode 100644 frost-secp256k1-tr/src/tests/proptests.rs
 create mode 100644 frost-secp256k1-tr/src/tests/vss_commitment.rs
 create mode 100644 frost-secp256k1-tr/tests/common_traits_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/helpers/elements.json
 create mode 100644 frost-secp256k1-tr/tests/helpers/mod.rs
 create mode 100644 frost-secp256k1-tr/tests/helpers/repair-share.json
 create mode 100644 frost-secp256k1-tr/tests/helpers/samples.json
 create mode 100644 frost-secp256k1-tr/tests/helpers/samples.rs
 create mode 100644 frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json
 create mode 100644 frost-secp256k1-tr/tests/helpers/vectors.json
 create mode 100644 frost-secp256k1-tr/tests/helpers/vectors_dkg.json
 create mode 100644 frost-secp256k1-tr/tests/integration_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/recreation_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/rerandomized_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/serde_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/serialization_tests.rs
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_nonces_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
 create mode 100644 frost-secp256k1-tr/tests/tweaking_tests.rs

diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs
index 4528e9b7..f5332273 100644
--- a/frost-core/src/tests/ciphersuite_generic.rs
+++ b/frost-core/src/tests/ciphersuite_generic.rs
@@ -7,9 +7,8 @@ use crate as frost;
 use crate::round2::SignatureShare;
 use crate::{
     keys::PublicKeyPackage, Error, Field, Group, Identifier, Signature, SigningKey, SigningPackage,
-    VerifyingKey,
+    SigningTarget, VerifyingKey,
 };
-use alloc::borrow::ToOwned;
 use alloc::vec::Vec;
 use rand_core::{CryptoRng, RngCore};
 
@@ -103,7 +102,8 @@ pub fn check_share_generation_fails_with_invalid_signers<C: Ciphersuite, R: RngC
 /// Test FROST signing with trusted dealer with a Ciphersuite.
 pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
     mut rng: R,
-) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
+    signing_target: SigningTarget<C>,
+) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>) {
     ////////////////////////////////////////////////////////////////////////////
     // Key generation
     ////////////////////////////////////////////////////////////////////////////
@@ -147,10 +147,11 @@ pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
             .collect(),
         &mut rng,
         pubkeys.clone(),
+        signing_target.clone(),
     );
     assert_eq!(r, Err(Error::InvalidSignature));
 
-    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
 }
 
 /// Test FROST signing with trusted dealer fails with invalid numbers of signers.
@@ -195,7 +196,8 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     key_packages: BTreeMap<frost::Identifier<C>, frost::keys::KeyPackage<C>>,
     mut rng: R,
     pubkey_package: PublicKeyPackage<C>,
-) -> Result<(Vec<u8>, Signature<C>, VerifyingKey<C>), Error<C>> {
+    signing_target: SigningTarget<C>,
+) -> Result<(SigningTarget<C>, Signature<C>, VerifyingKey<C>), Error<C>> {
     let mut nonces_map: BTreeMap<frost::Identifier<C>, frost::round1::SigningNonces<C>> =
         BTreeMap::new();
     let mut commitments_map: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
@@ -223,8 +225,7 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     // - decide what message to sign
     // - take one (unused) commitment per signing participant
     let mut signature_shares = BTreeMap::new();
-    let message = "message to sign".as_bytes();
-    let signing_package = SigningPackage::new(commitments_map, message);
+    let signing_package = frost::SigningPackage::new(commitments_map, signing_target.clone());
 
     ////////////////////////////////////////////////////////////////////////////
     // Round 2: each participant generates their signature share
@@ -266,11 +267,18 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     // Aggregate (also verifies the signature shares)
     let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
 
+    // Check that the effective verifying key can be verified against the raw message,
+    // without exposing the SigningParameters.
+    pubkey_package
+        .verifying_key
+        .effective_key(signing_target.sig_params())
+        .verify(signing_target.message(), &group_signature)?;
+
     // Check that the threshold signature can be verified by the group public
     // key (the verification key).
     pubkey_package
         .verifying_key
-        .verify(message, &group_signature)?;
+        .verify(signing_target.clone(), &group_signature)?;
 
     // Check that the threshold signature can be verified by the group public
     // key (the verification key) from KeyPackage.verifying_key
@@ -279,11 +287,11 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
 
         key_package
             .verifying_key
-            .verify(message, &group_signature)?;
+            .verify(signing_target.clone(), &group_signature)?;
     }
 
     Ok((
-        message.to_owned(),
+        signing_target,
         group_signature,
         pubkey_package.verifying_key,
     ))
@@ -303,7 +311,7 @@ fn check_sign_errors<C: Ciphersuite + PartialEq>(
         .find(|&&id| id != key_package.identifier)
         .unwrap();
     commitments.remove(&id);
-    let signing_package = frost::SigningPackage::new(commitments, signing_package.message());
+    let signing_package = frost::SigningPackage::new(commitments, signing_package.sig_target);
 
     let r = frost::round2::sign(&signing_package, &signing_nonces, &key_package);
     assert_eq!(r, Err(Error::IncorrectNumberOfCommitments));
@@ -376,7 +384,8 @@ fn check_aggregate_invalid_share_identifier_for_verifying_shares<C: Ciphersuite
 /// Test FROST signing with DKG with a Ciphersuite.
 pub fn check_sign_with_dkg<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     mut rng: R,
-) -> (Vec<u8>, Signature<C>, VerifyingKey<C>)
+    signing_target: SigningTarget<C>,
+) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>)
 where
     C::Group: core::cmp::PartialEq,
 {
@@ -533,7 +542,7 @@ where
     let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap());
 
     // Proceed with the signing test.
-    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
 }
 
 /// Check that calling dkg::part3() with distinct sets of participants fail.
@@ -577,7 +586,8 @@ fn check_part3_different_participants<C: Ciphersuite>(
 /// Identifiers.
 pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + CryptoRng>(
     mut rng: R,
-) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
+    signing_target: SigningTarget<C>,
+) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>) {
     // Check error cases first
     // Check repeated identifiers
 
@@ -643,7 +653,7 @@ pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + Crypt
         let key_package = frost::keys::KeyPackage::try_from(v).unwrap();
         key_packages.insert(k, key_package);
     }
-    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
 }
 
 fn check_part2_error<C: Ciphersuite>(
diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs
index 29330b90..940b0f53 100644
--- a/frost-core/src/tests/refresh.rs
+++ b/frost-core/src/tests/refresh.rs
@@ -9,7 +9,7 @@ use crate::keys::refresh::{compute_refreshing_shares, refresh_share};
 use crate::{self as frost};
 use crate::{
     keys::{KeyPackage, PublicKeyPackage, SecretShare},
-    Ciphersuite, Error, Identifier,
+    Ciphersuite, Error, Identifier, SigningTarget,
 };
 
 use super::ciphersuite_generic::check_sign;
@@ -81,7 +81,16 @@ pub fn check_refresh_shares_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
     for (k, v) in new_shares {
         key_packages.insert(k, v.unwrap());
     }
-    check_sign(MIN_SIGNERS, key_packages, rng, new_pub_key_package).unwrap();
+
+    let signing_target = SigningTarget::from_message(b"hello world");
+    check_sign(
+        MIN_SIGNERS,
+        key_packages,
+        rng,
+        new_pub_key_package,
+        signing_target,
+    )
+    .unwrap();
 }
 
 /// We want to check that shares are refreshed with valid signers
diff --git a/frost-ed25519/tests/helpers/samples.json b/frost-ed25519/tests/helpers/samples.json
index 3402fbe7..a61e0c4b 100644
--- a/frost-ed25519/tests/helpers/samples.json
+++ b/frost-ed25519/tests/helpers/samples.json
@@ -1,6 +1,7 @@
 {
     "identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
+    "proof_of_knowledge": "5866666666666666666666666666666666666666666666666666666666666666498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
     "element1": "5866666666666666666666666666666666666666666666666666666666666666",
     "element2": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
     "scalar1": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a"
-}
\ No newline at end of file
+}
diff --git a/frost-ed25519/tests/helpers/samples.rs b/frost-ed25519/tests/helpers/samples.rs
index 6f22aed3..4d785e5c 100644
--- a/frost-ed25519/tests/helpers/samples.rs
+++ b/frost-ed25519/tests/helpers/samples.rs
@@ -109,17 +109,12 @@ pub fn public_key_package() -> PublicKeyPackage {
 
 /// Generate a sample round1::Package.
 pub fn round1_package() -> round1::Package {
-    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
     let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
-    let serialized_signature = serialized_element
-        .as_ref()
-        .iter()
-        .chain(serialized_scalar.as_ref().iter())
-        .cloned()
-        .collect::<Vec<u8>>();
     let vss_commitment =
         VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
-    let signature = Signature::deserialize(&serialized_signature).unwrap();
 
     round1::Package::new(vss_commitment, signature)
 }
diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs
index 6c564788..320b8707 100644
--- a/frost-ed25519/tests/integration_tests.rs
+++ b/frost-ed25519/tests/integration_tests.rs
@@ -12,7 +12,10 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -184,7 +187,10 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -336,7 +342,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ed25519Sha512,
         _,
-    >(rng);
+    >(rng, b"message".into());
 }
 
 #[test]
diff --git a/frost-ed25519/tests/interoperability_tests.rs b/frost-ed25519/tests/interoperability_tests.rs
index 9c27193f..10c7032b 100644
--- a/frost-ed25519/tests/interoperability_tests.rs
+++ b/frost-ed25519/tests/interoperability_tests.rs
@@ -12,12 +12,13 @@ fn check_interoperability_in_sign_with_dkg() {
     // and the interoperability check. A smaller number of iterations is used
     // because DKG takes longer and otherwise the test would be too slow.
     for _ in 0..32 {
-        let (msg, group_signature, group_pubkey) =
+        let (target, group_signature, group_pubkey) =
             frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(
                 rng.clone(),
+                b"message".into(),
             );
 
-        helpers::verify_signature(&msg, group_signature, group_pubkey);
+        helpers::verify_signature(target.message(), group_signature, group_pubkey);
     }
 }
 
@@ -28,13 +29,14 @@ fn check_interoperability_in_sign_with_dealer() {
     // Test with multiple keys/signatures to better exercise the key generation
     // and the interoperability check.
     for _ in 0..256 {
-        let (msg, group_signature, group_pubkey) =
+        let (target, group_signature, group_pubkey) =
             frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(
                 rng.clone(),
+                b"message".into(),
             );
 
         // Check that the threshold signature can be verified by the `ed25519_dalek` crate
         // public key (interoperability test)
-        helpers::verify_signature(&msg, group_signature, group_pubkey);
+        helpers::verify_signature(target.message(), group_signature, group_pubkey);
     }
 }
diff --git a/frost-ed25519/tests/recreation_tests.rs b/frost-ed25519/tests/recreation_tests.rs
index 0b1d44f1..3bdb64ba 100644
--- a/frost-ed25519/tests/recreation_tests.rs
+++ b/frost-ed25519/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let message = signing_package.message();
+    let sig_target = signing_package.sig_target();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), message);
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-ed448/tests/helpers/samples.json b/frost-ed448/tests/helpers/samples.json
index 36e86286..f93c3e9c 100644
--- a/frost-ed448/tests/helpers/samples.json
+++ b/frost-ed448/tests/helpers/samples.json
@@ -1,6 +1,7 @@
 {
     "identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+    "proof_of_knowledge": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f69004d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
     "element1": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
     "element2": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
     "scalar1": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00"
-}
\ No newline at end of file
+}
diff --git a/frost-ed448/tests/helpers/samples.rs b/frost-ed448/tests/helpers/samples.rs
index ec556736..afb26d50 100644
--- a/frost-ed448/tests/helpers/samples.rs
+++ b/frost-ed448/tests/helpers/samples.rs
@@ -109,17 +109,12 @@ pub fn public_key_package() -> PublicKeyPackage {
 
 /// Generate a sample round1::Package.
 pub fn round1_package() -> round1::Package {
-    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
     let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
-    let serialized_signature = serialized_element
-        .as_ref()
-        .iter()
-        .chain(serialized_scalar.as_ref().iter())
-        .cloned()
-        .collect::<Vec<u8>>();
     let vss_commitment =
         VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
-    let signature = Signature::deserialize(&serialized_signature).unwrap();
 
     round1::Package::new(vss_commitment, signature)
 }
diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs
index 70061503..eaf4336b 100644
--- a/frost-ed448/tests/integration_tests.rs
+++ b/frost-ed448/tests/integration_tests.rs
@@ -12,7 +12,10 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed448Shake256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed448Shake256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -184,7 +187,10 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed448Shake256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed448Shake256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -336,7 +342,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ed448Shake256,
         _,
-    >(rng);
+    >(rng, b"message".into());
 }
 
 #[test]
diff --git a/frost-ed448/tests/recreation_tests.rs b/frost-ed448/tests/recreation_tests.rs
index e51f8b6e..0f0c14b4 100644
--- a/frost-ed448/tests/recreation_tests.rs
+++ b/frost-ed448/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let message = signing_package.message();
+    let sig_target = signing_package.sig_target();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), message);
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-p256/tests/helpers/samples.json b/frost-p256/tests/helpers/samples.json
index 928e355c..3fe4c698 100644
--- a/frost-p256/tests/helpers/samples.json
+++ b/frost-p256/tests/helpers/samples.json
@@ -1,6 +1,7 @@
 {
     "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+    "proof_of_knowledge": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
     "element1": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
     "element2": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
     "scalar1": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1"
-}
\ No newline at end of file
+}
diff --git a/frost-p256/tests/helpers/samples.rs b/frost-p256/tests/helpers/samples.rs
index 340bc763..432af475 100644
--- a/frost-p256/tests/helpers/samples.rs
+++ b/frost-p256/tests/helpers/samples.rs
@@ -109,17 +109,12 @@ pub fn public_key_package() -> PublicKeyPackage {
 
 /// Generate a sample round1::Package.
 pub fn round1_package() -> round1::Package {
-    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
     let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
-    let serialized_signature = serialized_element
-        .as_ref()
-        .iter()
-        .chain(serialized_scalar.as_ref().iter())
-        .cloned()
-        .collect::<Vec<u8>>();
     let vss_commitment =
         VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
-    let signature = Signature::deserialize(&serialized_signature).unwrap();
 
     round1::Package::new(vss_commitment, signature)
 }
diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs
index 8d44312d..c1c9347e 100644
--- a/frost-p256/tests/integration_tests.rs
+++ b/frost-p256/tests/integration_tests.rs
@@ -12,7 +12,10 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<P256Sha256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<P256Sha256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -184,7 +187,10 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<P256Sha256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<P256Sha256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -333,6 +339,7 @@ fn check_sign_with_dealer_and_identifiers() {
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<P256Sha256, _>(
         rng,
+        b"message".into(),
     );
 }
 
diff --git a/frost-p256/tests/recreation_tests.rs b/frost-p256/tests/recreation_tests.rs
index a24e57f5..0a96090f 100644
--- a/frost-p256/tests/recreation_tests.rs
+++ b/frost-p256/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let message = signing_package.message();
+    let sig_target = signing_package.sig_target();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), message);
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-ristretto255/tests/helpers/samples.json b/frost-ristretto255/tests/helpers/samples.json
index bb80d1b8..1fff1337 100644
--- a/frost-ristretto255/tests/helpers/samples.json
+++ b/frost-ristretto255/tests/helpers/samples.json
@@ -1,6 +1,7 @@
 {
     "identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
+    "proof_of_knowledge": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
     "element1": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
     "element2": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
     "scalar1": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a"
-}
\ No newline at end of file
+}
diff --git a/frost-ristretto255/tests/helpers/samples.rs b/frost-ristretto255/tests/helpers/samples.rs
index f598c53f..da9e9919 100644
--- a/frost-ristretto255/tests/helpers/samples.rs
+++ b/frost-ristretto255/tests/helpers/samples.rs
@@ -109,17 +109,12 @@ pub fn public_key_package() -> PublicKeyPackage {
 
 /// Generate a sample round1::Package.
 pub fn round1_package() -> round1::Package {
-    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
     let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
-    let serialized_signature = serialized_element
-        .as_ref()
-        .iter()
-        .chain(serialized_scalar.as_ref().iter())
-        .cloned()
-        .collect::<Vec<u8>>();
     let vss_commitment =
         VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
-    let signature = Signature::deserialize(&serialized_signature).unwrap();
 
     round1::Package::new(vss_commitment, signature)
 }
diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs
index af536ac3..307d5762 100644
--- a/frost-ristretto255/tests/integration_tests.rs
+++ b/frost-ristretto255/tests/integration_tests.rs
@@ -12,7 +12,10 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ristretto255Sha512, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ristretto255Sha512, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -185,7 +188,10 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ristretto255Sha512, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ristretto255Sha512, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -337,7 +343,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ristretto255Sha512,
         _,
-    >(rng);
+    >(rng, b"message".into());
 }
 
 #[test]
diff --git a/frost-ristretto255/tests/recreation_tests.rs b/frost-ristretto255/tests/recreation_tests.rs
index a8ed937c..ab06c93a 100644
--- a/frost-ristretto255/tests/recreation_tests.rs
+++ b/frost-ristretto255/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let message = signing_package.message();
+    let sig_target = signing_package.sig_target();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), message);
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-secp256k1-tr/benches/bench.rs b/frost-secp256k1-tr/benches/bench.rs
new file mode 100644
index 00000000..3d51c3f1
--- /dev/null
+++ b/frost-secp256k1-tr/benches/bench.rs
@@ -0,0 +1,19 @@
+use criterion::{criterion_group, criterion_main, Criterion};
+use rand::thread_rng;
+
+use frost_secp256k1_tr::*;
+
+fn bench_secp256k1_batch_verify(c: &mut Criterion) {
+    let mut rng = thread_rng();
+
+    frost_core::benches::bench_batch_verify::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
+}
+
+fn bench_secp256k1_sign(c: &mut Criterion) {
+    let mut rng = thread_rng();
+
+    frost_core::benches::bench_sign::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
+}
+
+criterion_group!(benches, bench_secp256k1_batch_verify, bench_secp256k1_sign);
+criterion_main!(benches);
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index e73333c5..7cf3b1ea 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -28,9 +28,8 @@ use sha2::{Digest, Sha256};
 
 use frost_core as frost;
 
-// TODO
-// #[cfg(test)]
-// mod tests;
+#[cfg(test)]
+mod tests;
 
 // Re-exports in our public API
 #[cfg(feature = "serde")]
diff --git a/frost-secp256k1-tr/src/tests.rs b/frost-secp256k1-tr/src/tests.rs
new file mode 100644
index 00000000..15a3e184
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests.rs
@@ -0,0 +1,5 @@
+mod batch;
+mod coefficient_commitment;
+mod deserialize;
+mod proptests;
+mod vss_commitment;
diff --git a/frost-secp256k1-tr/src/tests/batch.rs b/frost-secp256k1-tr/src/tests/batch.rs
new file mode 100644
index 00000000..b87d22a9
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests/batch.rs
@@ -0,0 +1,24 @@
+use rand::thread_rng;
+
+use crate::*;
+
+#[test]
+fn check_batch_verify() {
+    let rng = thread_rng();
+
+    frost_core::tests::batch::batch_verify::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_bad_batch_verify() {
+    let rng = thread_rng();
+
+    frost_core::tests::batch::bad_batch_verify::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn empty_batch_verify() {
+    let rng = thread_rng();
+
+    frost_core::tests::batch::empty_batch_verify::<Secp256K1Sha256, _>(rng);
+}
diff --git a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs
new file mode 100644
index 00000000..d1b6c22c
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs
@@ -0,0 +1,46 @@
+use lazy_static::lazy_static;
+use rand::thread_rng;
+use serde_json::Value;
+
+use crate::*;
+
+// Tests for serialization and deserialization of CoefficientCommitment
+
+lazy_static! {
+    pub static ref ELEMENTS: Value =
+        serde_json::from_str(include_str!("../../tests/helpers/elements.json").trim()).unwrap();
+}
+
+#[test]
+fn check_serialization_of_coefficient_commitment() {
+    let rng = thread_rng();
+    frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::<
+        Secp256K1Sha256,
+        _,
+    >(rng);
+}
+
+#[test]
+fn check_create_coefficient_commitment() {
+    let rng = thread_rng();
+    frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::<
+        Secp256K1Sha256,
+        _,
+    >(rng);
+}
+#[test]
+fn check_create_coefficient_commitment_error() {
+    frost_core::tests::coefficient_commitment::check_create_coefficient_commitment_error::<
+        Secp256K1Sha256,
+    >(&ELEMENTS);
+}
+
+#[test]
+fn check_get_value_of_coefficient_commitment() {
+    let rng = thread_rng();
+
+    frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::<
+        Secp256K1Sha256,
+        _,
+    >(rng);
+}
diff --git a/frost-secp256k1-tr/src/tests/deserialize.rs b/frost-secp256k1-tr/src/tests/deserialize.rs
new file mode 100644
index 00000000..a744832b
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests/deserialize.rs
@@ -0,0 +1,38 @@
+use crate::*;
+
+#[test]
+fn check_deserialize_non_canonical() {
+    let mut encoded_generator = <Secp256K1Sha256 as Ciphersuite>::Group::serialize(
+        &<Secp256K1Sha256 as Ciphersuite>::Group::generator(),
+    )
+    .unwrap();
+
+    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_generator);
+    assert!(r.is_ok());
+
+    // The first byte should be 0x02 or 0x03. Set other value to
+    // create a non-canonical encoding.
+    encoded_generator[0] = 0xFF;
+    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_generator);
+    assert_eq!(r, Err(GroupError::MalformedElement));
+
+    // Besides the first byte, it is still possible to get non-canonical encodings.
+    // This is x = p + 2 which is non-canonical and maps to a valid prime-order point.
+    let encoded_point =
+        hex::decode("02fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31")
+            .unwrap()
+            .try_into()
+            .unwrap();
+    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_point);
+    assert_eq!(r, Err(GroupError::MalformedElement));
+}
+
+#[test]
+fn check_deserialize_identity() {
+    // The identity is actually encoded as a single byte; but the API does not
+    // allow us to change that. Try to send something similar.
+    let encoded_identity = [0u8; 33];
+
+    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_identity);
+    assert_eq!(r, Err(GroupError::MalformedElement));
+}
diff --git a/frost-secp256k1-tr/src/tests/proptests.rs b/frost-secp256k1-tr/src/tests/proptests.rs
new file mode 100644
index 00000000..dd598569
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests/proptests.rs
@@ -0,0 +1,33 @@
+use crate::*;
+use frost_core::tests::proptests::{tweak_strategy, SignatureCase};
+use proptest::prelude::*;
+
+use rand_chacha::ChaChaRng;
+use rand_core::SeedableRng;
+
+proptest! {
+
+    #[test]
+    fn tweak_signature(
+        tweaks in prop::collection::vec(tweak_strategy(), (0,5)),
+        rng_seed in prop::array::uniform32(any::<u8>()),
+    ) {
+        // Use a deterministic RNG so that test failures can be reproduced.
+        // Seeding with 64 bits of entropy is INSECURE and this code should
+        // not be copied outside of this test!
+        let rng = ChaChaRng::from_seed(rng_seed);
+
+        // Create a test case for each signature type.
+        let msg = b"test message for proptests";
+        let mut sig = SignatureCase::<Secp256K1Sha256>::new(rng, msg.to_vec());
+
+        // Apply tweaks to each case.
+        for t in &tweaks {
+            sig.apply_tweak(t);
+        }
+
+        assert!(sig.check());
+    }
+
+
+}
diff --git a/frost-secp256k1-tr/src/tests/vss_commitment.rs b/frost-secp256k1-tr/src/tests/vss_commitment.rs
new file mode 100644
index 00000000..1a09195a
--- /dev/null
+++ b/frost-secp256k1-tr/src/tests/vss_commitment.rs
@@ -0,0 +1,38 @@
+use lazy_static::lazy_static;
+use rand::thread_rng;
+use serde_json::Value;
+
+use crate::*;
+
+// Tests for serialization and deserialization VerifiableSecretSharingCommitment
+
+lazy_static! {
+    pub static ref ELEMENTS: Value =
+        serde_json::from_str(include_str!("../../tests/helpers/elements.json").trim()).unwrap();
+}
+
+#[test]
+fn check_serialize_vss_commitment() {
+    let rng = thread_rng();
+    frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_deserialize_vss_commitment() {
+    let rng = thread_rng();
+    frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_deserialize_vss_commitment_error() {
+    let rng = thread_rng();
+    frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::<Secp256K1Sha256, _>(
+        rng, &ELEMENTS,
+    );
+}
+
+#[test]
+fn check_compute_public_key_package() {
+    let rng = thread_rng();
+    frost_core::tests::vss_commitment::check_compute_public_key_package::<Secp256K1Sha256, _>(rng);
+}
diff --git a/frost-secp256k1-tr/tests/common_traits_tests.rs b/frost-secp256k1-tr/tests/common_traits_tests.rs
new file mode 100644
index 00000000..81b97a95
--- /dev/null
+++ b/frost-secp256k1-tr/tests/common_traits_tests.rs
@@ -0,0 +1,74 @@
+#![cfg(feature = "serde")]
+
+mod helpers;
+
+use frost_secp256k1_tr::SigningKey;
+use helpers::samples;
+use rand::thread_rng;
+
+#[allow(clippy::unnecessary_literal_unwrap)]
+fn check_common_traits_for_type<T: Clone + Eq + PartialEq + std::fmt::Debug>(v: T) {
+    // Make sure can be debug-printed. This also catches if the Debug does not
+    // have an endless recursion (a popular mistake).
+    println!("{:?}", v);
+    // Test Clone and Eq
+    assert_eq!(v, v.clone());
+    // Make sure it can be unwrapped in a Result (which requires Debug).
+    let e: Result<T, ()> = Ok(v.clone());
+    assert_eq!(v, e.unwrap());
+}
+
+#[test]
+fn check_signing_key_common_traits() {
+    let mut rng = thread_rng();
+    let signing_key = SigningKey::new(&mut rng);
+    check_common_traits_for_type(signing_key);
+}
+
+#[test]
+fn check_signing_commitments_common_traits() {
+    let commitments = samples::signing_commitments();
+    check_common_traits_for_type(commitments);
+}
+
+#[test]
+fn check_signing_package_common_traits() {
+    let signing_package = samples::signing_package();
+    check_common_traits_for_type(signing_package);
+}
+
+#[test]
+fn check_signature_share_common_traits() {
+    let signature_share = samples::signature_share();
+    check_common_traits_for_type(signature_share);
+}
+
+#[test]
+fn check_secret_share_common_traits() {
+    let secret_share = samples::secret_share();
+    check_common_traits_for_type(secret_share);
+}
+
+#[test]
+fn check_key_package_common_traits() {
+    let key_package = samples::key_package();
+    check_common_traits_for_type(key_package);
+}
+
+#[test]
+fn check_public_key_package_common_traits() {
+    let public_key_package = samples::public_key_package();
+    check_common_traits_for_type(public_key_package);
+}
+
+#[test]
+fn check_round1_package_common_traits() {
+    let round1_package = samples::round1_package();
+    check_common_traits_for_type(round1_package);
+}
+
+#[test]
+fn check_round2_package_common_traits() {
+    let round2_package = samples::round2_package();
+    check_common_traits_for_type(round2_package);
+}
diff --git a/frost-secp256k1-tr/tests/helpers/elements.json b/frost-secp256k1-tr/tests/helpers/elements.json
new file mode 100644
index 00000000..e8cd4082
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/elements.json
@@ -0,0 +1,5 @@
+{
+    "elements": {
+        "invalid_element": "123456afdf4a7f88885ab26b20d18edb7d4d9589812a6cf1a5a1a09d3808dae5d8"
+    }
+}
diff --git a/frost-secp256k1-tr/tests/helpers/mod.rs b/frost-secp256k1-tr/tests/helpers/mod.rs
new file mode 100644
index 00000000..2f415966
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/mod.rs
@@ -0,0 +1,5 @@
+// Required since each integration test is compiled as a separated crate,
+// and each one uses only part of the module.
+#![allow(dead_code)]
+
+pub mod samples;
diff --git a/frost-secp256k1-tr/tests/helpers/repair-share.json b/frost-secp256k1-tr/tests/helpers/repair-share.json
new file mode 100644
index 00000000..f4db9bc8
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/repair-share.json
@@ -0,0 +1,15 @@
+{
+    "scalar_generation": {
+        "random_scalar_1": "1847f6c4a85096e5dbc9e200c9691c5164f8e276d32d4a54ebaf4275474a1403",
+        "random_scalar_2": "eac5595269d108812eaa865bf62c703a2c128a61fa3bd4dc837b9314bc515204",
+        "random_scalar_3": "5b3b6084e41c273a39a8d9bbbd87fbcd626c07030142bf78c6c91247bf175700",
+        "random_scalar_sum": "5e48b09bf63dc6a1441d42187d1d885a38c896f51f633e6e76218944f27c7bc6"
+    },
+    "sigma_generation": {
+        "sigma_1": "ec3aa83140065181d75b746bfd6bbbbaf212bdfbb3a91670f924d1ca899cbc0c",
+        "sigma_2": "5dd288d659e0a2dd3ef7523a9cc4f80f4a7f919e9980005c7fbec0961d3fb500",
+        "sigma_3": "3e62e7461db9ca1ed2f1549a8114bbc87fa9242ce0012ed3f9ac9dcf23f4c30a",
+        "sigma_4": "684c44e7aba416a1982a8db8ec2a3095f5cc6a3f958a4716b69ae76524dd7200",
+        "sigma_sum": "f0bc5d356344d51f816ea8fa076fa029f7590120136bec7c6958b9081f7864d5"
+    }
+}
diff --git a/frost-secp256k1-tr/tests/helpers/samples.json b/frost-secp256k1-tr/tests/helpers/samples.json
new file mode 100644
index 00000000..1cc174a9
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/samples.json
@@ -0,0 +1,7 @@
+{
+    "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+    "proof_of_knowledge": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+    "element1": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+    "element2": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
+    "scalar1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+}
diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs
new file mode 100644
index 00000000..839f3f57
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/samples.rs
@@ -0,0 +1,128 @@
+//! Generate sample, fixed instances of structs for testing.
+
+use std::collections::BTreeMap;
+
+use frost_core::{round1::Nonce, Ciphersuite, Element, Group, Scalar};
+use frost_secp256k1_tr::{
+    keys::{
+        dkg::{round1, round2},
+        KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
+        VerifyingShare,
+    },
+    round1::{NonceCommitment, SigningCommitments, SigningNonces},
+    round2::SignatureShare,
+    Field, Signature, SigningPackage, VerifyingKey,
+};
+
+type C = frost_secp256k1_tr::Secp256K1Sha256;
+
+fn element1() -> Element<C> {
+    <C as Ciphersuite>::Group::generator()
+}
+
+fn element2() -> Element<C> {
+    element1() + element1()
+}
+
+fn scalar1() -> Scalar<C> {
+    let one = <<C as Ciphersuite>::Group as Group>::Field::one();
+    let three = one + one + one;
+    // To return a fixed non-small number, get the inverse of 3
+    <<C as Ciphersuite>::Group as Group>::Field::invert(&three)
+        .expect("nonzero elements have inverses")
+}
+
+/// Generate a sample SigningCommitments.
+pub fn signing_nonces() -> SigningNonces {
+    let serialized_scalar1 = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_scalar2 = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let hiding_nonce = Nonce::deserialize(serialized_scalar1.as_ref()).unwrap();
+    let binding_nonce = Nonce::deserialize(serialized_scalar2.as_ref()).unwrap();
+
+    SigningNonces::from_nonces(hiding_nonce, binding_nonce)
+}
+
+/// Generate a sample SigningCommitments.
+pub fn signing_commitments() -> SigningCommitments {
+    let serialized_element1 = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()).unwrap();
+    let hiding_nonce_commitment =
+        NonceCommitment::deserialize(serialized_element1.as_ref()).unwrap();
+    let binding_nonce_commitment =
+        NonceCommitment::deserialize(serialized_element2.as_ref()).unwrap();
+
+    SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
+}
+
+/// Generate a sample SigningPackage.
+pub fn signing_package() -> SigningPackage {
+    let identifier = 42u16.try_into().unwrap();
+    let commitments = BTreeMap::from([(identifier, signing_commitments())]);
+    let message = "hello world".as_bytes();
+
+    SigningPackage::new(commitments, message)
+}
+
+/// Generate a sample SignatureShare.
+pub fn signature_share() -> SignatureShare {
+    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+
+    SignatureShare::deserialize(serialized_scalar.as_ref()).unwrap()
+}
+
+/// Generate a sample SecretShare.
+pub fn secret_share() -> SecretShare {
+    let identifier = 42u16.try_into().unwrap();
+    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let signing_share = SigningShare::deserialize(serialized_scalar.as_ref()).unwrap();
+    let vss_commitment =
+        VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
+
+    SecretShare::new(identifier, signing_share, vss_commitment)
+}
+
+/// Generate a sample KeyPackage.
+pub fn key_package() -> KeyPackage {
+    let identifier = 42u16.try_into().unwrap();
+    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let signing_share = SigningShare::deserialize(serialized_scalar.as_ref()).unwrap();
+    let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap();
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap();
+
+    KeyPackage::new(identifier, signing_share, verifying_share, verifying_key, 2)
+}
+
+/// Generate a sample PublicKeyPackage.
+pub fn public_key_package() -> PublicKeyPackage {
+    let identifier = 42u16.try_into().unwrap();
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap();
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap();
+    let verifying_shares = BTreeMap::from([(identifier, verifying_share)]);
+
+    PublicKeyPackage::new(verifying_shares, verifying_key)
+}
+
+/// Generate a sample round1::Package.
+pub fn round1_package() -> round1::Package {
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
+    let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
+    let vss_commitment =
+        VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
+
+    round1::Package::new(vss_commitment, signature)
+}
+
+/// Generate a sample round2::Package.
+pub fn round2_package() -> round2::Package {
+    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let signing_share = SigningShare::deserialize(serialized_scalar.as_ref()).unwrap();
+
+    round2::Package::new(signing_share)
+}
diff --git a/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json b/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json
new file mode 100644
index 00000000..2fedbec2
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json
@@ -0,0 +1,77 @@
+{
+  "config": {
+    "MAX_PARTICIPANTS": "3",
+    "NUM_PARTICIPANTS": "2",
+    "MIN_PARTICIPANTS": "2",
+    "name": "FROST(secp256k1, SHA-256)",
+    "group": "secp256k1",
+    "hash": "SHA-256"
+  },
+  "inputs": {
+    "participant_list": [
+      1,
+      3
+    ],
+    "group_secret_key": "0d004150d27c3bf2a42f312683d35fac7394b1e9e318249c1bfe7f0795a83114",
+    "verifying_key_key": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4f",
+    "message": "74657374",
+    "share_polynomial_coefficients": [
+      "fbf85eadae3058ea14f19148bb72b45e4399c0b16028acaf0395c9b03c823579"
+    ],
+    "participant_shares": [
+      {
+        "identifier": 1,
+        "participant_share": "08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c"
+      },
+      {
+        "identifier": 2,
+        "participant_share": "04f0feac2edcedc6ce1253b7fab8c86b856a797f44d83d82a385554e6e401984"
+      },
+      {
+        "identifier": 3,
+        "participant_share": "00e95d59dd0d46b0e303e500b62b7ccb0e555d49f5b849f5e748c071da8c0dbc"
+      }
+    ]
+  },
+  "round_one_outputs": {
+    "outputs": [
+      {
+        "identifier": 1,
+        "hiding_nonce_randomness": "bda8e748e599187762cff956f03dc6ea13fc8e04491a0427b7e6e78600f41c52",
+        "binding_nonce_randomness": "2ca682429bf05df435b9927b8edb1d748278f3e42fa11ef358e49bbf4a1b780d",
+        "hiding_nonce": "58cd30723da418156fe9b71870a118e0bbc3d0353ba7c760f9bbc8d60c3dab29",
+        "binding_nonce": "c22289cc43b82ed938d4b2288efb7381c405fb59f5d43bddc543d98838c60b19",
+        "hiding_nonce_commitment": "024e34ab3a7ad6b4563dbfe97e9f1206b3378cceb2502491ed0fb709765e1e5ba8",
+        "binding_nonce_commitment": "03d4b1f3a61dc67e64dfb4abfccabb712f1f6914a6ec9b67749d171370453192cb",
+        "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000001",
+        "binding_factor": "55a3e44879db6daf00c81eb28e828869560c0901f347baff524f1c91a6669604"
+      },
+      {
+        "identifier": 3,
+        "hiding_nonce_randomness": "70818dd5170672c4a4285fd593d4f222417f941f3118e1244955e7a1098a35d8",
+        "binding_nonce_randomness": "74ca2da071ed4a2a6cad5087d6758b48a558ab5861c61117fee05757e4b1309e",
+        "hiding_nonce": "a4109db0a5db30fac8cd1f4e272ff02e08258928f067d82c63d97279b114514a",
+        "binding_nonce": "ce3837bd963f0d81002279f7bb9eefceac64435f638885c2beae6f1dd881fd9e",
+        "hiding_nonce_commitment": "02d768658a1b94225645401a1512b803657770c7a21bf9ccccccfa09930a44951b",
+        "binding_nonce_commitment": "034570a4e5217ee8770a28401185f50b4fce4d3f3933a3af9df7ab39b42381d0eb",
+        "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000003",
+        "binding_factor": "444c1cc7cfe48f4577cce65488f337a9cc8c33dea4cfa986eb590bd8e2b1fa2d"
+      }
+    ]
+  },
+  "round_two_outputs": {
+    "outputs": [
+      {
+        "identifier": 1,
+        "sig_share": "2ffc305d1694fd84108b84d98306a1af807c6ad9bc3a2d8e448a09643202a15b"
+      },
+      {
+        "identifier": 3,
+        "sig_share": "a8c392566ea29e852b4080a028bf5547166c87e703e4fb7136d4ebef65f99b3f"
+      }
+    ]
+  },
+  "final_output": {
+    "sig": "0c776a9516a77808b70a31e74f1464814a6fcf897fb3a6bd84c7a9a9a7a5bcb8d8bfc2b385379c093bcc0579abc5f6f696e8f2c0c01f28ff7b5ef55397fc3c9a"
+  }
+}
diff --git a/frost-secp256k1-tr/tests/helpers/vectors.json b/frost-secp256k1-tr/tests/helpers/vectors.json
new file mode 100644
index 00000000..2fedbec2
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/vectors.json
@@ -0,0 +1,77 @@
+{
+  "config": {
+    "MAX_PARTICIPANTS": "3",
+    "NUM_PARTICIPANTS": "2",
+    "MIN_PARTICIPANTS": "2",
+    "name": "FROST(secp256k1, SHA-256)",
+    "group": "secp256k1",
+    "hash": "SHA-256"
+  },
+  "inputs": {
+    "participant_list": [
+      1,
+      3
+    ],
+    "group_secret_key": "0d004150d27c3bf2a42f312683d35fac7394b1e9e318249c1bfe7f0795a83114",
+    "verifying_key_key": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4f",
+    "message": "74657374",
+    "share_polynomial_coefficients": [
+      "fbf85eadae3058ea14f19148bb72b45e4399c0b16028acaf0395c9b03c823579"
+    ],
+    "participant_shares": [
+      {
+        "identifier": 1,
+        "participant_share": "08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c"
+      },
+      {
+        "identifier": 2,
+        "participant_share": "04f0feac2edcedc6ce1253b7fab8c86b856a797f44d83d82a385554e6e401984"
+      },
+      {
+        "identifier": 3,
+        "participant_share": "00e95d59dd0d46b0e303e500b62b7ccb0e555d49f5b849f5e748c071da8c0dbc"
+      }
+    ]
+  },
+  "round_one_outputs": {
+    "outputs": [
+      {
+        "identifier": 1,
+        "hiding_nonce_randomness": "bda8e748e599187762cff956f03dc6ea13fc8e04491a0427b7e6e78600f41c52",
+        "binding_nonce_randomness": "2ca682429bf05df435b9927b8edb1d748278f3e42fa11ef358e49bbf4a1b780d",
+        "hiding_nonce": "58cd30723da418156fe9b71870a118e0bbc3d0353ba7c760f9bbc8d60c3dab29",
+        "binding_nonce": "c22289cc43b82ed938d4b2288efb7381c405fb59f5d43bddc543d98838c60b19",
+        "hiding_nonce_commitment": "024e34ab3a7ad6b4563dbfe97e9f1206b3378cceb2502491ed0fb709765e1e5ba8",
+        "binding_nonce_commitment": "03d4b1f3a61dc67e64dfb4abfccabb712f1f6914a6ec9b67749d171370453192cb",
+        "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000001",
+        "binding_factor": "55a3e44879db6daf00c81eb28e828869560c0901f347baff524f1c91a6669604"
+      },
+      {
+        "identifier": 3,
+        "hiding_nonce_randomness": "70818dd5170672c4a4285fd593d4f222417f941f3118e1244955e7a1098a35d8",
+        "binding_nonce_randomness": "74ca2da071ed4a2a6cad5087d6758b48a558ab5861c61117fee05757e4b1309e",
+        "hiding_nonce": "a4109db0a5db30fac8cd1f4e272ff02e08258928f067d82c63d97279b114514a",
+        "binding_nonce": "ce3837bd963f0d81002279f7bb9eefceac64435f638885c2beae6f1dd881fd9e",
+        "hiding_nonce_commitment": "02d768658a1b94225645401a1512b803657770c7a21bf9ccccccfa09930a44951b",
+        "binding_nonce_commitment": "034570a4e5217ee8770a28401185f50b4fce4d3f3933a3af9df7ab39b42381d0eb",
+        "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000003",
+        "binding_factor": "444c1cc7cfe48f4577cce65488f337a9cc8c33dea4cfa986eb590bd8e2b1fa2d"
+      }
+    ]
+  },
+  "round_two_outputs": {
+    "outputs": [
+      {
+        "identifier": 1,
+        "sig_share": "2ffc305d1694fd84108b84d98306a1af807c6ad9bc3a2d8e448a09643202a15b"
+      },
+      {
+        "identifier": 3,
+        "sig_share": "a8c392566ea29e852b4080a028bf5547166c87e703e4fb7136d4ebef65f99b3f"
+      }
+    ]
+  },
+  "final_output": {
+    "sig": "0c776a9516a77808b70a31e74f1464814a6fcf897fb3a6bd84c7a9a9a7a5bcb8d8bfc2b385379c093bcc0579abc5f6f696e8f2c0c01f28ff7b5ef55397fc3c9a"
+  }
+}
diff --git a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
new file mode 100644
index 00000000..ff497e9a
--- /dev/null
+++ b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
@@ -0,0 +1,51 @@
+{
+  "config": {
+    "MAX_PARTICIPANTS": 3,
+    "MIN_PARTICIPANTS": 2,
+    "name": "FROST(secp256k1, SHA-256)",
+    "group": "secp256k1",
+    "hash": "SHA-256"
+  },
+  "inputs": {
+    "verifying_key": "034a48daffc43b47b42695611942c481aecffb9137686ad0b3e0ab8e1f1dab0293",
+    "1": {
+      "identifier": 1,
+      "signing_key": "68e3f6904c6043973515a36bf7801a71597da35733f21305d75a5234f06e4529",
+      "coefficient": "25d2d840a3e2718a431ec69e14ee8a015b000d43c7a9868060f01d5aa52a19d1",
+      "vss_commitments": ["03e7ba4acb164d2bd5eba4f47b3a788109ddb3f88f1181792424fa332123a25ea8", "037495e920a1f032916193aa80ea97a4c3a611dec9ab47ccc969deb664f5f88bbe"],
+      "proof_of_knowledge": "6689a8d414eb4961308e21f8caa1045236efded4f3de9209dc07547e88be3b42e192de9bed27fb78a7a4d4e35a0422f11f52631b8e66d69e609398eaff2770b8",
+      "signing_shares": {
+        "2": "1dd3cb3e2370e6af22917415f0ad584514807b58b3cc40d2230a26e115f02771",
+        "3": "dd25ee86acd01f996618aa0d1153f5e8fbc929a8e8a18b8f0a15f91d087217e2"
+      },
+      "verifying_share": "03e2eada7cdb20ec24babb687eb633580c977148c70254700f1ad4a931316dc6d9",
+      "signing_share": "89b08895c083bb6a00de882d0e6ff2a20a1878b5e8c0c5aba5983100e3c45d0c"
+    },
+    "2": {
+      "identifier": 2,
+      "signing_key": "2619be8223b23e0453ddc630a4d164e81f7d8a9e07af33c4d4d02190df8bec13",
+      "coefficient": "f7ba0cbbffbea8aaceb3ade54bdbf35bafb1cda15b65ad490e0c63dd069a7c9f",
+      "vss_commitments": ["03ef10370a008cd95e179dc51e2cb7828f30b72d254e5166484f927c84ab326582", "022ce0dac0db217ba326fbbe3e6132d45e2a4bfa0a0c3790d91eacce9a1c2d6a10"],
+      "proof_of_knowledge": "19df66bda7724ccfa6a5ea76aac9cc167880d55717fe6887b89aeea94408cc9ce47b65a55f9d00479e9d3ea2c7402e81803e2e724d45d70c2cb93e3b0deb5f78",
+      "signing_shares": {
+        "1": "b489a711942526abbb5330a8215d2e740f7dbddec3452006993a8cea3ac278cb",
+        "3": "20255dc07b1fb78bdf90bd85fd2389c988c8250faee11826656a09142fa9fc97"
+      },
+      "verifying_share": "0312705f7560a146760034ebdd103277e184ce81d2ba6a67a43f0b3f39410cc396",
+      "signing_share": "ea3cdccc32746d918c2910295b0a03dfa1c94f01d20f860c6fe8c22fb6c0d831"
+    },
+    "3": {
+      "identifier": 3,
+      "signing_key": "9a267f4cde8087a6eca0969425846209b41b515b73195ebbeeef8a991103f1ec",
+      "coefficient": "42ff6f39ce4f97f279781378ebcf93df47add84d75882cd31b266e83f76e25f6",
+      "vss_commitments": ["02da186c3863c5600b471a2799cb6f15ae4d8315a2f225c177798880e75ac820a0", "03e6a36e7fa4b117c1aa428886672e3a35d926bb4c585a9b07d8ee9a3387420067"],
+      "proof_of_knowledge": "6e115d9e63fd15d432b380ccf1ec4ed03340fcf96caeae8985aedb5f905b1a65dc422ffe5878988fbbc55454857736c7755d9c8f5ee6822c8833ea21d54dba36",
+      "signing_shares": {
+        "1": "da5c7f5238079835fe71f746364bb8756a7dcb228aeea686fa2aaa44dfec929c",
+        "2": "0d47e4b622ee3804bff8cfe088653efefe865cce0c065aecbf7e318182b89e2d"
+      },
+      "verifying_share": "036607b45621ce6840d999980c9c74d69a15fa5a246e852ee2ca6924ce65fa299b",
+      "signing_share": "4ac93102a4651fb917739825a7a4151e7ecb48670c15a6317a66f4d1b9871215"
+    }
+  }
+}
diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs
new file mode 100644
index 00000000..ddbb6540
--- /dev/null
+++ b/frost-secp256k1-tr/tests/integration_tests.rs
@@ -0,0 +1,363 @@
+use frost_secp256k1_tr::*;
+use lazy_static::lazy_static;
+use rand::thread_rng;
+use serde_json::Value;
+
+#[test]
+fn check_zero_key_fails() {
+    frost_core::tests::ciphersuite_generic::check_zero_key_fails::<Secp256K1Sha256>();
+}
+
+#[test]
+fn check_sign_with_dkg() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
+        rng,
+        b"message".into(),
+    );
+}
+
+#[test]
+fn check_dkg_part1_fails_with_invalid_signers_min_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 1;
+    let max_signers = 3;
+    let error = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_dkg_part1_fails_with_min_signers_greater_than_max() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 2;
+    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_dkg_part1_fails_with_invalid_signers_max_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 1;
+    let error = Error::InvalidMaxSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_rts() {
+    let rng = thread_rng();
+
+    frost_core::tests::repairable::check_rts::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer() {
+    let rng = thread_rng();
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_serialisation() {
+    let rng = thread_rng();
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::<Secp256K1Sha256, _>(
+        rng,
+    );
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() {
+    let rng = thread_rng();
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::<
+        Secp256K1Sha256,
+        _,
+    >(rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() {
+    let rng = thread_rng();
+    let identifiers = vec![
+        Identifier::try_from(1).unwrap(),
+        Identifier::try_from(3).unwrap(),
+        Identifier::try_from(4).unwrap(),
+        Identifier::try_from(5).unwrap(),
+    ];
+    let min_signers = 1;
+    let max_signers = 4;
+    let error = Error::InvalidMinSigners;
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(max_signers, min_signers, &identifiers, error, rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() {
+    let rng = thread_rng();
+    let identifiers = vec![
+        Identifier::try_from(1).unwrap(),
+        Identifier::try_from(3).unwrap(),
+        Identifier::try_from(4).unwrap(),
+        Identifier::try_from(5).unwrap(),
+    ];
+    let min_signers = 3;
+    let max_signers = 3;
+    let error: frost_core::Error<Secp256K1Sha256> = Error::IncorrectNumberOfIdentifiers;
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(max_signers, min_signers, &identifiers, error, rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() {
+    let rng = thread_rng();
+    let identifiers = vec![
+        Identifier::try_from(1).unwrap(),
+        Identifier::try_from(3).unwrap(),
+        Identifier::try_from(4).unwrap(),
+        Identifier::try_from(5).unwrap(),
+    ];
+    let min_signers = 6;
+    let max_signers = 4;
+    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(max_signers, min_signers, &identifiers, error, rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() {
+    let rng = thread_rng();
+    let identifiers = vec![Identifier::try_from(1).unwrap()];
+    let min_signers = 3;
+    let max_signers = 1;
+    let error = Error::InvalidMaxSigners;
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(max_signers, min_signers, &identifiers, error, rng);
+}
+
+#[test]
+fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
+    let rng = thread_rng();
+    let identifiers = vec![
+        Identifier::try_from(8).unwrap(),
+        Identifier::try_from(3).unwrap(),
+        Identifier::try_from(4).unwrap(),
+        Identifier::try_from(6).unwrap(),
+    ];
+    let min_signers = 2;
+    let max_signers = 4;
+    let error = Error::UnknownIdentifier;
+
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(max_signers, min_signers, &identifiers, error, rng);
+}
+
+#[test]
+fn check_sign_with_dealer() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
+        rng,
+        b"message".into(),
+    );
+}
+
+#[test]
+fn check_sign_with_dealer_fails_with_invalid_min_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 1;
+    let max_signers = 3;
+    let error = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 2;
+    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_sign_with_dealer_fails_with_invalid_max_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 1;
+    let error = Error::InvalidMaxSigners;
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+/// This is testing that Shamir's secret sharing to compute and arbitrary
+/// value is working.
+#[test]
+fn check_share_generation_secp256k1_tr_sha256() {
+    let rng = thread_rng();
+    frost_core::tests::ciphersuite_generic::check_share_generation::<Secp256K1Sha256, _>(rng);
+}
+
+#[test]
+fn check_share_generation_fails_with_invalid_min_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 0;
+    let max_signers = 3;
+    let error = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_share_generation_fails_with_min_signers_greater_than_max() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 2;
+    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+
+    frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+#[test]
+fn check_share_generation_fails_with_invalid_max_signers() {
+    let rng = thread_rng();
+
+    let min_signers = 3;
+    let max_signers = 0;
+    let error = Error::InvalidMaxSigners;
+
+    frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
+        Secp256K1Sha256,
+        _,
+    >(min_signers, max_signers, error, rng);
+}
+
+lazy_static! {
+    pub static ref VECTORS: Value =
+        serde_json::from_str(include_str!("../tests/helpers/vectors.json").trim())
+            .expect("Test vector is valid JSON");
+    pub static ref VECTORS_BIG_IDENTIFIER: Value =
+        serde_json::from_str(include_str!("../tests/helpers/vectors-big-identifier.json").trim())
+            .expect("Test vector is valid JSON");
+    pub static ref VECTORS_DKG: Value =
+        serde_json::from_str(include_str!("../tests/helpers/vectors_dkg.json").trim())
+            .expect("Test vector is valid JSON");
+}
+
+#[test]
+fn check_sign_with_test_vectors() {
+    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256>(&VECTORS);
+}
+
+#[test]
+fn check_sign_with_test_vectors_dkg() {
+    frost_core::tests::vectors_dkg::check_dkg_keygen::<Secp256K1Sha256>(&VECTORS_DKG);
+}
+
+#[test]
+fn check_sign_with_test_vectors_with_big_identifiers() {
+    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256>(
+        &VECTORS_BIG_IDENTIFIER,
+    );
+}
+
+#[test]
+fn check_error_culprit() {
+    frost_core::tests::ciphersuite_generic::check_error_culprit::<Secp256K1Sha256>();
+}
+
+#[test]
+fn check_identifier_derivation() {
+    frost_core::tests::ciphersuite_generic::check_identifier_derivation::<Secp256K1Sha256>();
+}
+
+// Explicit test which is used in a documentation snippet
+#[test]
+#[allow(unused_variables)]
+fn check_identifier_generation() -> Result<(), Error> {
+    // ANCHOR: dkg_identifier
+    let participant_identifier = Identifier::try_from(7u16)?;
+    let participant_identifier = Identifier::derive("alice@example.com".as_bytes())?;
+    // ANCHOR_END: dkg_identifier
+    Ok(())
+}
+
+#[test]
+fn check_sign_with_dealer_and_identifiers() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
+        Secp256K1Sha256,
+        _,
+    >(rng, b"message".into());
+}
+
+#[test]
+fn check_sign_with_missing_identifier() {
+    let rng = thread_rng();
+    frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::<Secp256K1Sha256, _>(
+        rng,
+    );
+}
+
+#[test]
+fn check_sign_with_incorrect_commitments() {
+    let rng = thread_rng();
+    frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::<
+        Secp256K1Sha256,
+        _,
+    >(rng);
+}
diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs
new file mode 100644
index 00000000..6b3ce890
--- /dev/null
+++ b/frost-secp256k1-tr/tests/recreation_tests.rs
@@ -0,0 +1,133 @@
+//! Test for recreating packages from their components, which shows that they
+//! can be serialized and deserialized as the user wishes.
+
+use frost_secp256k1_tr::{
+    keys::{
+        dkg::{round1, round2},
+        KeyPackage, PublicKeyPackage, SecretShare,
+    },
+    round1::{SigningCommitments, SigningNonces},
+    round2::SignatureShare,
+    SigningPackage,
+};
+
+mod helpers;
+
+use helpers::samples;
+
+/// Check if SigningNonces can be recreated.
+#[test]
+fn check_signing_nonces_recreation() {
+    let nonces = samples::signing_nonces();
+    let hiding = nonces.hiding();
+    let binding = nonces.binding();
+    let new_nonces = SigningNonces::from_nonces(*hiding, *binding);
+    assert!(nonces == new_nonces);
+}
+
+/// Check if SigningCommitments can be recreated.
+#[test]
+fn check_signing_commitments_recreation() {
+    let commitments = samples::signing_commitments();
+    let hiding = commitments.hiding();
+    let binding = commitments.binding();
+    let new_commitments = SigningCommitments::new(*hiding, *binding);
+    assert!(commitments == new_commitments);
+}
+
+/// Check if SigningPackage can be recreated.
+#[test]
+fn check_signing_package_recreation() {
+    let signing_package = samples::signing_package();
+
+    let commitments = signing_package.signing_commitments();
+    let sig_target = signing_package.sig_target();
+
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    assert!(signing_package == new_signing_package);
+}
+
+/// Check if SignatureShare can be recreated.
+#[test]
+fn check_signature_share_recreation() {
+    let signature_share = samples::signature_share();
+
+    let encoded = signature_share.serialize();
+
+    let new_signature_share = SignatureShare::deserialize(&encoded).unwrap();
+    assert!(signature_share == new_signature_share);
+}
+
+/// Check if SecretShare can be recreated.
+#[test]
+fn check_secret_share_recreation() {
+    let secret_share = samples::secret_share();
+
+    let identifier = secret_share.identifier();
+    let value = secret_share.signing_share();
+    let commitment = secret_share.commitment();
+
+    let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
+
+    assert!(secret_share == new_secret_share);
+}
+
+/// Check if KeyPackage can be recreated.
+#[test]
+fn check_key_package_recreation() {
+    let key_package = samples::key_package();
+
+    let identifier = key_package.identifier();
+    let signing_share = key_package.signing_share();
+    let verifying_share = key_package.verifying_share();
+    let verifying_key = key_package.verifying_key();
+    let min_signers = key_package.min_signers();
+
+    let new_key_package = KeyPackage::new(
+        *identifier,
+        *signing_share,
+        *verifying_share,
+        *verifying_key,
+        *min_signers,
+    );
+
+    assert!(key_package == new_key_package);
+}
+
+/// Check if PublicKeyPackage can be recreated.
+#[test]
+fn check_public_key_package_recreation() {
+    let public_key_package = samples::public_key_package();
+
+    let verifying_shares = public_key_package.verifying_shares();
+    let verifying_key = public_key_package.verifying_key();
+
+    let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key);
+
+    assert!(public_key_package == new_public_key_package);
+}
+
+/// Check if round1::Package can be recreated.
+#[test]
+fn check_round1_package_recreation() {
+    let round1_package = samples::round1_package();
+
+    let vss_commitment = round1_package.commitment();
+    let signature = round1_package.proof_of_knowledge();
+
+    let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
+
+    assert!(round1_package == new_round1_package);
+}
+
+/// Check if round2::Package can be recreated.
+#[test]
+fn check_round2_package_recreation() {
+    let round2_package = samples::round2_package();
+
+    let signing_share = round2_package.signing_share();
+
+    let new_round2_package = round2::Package::new(*signing_share);
+
+    assert!(round2_package == new_round2_package);
+}
diff --git a/frost-secp256k1-tr/tests/rerandomized_tests.rs b/frost-secp256k1-tr/tests/rerandomized_tests.rs
new file mode 100644
index 00000000..79bb5aa3
--- /dev/null
+++ b/frost-secp256k1-tr/tests/rerandomized_tests.rs
@@ -0,0 +1,10 @@
+use frost_secp256k1_tr::Secp256K1Sha256;
+use rand::thread_rng;
+
+#[test]
+fn check_randomized_sign_with_dealer() {
+    let rng = thread_rng();
+
+    let (_msg, _group_signature, _group_pubkey) =
+        frost_rerandomized::tests::check_randomized_sign_with_dealer::<Secp256K1Sha256, _>(rng);
+}
diff --git a/frost-secp256k1-tr/tests/serde_tests.rs b/frost-secp256k1-tr/tests/serde_tests.rs
new file mode 100644
index 00000000..fb8ce73b
--- /dev/null
+++ b/frost-secp256k1-tr/tests/serde_tests.rs
@@ -0,0 +1,642 @@
+#![cfg(feature = "serde")]
+
+mod helpers;
+
+use frost_secp256k1_tr::{
+    keys::{
+        dkg::{round1, round2},
+        KeyPackage, PublicKeyPackage, SecretShare,
+    },
+    round1::SigningCommitments,
+    round2::SignatureShare,
+    SigningPackage,
+};
+
+use helpers::samples;
+
+#[test]
+fn check_signing_commitments_serialization() {
+    let commitments = samples::signing_commitments();
+
+    let json = serde_json::to_string_pretty(&commitments).unwrap();
+    println!("{}", json);
+
+    let decoded_commitments: SigningCommitments = serde_json::from_str(&json).unwrap();
+    assert!(commitments == decoded_commitments);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+      }"#;
+    let decoded_commitments: SigningCommitments = serde_json::from_str(json).unwrap();
+    assert!(commitments == decoded_commitments);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
+
+    // Wrong ciphersuite
+    let invalid_json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST(Wrong, SHA-512)"
+      },
+      "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+      "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+    }"#;
+    assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+      }"#;
+    assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "foo": "0000000000000000000000000000000000000000000000000000000000000000",
+        "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+      }"#;
+    assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST(Ed25519, SHA-512)"
+        },
+        "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
+        "extra": 1
+      }"#;
+    assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
+}
+
+#[test]
+fn check_signing_package_serialization() {
+    let signing_package = samples::signing_package();
+
+    let json = serde_json::to_string_pretty(&signing_package).unwrap();
+    println!("{}", json);
+
+    let decoded_signing_package: SigningPackage = serde_json::from_str(&json).unwrap();
+    assert!(signing_package == decoded_signing_package);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
+
+    let json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "signing_commitments": {
+        "000000000000000000000000000000000000000000000000000000000000002a": {
+          "header": {
+            "version": 0,
+            "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+          },
+          "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+          "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+        }
+      },
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
+    }"#;
+    let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
+    assert!(signing_package == decoded_signing_package);
+
+    // Invalid identifier
+    let invalid_json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "signing_commitments": {
+        "0000000000000000000000000000000000000000000000000000000000000000": {
+          "header": {
+            "version": 0,
+            "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+          },
+          "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+          "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+        }
+      },
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
+    }"#;
+    assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "signing_commitments": {
+        "000000000000000000000000000000000000000000000000000000000000002a": {
+          "header": {
+            "version": 0,
+            "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+          },
+          "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+          "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+        }
+      },
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
+    }"#;
+    assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "signing_commitments": {
+        "000000000000000000000000000000000000000000000000000000000000002a": {
+          "header": {
+            "version": 0,
+            "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+          },
+          "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+        }
+      },
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      }
+    }"#;
+    assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "signing_commitments": {
+        "000000000000000000000000000000000000000000000000000000000000002a": {
+          "header": {
+            "version": 0,
+            "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+          },
+          "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+          "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
+        }
+      },
+      "sig_target": {
+        "message": "68656c6c6f20776f726c64"
+      },
+      "extra": 1
+    }
+    "#;
+    assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
+}
+
+#[test]
+fn check_signature_share_serialization() {
+    let signature_share = samples::signature_share();
+
+    let json = serde_json::to_string_pretty(&signature_share).unwrap();
+    println!("{}", json);
+
+    let decoded_signature_share: SignatureShare = serde_json::from_str(&json).unwrap();
+    assert!(signature_share == decoded_signature_share);
+
+    let json = r#"{
+      "header": {
+        "version": 0,
+        "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+      },
+      "share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+    }"#;
+    let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
+    assert!(signature_share == decoded_commitments);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+      }"#;
+    assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        }
+      }"#;
+    assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "extra": 1
+      }"#;
+    assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
+}
+
+#[test]
+fn check_secret_share_serialization() {
+    let secret_share = samples::secret_share();
+
+    let json = serde_json::to_string_pretty(&secret_share).unwrap();
+    println!("{}", json);
+
+    let decoded_secret_share: SecretShare = serde_json::from_str(&json).unwrap();
+    assert!(secret_share == decoded_secret_share);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+      }"#;
+    let decoded_secret_share: SecretShare = serde_json::from_str(json).unwrap();
+    assert!(secret_share == decoded_secret_share);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
+
+    // Invalid identifier
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "0000000000000000000000000000000000000000000000000000000000000000",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+      }"#;
+    assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+      }"#;
+    assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+      }"#;
+    assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+        "extra": 1,
+      }"#;
+    assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
+}
+
+#[test]
+fn check_key_package_serialization() {
+    let key_package = samples::key_package();
+
+    let json = serde_json::to_string_pretty(&key_package).unwrap();
+    println!("{}", json);
+
+    let decoded_key_package: KeyPackage = serde_json::from_str(&json).unwrap();
+    assert!(key_package == decoded_key_package);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "min_signers": 2
+      }"#;
+    let decoded_key_package: KeyPackage = serde_json::from_str(json).unwrap();
+    assert!(key_package == decoded_key_package);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+
+    // Invalid identifier
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "0000000000000000000000000000000000000000000000000000000000000000",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "min_signers": 2
+      }"#;
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }"#;
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }"#;
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "extra_field": 1
+      }"#;
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+
+    // Invalid version
+    let invalid_json = r#"{
+        "header": {
+          "version": 1,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+        "secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "public": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "group_public": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "min_signers": 2
+      }"#;
+    assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
+}
+
+#[test]
+fn check_public_key_package_serialization() {
+    let public_key_package = samples::public_key_package();
+
+    let json = serde_json::to_string_pretty(&public_key_package).unwrap();
+    println!("{}", json);
+
+    let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(&json).unwrap();
+    assert!(public_key_package == decoded_public_key_package);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "verifying_shares": {
+          "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        },
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }"#;
+    let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap();
+    assert!(public_key_package == decoded_public_key_package);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
+
+    // Invalid identifier
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "verifying_shares": {
+          "0000000000000000000000000000000000000000000000000000000000000000": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        },
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }"#;
+    assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "verifying_shares": {
+          "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        },
+        "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }"#;
+    assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "verifying_shares": {
+          "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        }
+      }"#;
+    assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "verifying_shares": {
+          "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        },
+        "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+        "extra": 1
+      }"#;
+    assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
+}
+
+#[test]
+fn check_round1_package_serialization() {
+    let round1_package = samples::round1_package();
+
+    let json = serde_json::to_string_pretty(&round1_package).unwrap();
+    println!("{}", json);
+
+    let decoded_round1_package: round1::Package = serde_json::from_str(&json).unwrap();
+    assert!(round1_package == decoded_round1_package);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ],
+        "proof_of_knowledge": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+      }"#;
+    let decoded_round1_package: round1::Package = serde_json::from_str(json).unwrap();
+    assert!(round1_package == decoded_round1_package);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ],
+        "foo": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+      }"#;
+    assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ]
+      }"#;
+    assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "commitment": [
+          "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+        ],
+        "proof_of_knowledge": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "extra": 1
+      }"#;
+    assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
+}
+
+#[test]
+fn check_round2_package_serialization() {
+    let round2_package = samples::round2_package();
+
+    let json = serde_json::to_string_pretty(&round2_package).unwrap();
+    println!("{}", json);
+
+    let decoded_round2_package: round2::Package = serde_json::from_str(&json).unwrap();
+    assert!(round2_package == decoded_round2_package);
+
+    let json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+      }"#;
+    let decoded_round2_package: round2::Package = serde_json::from_str(json).unwrap();
+    assert!(round2_package == decoded_round2_package);
+
+    let invalid_json = "{}";
+    assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
+
+    // Invalid field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
+      }"#;
+    assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
+
+    // Missing field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        }
+      }"#;
+    assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
+
+    // Extra field
+    let invalid_json = r#"{
+        "header": {
+          "version": 0,
+          "ciphersuite": "FROST-secp256k1-SHA256-TR-v1"
+        },
+        "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
+        "extra": 1
+      }"#;
+    assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
+}
diff --git a/frost-secp256k1-tr/tests/serialization_tests.rs b/frost-secp256k1-tr/tests/serialization_tests.rs
new file mode 100644
index 00000000..8f93cfb5
--- /dev/null
+++ b/frost-secp256k1-tr/tests/serialization_tests.rs
@@ -0,0 +1,105 @@
+#![cfg(feature = "serialization")]
+
+mod helpers;
+
+use frost_secp256k1_tr::{
+    keys::{
+        dkg::{round1, round2},
+        KeyPackage, PublicKeyPackage, SecretShare,
+    },
+    round1::{SigningCommitments, SigningNonces},
+    round2::SignatureShare,
+    SigningPackage,
+};
+
+use helpers::samples;
+use insta::assert_snapshot;
+
+#[test]
+fn check_signing_nonces_postcard_serialization() {
+    let nonces = samples::signing_nonces();
+    let bytes: Vec<_> = nonces.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(nonces, SigningNonces::deserialize(&bytes).unwrap());
+}
+
+#[test]
+fn check_signing_commitments_postcard_serialization() {
+    let commitments = samples::signing_commitments();
+    let bytes: Vec<_> = commitments.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        commitments,
+        SigningCommitments::deserialize(&bytes).unwrap()
+    );
+}
+
+#[test]
+fn check_signing_package_postcard_serialization() {
+    let signing_package = samples::signing_package();
+    let bytes: Vec<_> = signing_package.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        signing_package,
+        SigningPackage::deserialize(&bytes).unwrap()
+    );
+}
+
+#[test]
+fn check_signature_share_postcard_serialization() {
+    let signature_share = samples::signature_share();
+    let bytes = signature_share.serialize();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        signature_share,
+        SignatureShare::deserialize(&bytes).unwrap()
+    );
+}
+#[test]
+fn check_secret_share_postcard_serialization() {
+    let secret_share = samples::secret_share();
+    let bytes: Vec<_> = secret_share.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(secret_share, SecretShare::deserialize(&bytes).unwrap());
+}
+
+#[test]
+fn check_key_package_postcard_serialization() {
+    let key_package = samples::key_package();
+    let bytes: Vec<_> = key_package.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(key_package, KeyPackage::deserialize(&bytes).unwrap());
+}
+
+#[test]
+fn check_public_key_package_postcard_serialization() {
+    let public_key_package = samples::public_key_package();
+    let bytes: Vec<_> = public_key_package.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        public_key_package,
+        PublicKeyPackage::deserialize(&bytes).unwrap()
+    );
+}
+
+#[test]
+fn check_round1_package_postcard_serialization() {
+    let round1_package = samples::round1_package();
+    let bytes: Vec<_> = round1_package.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        round1_package,
+        round1::Package::deserialize(&bytes).unwrap()
+    );
+}
+
+#[test]
+fn check_round2_package_postcard_serialization() {
+    let round2_package = samples::round2_package();
+    let bytes: Vec<_> = round2_package.serialize().unwrap();
+    assert_snapshot!(hex::encode(&bytes));
+    assert_eq!(
+        round2_package,
+        round2::Package::deserialize(&bytes).unwrap()
+    );
+}
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap
new file mode 100644
index 00000000..ca169f4c
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab3000000000000000000000000000000000000000000000000000000000000002aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b810279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap
new file mode 100644
index 00000000..5600403f
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab301000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap
new file mode 100644
index 00000000..9099b14e
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab3010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817984079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap
new file mode 100644
index 00000000..218294fb
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap
new file mode 100644
index 00000000..82e3585a
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab3000000000000000000000000000000000000000000000000000000000000002aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap
new file mode 100644
index 00000000..aa7a5030
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(bytes)"
+---
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap
new file mode 100644
index 00000000..66962d3c
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_nonces_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_nonces_postcard_serialization.snap
new file mode 100644
index 00000000..537b8e38
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_nonces_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b8100230f8ab3034c7ff4f2ba8603998339c8e42675ceac23ef2e9623fdb260b24b1c944a2ea1a9034c7ff4f2ba8603998339c8e42675ceac23ef2e9623fdb260b24b1c944a2ea1a9
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
new file mode 100644
index 00000000..cc62ef11
--- /dev/null
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
@@ -0,0 +1,5 @@
+---
+source: frost-secp256k1-tr/tests/serialization_tests.rs
+expression: "hex::encode(&bytes)"
+---
+00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c6400
diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs
new file mode 100644
index 00000000..ee31e487
--- /dev/null
+++ b/frost-secp256k1-tr/tests/tweaking_tests.rs
@@ -0,0 +1,89 @@
+use frost_secp256k1_tr::*;
+use rand::thread_rng;
+
+#[test]
+fn check_tweaked_signing_key() {
+    let signing_key = SigningKey::deserialize(&[0xAA; 32]).unwrap();
+    let untweaked_verifying_key = VerifyingKey::from(signing_key);
+
+    let mut rng = rand::thread_rng();
+    let message = b"message";
+
+    let untweaked_signature = signing_key.sign(&mut rng, &message);
+
+    untweaked_verifying_key
+        .verify(&message, &untweaked_signature)
+        .expect("untweaked signature should be valid under untweaked verifying key");
+
+    let signing_target = SigningTarget::new(
+        &message,
+        SigningParameters {
+            tapscript_merkle_root: Some(vec![]),
+        },
+    );
+
+    let tweaked_signature = signing_key.sign(&mut rng, signing_target.clone());
+
+    untweaked_verifying_key
+        .verify(&message, &tweaked_signature)
+        .expect_err("tweaked signature should not be valid under untweaked verifying key");
+
+    let tweaked_verifying_key = untweaked_verifying_key.effective_key(signing_target.sig_params());
+    tweaked_verifying_key
+        .verify(&message, &tweaked_signature)
+        .expect("tweaked signature should be valid under tweaked verifying key");
+
+    untweaked_verifying_key
+        .verify(signing_target.clone(), &tweaked_signature)
+        .expect(
+            "tweaked signature should be valid under untweaked verifying key\
+             when signing params are provided",
+        );
+}
+
+#[test]
+fn check_tweaked_sign_with_dkg() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
+        rng,
+        SigningTarget::new(
+            b"message",
+            SigningParameters {
+                tapscript_merkle_root: Some(vec![]),
+            },
+        ),
+    );
+}
+#[test]
+fn check_tweaked_sign_with_dealer() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
+        rng,
+        SigningTarget::new(
+            b"message",
+            SigningParameters {
+                tapscript_merkle_root: Some(vec![]),
+            },
+        ),
+    );
+}
+
+#[test]
+fn check_tweaked_sign_with_dealer_and_identifiers() {
+    let rng = thread_rng();
+
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
+        Secp256K1Sha256,
+        _,
+    >(
+        rng,
+        SigningTarget::new(
+            b"message",
+            SigningParameters {
+                tapscript_merkle_root: Some(vec![]),
+            },
+        ),
+    );
+}
diff --git a/frost-secp256k1/tests/helpers/samples.json b/frost-secp256k1/tests/helpers/samples.json
index 210c6f27..54f6e1e1 100644
--- a/frost-secp256k1/tests/helpers/samples.json
+++ b/frost-secp256k1/tests/helpers/samples.json
@@ -1,6 +1,7 @@
 {
     "identifier": "000000000000000000000000000000000000000000000000000000000000002a",
+    "proof_of_knowledge": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
     "element1": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
     "element2": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
     "scalar1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81"
-}
\ No newline at end of file
+}
diff --git a/frost-secp256k1/tests/helpers/samples.rs b/frost-secp256k1/tests/helpers/samples.rs
index 3afe78f9..11b84085 100644
--- a/frost-secp256k1/tests/helpers/samples.rs
+++ b/frost-secp256k1/tests/helpers/samples.rs
@@ -109,17 +109,12 @@ pub fn public_key_package() -> PublicKeyPackage {
 
 /// Generate a sample round1::Package.
 pub fn round1_package() -> round1::Package {
-    let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
+    let serialized_signature = Signature::new(element1(), scalar1()).serialize().unwrap();
+    let signature = Signature::deserialize(&serialized_signature).unwrap();
+
     let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()).unwrap();
-    let serialized_signature = serialized_element
-        .as_ref()
-        .iter()
-        .chain(serialized_scalar.as_ref().iter())
-        .cloned()
-        .collect::<Vec<u8>>();
     let vss_commitment =
         VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
-    let signature = Signature::deserialize(&serialized_signature).unwrap();
 
     round1::Package::new(vss_commitment, signature)
 }
diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs
index 9581384b..4cdd2294 100644
--- a/frost-secp256k1/tests/integration_tests.rs
+++ b/frost-secp256k1/tests/integration_tests.rs
@@ -12,7 +12,10 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -184,7 +187,10 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
+        rng,
+        b"message".into(),
+    );
 }
 
 #[test]
@@ -336,7 +342,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Secp256K1Sha256,
         _,
-    >(rng);
+    >(rng, b"message".into());
 }
 
 #[test]
diff --git a/frost-secp256k1/tests/recreation_tests.rs b/frost-secp256k1/tests/recreation_tests.rs
index bb2f8315..1158385b 100644
--- a/frost-secp256k1/tests/recreation_tests.rs
+++ b/frost-secp256k1/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let message = signing_package.message();
+    let sig_target = signing_package.sig_target();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), message);
+    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/gencode/src/main.rs b/gencode/src/main.rs
index eda52d4e..8a91e576 100644
--- a/gencode/src/main.rs
+++ b/gencode/src/main.rs
@@ -227,7 +227,13 @@ fn main() -> ExitCode {
         &std::fs::read_to_string(format!("{original_folder}/tests/helpers/samples.json")).unwrap(),
     )
     .unwrap();
-    for key in &["identifier", "element1", "element2", "scalar1"] {
+    for key in &[
+        "identifier",
+        "proof_of_knowledge",
+        "element1",
+        "element2",
+        "scalar1",
+    ] {
         original_strings.push(samples[key].as_str().unwrap().to_owned());
     }
     let original_strings: Vec<&str> = original_strings.iter().map(|s| s.as_ref()).collect();
@@ -290,6 +296,19 @@ fn main() -> ExitCode {
                 "<S>",
             ],
         ),
+        (
+            "frost-secp256k1-tr",
+            &[
+                "Secp256K1Sha256",
+                "secp256k1 curve",
+                "Secp256K1",
+                "FROST(secp256k1, SHA-256)",
+                "FROST-secp256k1-SHA256-TR-v1",
+                "secp256k1_tr_sha256",
+                "secp256k1_tr",
+                "<S>",
+            ],
+        ),
     ] {
         // Some test use "sample" values. To make these tests work for another ciphersuites,
         // these values must be replaced. To make it cleaner, the strings are
@@ -300,7 +319,13 @@ fn main() -> ExitCode {
             &std::fs::read_to_string(format!("{folder}/tests/helpers/samples.json")).unwrap(),
         )
         .unwrap();
-        for key in &["identifier", "element1", "element2", "scalar1"] {
+        for key in &[
+            "identifier",
+            "proof_of_knowledge",
+            "element1",
+            "element2",
+            "scalar1",
+        ] {
             replacement_strings.push(samples[key].as_str().unwrap().to_owned());
         }
         let replacement_strings: Vec<&str> =

From d69a59d62f2d6e1ae81ad3034188535119e2e7d3 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Fri, 4 Oct 2024 19:50:12 +0000
Subject: [PATCH 04/13] clippy fixes

---
 frost-core/src/keys/dkg.rs      | 2 +-
 frost-core/src/round2.rs        | 4 ++--
 frost-core/src/verifying_key.rs | 2 +-
 frost-secp256k1-tr/src/lib.rs   | 8 ++++----
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs
index 77e29d41..29c906ec 100644
--- a/frost-core/src/keys/dkg.rs
+++ b/frost-core/src/keys/dkg.rs
@@ -331,7 +331,7 @@ where
     let mut preimage = vec![];
 
     preimage.extend_from_slice(identifier.serialize().as_ref());
-    preimage.extend_from_slice(<C::Group>::serialize(&verifying_key)?.as_ref());
+    preimage.extend_from_slice(<C::Group>::serialize(verifying_key)?.as_ref());
     preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
 
     Ok(Challenge(
diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs
index 06cfec29..00f9911d 100644
--- a/frost-core/src/round2.rs
+++ b/frost-core/src/round2.rs
@@ -75,8 +75,8 @@ where
         sig_params: &C::SigningParameters,
     ) -> Result<(), Error<C>> {
         let commitment_share =
-            <C>::effective_commitment_share(group_commitment_share.clone(), &group_commitment);
-        let vsh = <C>::effective_verifying_share(&verifying_share, &verifying_key, &sig_params);
+            <C>::effective_commitment_share(*group_commitment_share, group_commitment);
+        let vsh = <C>::effective_verifying_share(verifying_share, verifying_key, sig_params);
 
         if (<C::Group>::generator() * self.to_scalar())
             != (commitment_share + (vsh * challenge.0 * lambda_i))
diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs
index b472d484..ec5c9f69 100644
--- a/frost-core/src/verifying_key.rs
+++ b/frost-core/src/verifying_key.rs
@@ -77,7 +77,7 @@ where
         //
         // where h is the cofactor
         let R = signature.R;
-        let vk = C::effective_pubkey_element(&self, sig_params);
+        let vk = C::effective_pubkey_element(self, sig_params);
 
         let zB = C::Group::generator() * signature.z;
         let cA = vk * challenge.0;
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index 7cf3b1ea..8d345fbe 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -335,7 +335,7 @@ impl Ciphersuite for Secp256K1Sha256 {
     ) -> Result<Challenge<S>, Error> {
         let mut preimage = vec![];
         let tweaked_pk = tweaked_public_key(
-            &verifying_key,
+            verifying_key,
             sig_target.sig_params().tapscript_merkle_root.as_ref(),
         );
         preimage.extend_from_slice(&R.to_affine().x());
@@ -352,13 +352,13 @@ impl Ciphersuite for Secp256K1Sha256 {
         verifying_key: &VerifyingKey,
         sig_target: &SigningTarget,
     ) -> Result<Signature, Error> {
-        let challenge = Self::challenge(&R, verifying_key, &sig_target)?;
+        let challenge = Self::challenge(&R, verifying_key, sig_target)?;
 
         let t = tweak(
             &verifying_key.to_element(),
             sig_target.sig_params().tapscript_merkle_root.as_ref(),
         );
-        let tc = t * challenge.clone().to_scalar();
+        let tc = t * challenge.to_scalar();
         let tweaked_pubkey = tweaked_public_key(
             verifying_key,
             sig_target.sig_params().tapscript_merkle_root.as_ref(),
@@ -382,7 +382,7 @@ impl Ciphersuite for Secp256K1Sha256 {
     ) -> Signature {
         let tweaked_pubkey =
             tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref());
-        let c = challenge.clone().to_scalar();
+        let c = challenge.to_scalar();
         let z = if tweaked_pubkey.to_affine().y_is_odd().into() {
             k - (c * secret)
         } else {

From 802de7a5c09faba9e5f3b7347bbed1b52a8d1dff Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Sat, 5 Oct 2024 19:31:53 +0000
Subject: [PATCH 05/13] tweak DKG output to avoid rogue taproot tweaks

---
 frost-core/src/keys/dkg.rs                    | 14 +++----
 frost-core/src/traits.rs                      | 37 +++++++++++++++--
 frost-secp256k1-tr/src/lib.rs                 | 41 +++++++++++++++++++
 .../tests/helpers/vectors_dkg.json            | 14 +++----
 4 files changed, 87 insertions(+), 19 deletions(-)

diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs
index 29c906ec..712a516a 100644
--- a/frost-core/src/keys/dkg.rs
+++ b/frost-core/src/keys/dkg.rs
@@ -562,16 +562,12 @@ pub fn part3<C: Ciphersuite>(
             &round2_secret_package.commitment,
         )))
         .collect();
-    let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
 
-    let key_package = KeyPackage {
-        header: Header::default(),
-        identifier: round2_secret_package.identifier,
+    C::dkg_output_finalize(
+        round2_secret_package.identifier,
+        commitments,
         signing_share,
         verifying_share,
-        verifying_key: public_key_package.verifying_key,
-        min_signers: round2_secret_package.min_signers,
-    };
-
-    Ok((key_package, public_key_package))
+        round2_secret_package.min_signers,
+    )
 }
diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs
index 5e21a2f0..73e52c66 100644
--- a/frost-core/src/traits.rs
+++ b/frost-core/src/traits.rs
@@ -5,14 +5,17 @@ use core::{
     ops::{Add, Mul, Sub},
 };
 
-use alloc::vec::Vec;
+use alloc::{collections::BTreeMap, vec::Vec};
 use rand_core::{CryptoRng, RngCore};
 
 use crate::{
     challenge,
-    keys::{KeyPackage, VerifyingShare},
+    keys::{
+        KeyPackage, PublicKeyPackage, SigningShare, VerifiableSecretSharingCommitment,
+        VerifyingShare,
+    },
     round1, round2, BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError,
-    Signature, SigningTarget, VerifyingKey,
+    Header, Identifier, Signature, SigningTarget, VerifyingKey,
 };
 
 /// A prime order finite field GF(q) over which all scalar values for our prime order group can be
@@ -447,4 +450,32 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     ) -> <Self::Group as Group>::Element {
         verifying_share.to_element()
     }
+
+    /// Construct the key packages from the output of a successful DKG execution.
+    /// The signing share and verifying share have already been verified and the
+    /// identifier belongs to our signer.
+    ///
+    /// In frost-sepc256k1-tr, this adds a hash-based tweak to the group key
+    /// to prevent peers from inserting rogue tapscript tweaks into the group's
+    /// joint public key.
+    fn dkg_output_finalize(
+        identifier: Identifier<Self>,
+        commitments: BTreeMap<Identifier<Self>, &VerifiableSecretSharingCommitment<Self>>,
+        signing_share: SigningShare<Self>,
+        verifying_share: VerifyingShare<Self>,
+        min_signers: u16,
+    ) -> Result<(KeyPackage<Self>, PublicKeyPackage<Self>), Error<Self>> {
+        let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
+
+        let key_package = KeyPackage {
+            header: Header::default(),
+            identifier,
+            signing_share,
+            verifying_share,
+            verifying_key: public_key_package.verifying_key,
+            min_signers,
+        };
+
+        Ok((key_package, public_key_package))
+    }
 }
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index 8d345fbe..a34680b5 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -545,6 +545,47 @@ impl Ciphersuite for Secp256K1Sha256 {
             vs
         }
     }
+
+    /// We add an unusable taproot tweak to the group key computed by a DKG run,
+    /// to prevent peers from inserting rogue tapscript tweaks into the group's
+    /// joint public key.
+    fn dkg_output_finalize(
+        identifier: Identifier,
+        commitments: BTreeMap<Identifier, &keys::VerifiableSecretSharingCommitment>,
+        signing_share: keys::SigningShare,
+        verifying_share: keys::VerifyingShare,
+        min_signers: u16,
+    ) -> Result<(keys::KeyPackage, keys::PublicKeyPackage), Error> {
+        let untweaked_public_key_package =
+            keys::PublicKeyPackage::from_dkg_commitments(&commitments)?;
+
+        let untweaked_vk = untweaked_public_key_package.verifying_key().to_element();
+        let t = tweak(&untweaked_vk, Some(vec![])); // unspendable script path
+        let tG = ProjectivePoint::GENERATOR * t;
+
+        let tweaked_verifying_shares: BTreeMap<Identifier, keys::VerifyingShare> =
+            untweaked_public_key_package
+                .verifying_shares()
+                .clone()
+                .into_iter()
+                .map(|(id, share)| (id, keys::VerifyingShare::new(share.to_element() + tG)))
+                .collect();
+
+        let tweaked_verifying_key = VerifyingKey::new(untweaked_vk + tG);
+
+        let key_package = keys::KeyPackage::new(
+            identifier,
+            keys::SigningShare::new(signing_share.to_scalar() + t),
+            keys::VerifyingShare::new(verifying_share.to_element() + tG),
+            tweaked_verifying_key,
+            min_signers,
+        );
+
+        let public_key_package =
+            keys::PublicKeyPackage::new(tweaked_verifying_shares, tweaked_verifying_key);
+
+        Ok((key_package, public_key_package))
+    }
 }
 
 impl RandomizedCiphersuite for Secp256K1Sha256 {
diff --git a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
index ff497e9a..048f620e 100644
--- a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
+++ b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
@@ -7,7 +7,7 @@
     "hash": "SHA-256"
   },
   "inputs": {
-    "verifying_key": "034a48daffc43b47b42695611942c481aecffb9137686ad0b3e0ab8e1f1dab0293",
+    "verifying_key": "02409611d2fd36025b75caa15f1d70f6d5cfea9cc4254d29580075fdf832d934db",
     "1": {
       "identifier": 1,
       "signing_key": "68e3f6904c6043973515a36bf7801a71597da35733f21305d75a5234f06e4529",
@@ -18,8 +18,8 @@
         "2": "1dd3cb3e2370e6af22917415f0ad584514807b58b3cc40d2230a26e115f02771",
         "3": "dd25ee86acd01f996618aa0d1153f5e8fbc929a8e8a18b8f0a15f91d087217e2"
       },
-      "verifying_share": "03e2eada7cdb20ec24babb687eb633580c977148c70254700f1ad4a931316dc6d9",
-      "signing_share": "89b08895c083bb6a00de882d0e6ff2a20a1878b5e8c0c5aba5983100e3c45d0c"
+      "verifying_share": "03487e21b8658bffc7903fa2f6435bdae7a417990a27698c273a14f72bece665f3",
+      "signing_share": "051f56860fd1225af141494c573ce33da026e1c5f8c5099b73c9ad28675a8389"
     },
     "2": {
       "identifier": 2,
@@ -31,8 +31,8 @@
         "1": "b489a711942526abbb5330a8215d2e740f7dbddec3452006993a8cea3ac278cb",
         "3": "20255dc07b1fb78bdf90bd85fd2389c988c8250faee11826656a09142fa9fc97"
       },
-      "verifying_share": "0312705f7560a146760034ebdd103277e184ce81d2ba6a67a43f0b3f39410cc396",
-      "signing_share": "ea3cdccc32746d918c2910295b0a03dfa1c94f01d20f860c6fe8c22fb6c0d831"
+      "verifying_share": "02aab83a9c57284041ae6add2d5e2ea43d715c6607e03c4c22ca5aae10086076f4",
+      "signing_share": "65abaabc81c1d4827c8bd148a3d6f47b37d7b811e213c9fc3e1a3e573a56feae"
     },
     "3": {
       "identifier": 3,
@@ -44,8 +44,8 @@
         "1": "da5c7f5238079835fe71f746364bb8756a7dcb228aeea686fa2aaa44dfec929c",
         "2": "0d47e4b622ee3804bff8cfe088653efefe865cce0c065aecbf7e318182b89e2d"
       },
-      "verifying_share": "036607b45621ce6840d999980c9c74d69a15fa5a246e852ee2ca6924ce65fa299b",
-      "signing_share": "4ac93102a4651fb917739825a7a4151e7ecb48670c15a6317a66f4d1b9871215"
+      "verifying_share": "031f1571625e54d9bb0e58e228abe5430460ab9d414bced42250fd09526476290b",
+      "signing_share": "c637fef2f3b286aa07d65944f07105b8cf888e5dcb628a5d086acf860d5379d3"
     }
   }
 }

From 404ba69862562c502f3d23e8b46acfaea07092e4 Mon Sep 17 00:00:00 2001
From: Conrado Gouvea <conradoplg@gmail.com>
Date: Fri, 18 Oct 2024 18:56:13 -0300
Subject: [PATCH 06/13] add interoperability tests

---
 frost-secp256k1-tr/Cargo.toml                 |  1 +
 frost-secp256k1-tr/tests/helpers/mod.rs       | 19 ++++++++
 .../tests/interoperability_tests.rs           | 43 +++++++++++++++++++
 frost-secp256k1-tr/tests/tweaking_tests.rs    |  7 +++
 4 files changed, 70 insertions(+)
 create mode 100644 frost-secp256k1-tr/tests/interoperability_tests.rs

diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml
index ac0f26ae..99d93687 100644
--- a/frost-secp256k1-tr/Cargo.toml
+++ b/frost-secp256k1-tr/Cargo.toml
@@ -40,6 +40,7 @@ lazy_static = "1.4"
 proptest = "1.0"
 rand = "0.8"
 rand_chacha = "0.3"
+secp256k1 = "0.30.0"
 serde_json = "1.0"
 
 [features]
diff --git a/frost-secp256k1-tr/tests/helpers/mod.rs b/frost-secp256k1-tr/tests/helpers/mod.rs
index 2f415966..6b899101 100644
--- a/frost-secp256k1-tr/tests/helpers/mod.rs
+++ b/frost-secp256k1-tr/tests/helpers/mod.rs
@@ -2,4 +2,23 @@
 // and each one uses only part of the module.
 #![allow(dead_code)]
 
+use frost_secp256k1_tr::Secp256K1Sha256;
+use secp256k1::Secp256k1;
+
 pub mod samples;
+
+pub fn verify_signature(
+    msg: &[u8],
+    group_signature: frost_core::Signature<Secp256K1Sha256>,
+    group_pubkey: frost_core::VerifyingKey<Secp256K1Sha256>,
+) {
+    let secp = Secp256k1::new();
+    let sig = secp256k1::schnorr::Signature::from_byte_array(
+        group_signature.serialize().unwrap().try_into().unwrap(),
+    );
+    let pubkey = secp256k1::XOnlyPublicKey::from_byte_array(
+        &group_pubkey.serialize().unwrap()[1..33].try_into().unwrap(),
+    )
+    .unwrap();
+    secp.verify_schnorr(&sig, msg, &pubkey).unwrap();
+}
diff --git a/frost-secp256k1-tr/tests/interoperability_tests.rs b/frost-secp256k1-tr/tests/interoperability_tests.rs
new file mode 100644
index 00000000..165d7b6c
--- /dev/null
+++ b/frost-secp256k1-tr/tests/interoperability_tests.rs
@@ -0,0 +1,43 @@
+use frost_secp256k1_tr::*;
+
+use crate::Secp256K1Sha256;
+use rand::thread_rng;
+
+mod helpers;
+
+#[test]
+fn check_interoperability_in_sign_with_dkg() {
+    let rng = thread_rng();
+
+    // Test with multiple keys/signatures to better exercise the key generation
+    // and the interoperability check. A smaller number of iterations is used
+    // because DKG takes longer and otherwise the test would be too slow.
+    for _ in 0..32 {
+        let (target, group_signature, group_pubkey) =
+            frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
+                rng.clone(),
+                b"message".into(),
+            );
+
+        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+    }
+}
+
+#[test]
+fn check_interoperability_in_sign_with_dealer() {
+    let rng = thread_rng();
+
+    // Test with multiple keys/signatures to better exercise the key generation
+    // and the interoperability check.
+    for _ in 0..256 {
+        let (target, group_signature, group_pubkey) =
+            frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
+                rng.clone(),
+                b"message".into(),
+            );
+
+        // Check that the threshold signature can be verified by the `ed25519_dalek` crate
+        // public key (interoperability test)
+        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+    }
+}
diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs
index ee31e487..7ae33ad5 100644
--- a/frost-secp256k1-tr/tests/tweaking_tests.rs
+++ b/frost-secp256k1-tr/tests/tweaking_tests.rs
@@ -1,5 +1,8 @@
 use frost_secp256k1_tr::*;
 use rand::thread_rng;
+use secp256k1::Secp256k1;
+
+mod helpers;
 
 #[test]
 fn check_tweaked_signing_key() {
@@ -11,6 +14,8 @@ fn check_tweaked_signing_key() {
 
     let untweaked_signature = signing_key.sign(&mut rng, &message);
 
+    helpers::verify_signature(message, untweaked_signature, untweaked_verifying_key);
+
     untweaked_verifying_key
         .verify(&message, &untweaked_signature)
         .expect("untweaked signature should be valid under untweaked verifying key");
@@ -39,6 +44,8 @@ fn check_tweaked_signing_key() {
             "tweaked signature should be valid under untweaked verifying key\
              when signing params are provided",
         );
+
+    helpers::verify_signature(message, tweaked_signature, tweaked_verifying_key);
 }
 
 #[test]

From 8e22bc7dc23893cb5d8e73e1d93da65b47b4400a Mon Sep 17 00:00:00 2001
From: Conrado Gouvea <conradoplg@gmail.com>
Date: Fri, 18 Oct 2024 21:19:51 -0300
Subject: [PATCH 07/13] cleanup taproot implementation to minimize impact in
 frost_core

---
 frost-core/src/batch.rs                       |  16 +-
 frost-core/src/keys.rs                        |  12 +-
 frost-core/src/keys/dkg.rs                    |  44 +-
 frost-core/src/lib.rs                         | 133 ++--
 frost-core/src/round1.rs                      |  23 +-
 frost-core/src/round2.rs                      |  32 +-
 frost-core/src/signature.rs                   |  50 ++
 frost-core/src/signing_key.rs                 |  27 +-
 frost-core/src/tests/ciphersuite_generic.rs   |  39 +-
 frost-core/src/tests/refresh.rs               |  13 +-
 frost-core/src/traits.rs                      | 393 +++++------
 frost-core/src/verifying_key.rs               |  37 +-
 frost-ed25519/src/lib.rs                      |  10 +-
 frost-ed25519/tests/integration_tests.rs      |  12 +-
 frost-ed25519/tests/interoperability_tests.rs |  10 +-
 frost-ed25519/tests/recreation_tests.rs       |   4 +-
 frost-ed25519/tests/serde_tests.rs            |  20 +-
 frost-ed448/src/lib.rs                        |  10 +-
 frost-ed448/tests/integration_tests.rs        |  12 +-
 frost-ed448/tests/recreation_tests.rs         |   4 +-
 frost-ed448/tests/serde_tests.rs              |  20 +-
 frost-p256/src/lib.rs                         |  10 +-
 frost-p256/tests/integration_tests.rs         |  11 +-
 frost-p256/tests/recreation_tests.rs          |   4 +-
 frost-p256/tests/serde_tests.rs               |  20 +-
 frost-ristretto255/src/lib.rs                 |  10 +-
 frost-ristretto255/tests/integration_tests.rs |  12 +-
 frost-ristretto255/tests/recreation_tests.rs  |   4 +-
 frost-ristretto255/tests/serde_tests.rs       |  20 +-
 frost-secp256k1-tr/README.md                  |   2 +-
 frost-secp256k1-tr/benches/bench.rs           |   4 +-
 frost-secp256k1-tr/src/keys/repairable.rs     |  16 +-
 frost-secp256k1-tr/src/lib.rs                 | 661 +++++++++++-------
 frost-secp256k1-tr/src/tests/batch.rs         |   6 +-
 .../src/tests/coefficient_commitment.rs       |   8 +-
 frost-secp256k1-tr/src/tests/deserialize.rs   |  12 +-
 frost-secp256k1-tr/src/tests/proptests.rs     |   2 +-
 .../src/tests/vss_commitment.rs               |  12 +-
 frost-secp256k1-tr/tests/helpers/mod.rs       |   6 +-
 frost-secp256k1-tr/tests/helpers/samples.rs   |   2 +-
 frost-secp256k1-tr/tests/integration_tests.rs |  88 ++-
 .../tests/interoperability_tests.rs           |  28 +-
 frost-secp256k1-tr/tests/recreation_tests.rs  |   4 +-
 .../tests/rerandomized_tests.rs               |   4 +-
 frost-secp256k1-tr/tests/serde_tests.rs       |  20 +-
 ...igning_package_postcard_serialization.snap |   2 +-
 frost-secp256k1-tr/tests/tweaking_tests.rs    | 163 ++---
 frost-secp256k1/src/lib.rs                    |  10 +-
 frost-secp256k1/tests/integration_tests.rs    |  12 +-
 frost-secp256k1/tests/recreation_tests.rs     |   4 +-
 frost-secp256k1/tests/serde_tests.rs          |  20 +-
 gencode/src/main.rs                           |  23 +-
 52 files changed, 1010 insertions(+), 1111 deletions(-)

diff --git a/frost-core/src/batch.rs b/frost-core/src/batch.rs
index fb5ada0e..a30a109e 100644
--- a/frost-core/src/batch.rs
+++ b/frost-core/src/batch.rs
@@ -20,7 +20,6 @@ use crate::{scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, *};
 pub struct Item<C: Ciphersuite> {
     vk: VerifyingKey<C>,
     sig: Signature<C>,
-    sig_params: C::SigningParameters,
     c: Challenge<C>,
 }
 
@@ -34,13 +33,12 @@ where
     where
         M: AsRef<[u8]>,
     {
-        let sig_target = SigningTarget::from_message(msg);
-        let c = <C>::challenge(&sig.R, &vk, &sig_target)?;
+        let (msg, sig, vk) = <C>::pre_verify(msg.as_ref(), &sig, &vk)?;
+        let c = <C>::challenge(&sig.R, &vk, &msg)?;
 
         Ok(Self {
-            vk,
-            sig,
-            sig_params: sig_target.sig_params,
+            vk: *vk,
+            sig: *sig,
             c,
         })
     }
@@ -58,8 +56,7 @@ where
     /// requires borrowing the message data, the `Item` type is unlinked
     /// from the lifetime of the message.
     pub fn verify_single(self) -> Result<(), Error<C>> {
-        self.vk
-            .verify_prehashed(self.c, &self.sig, &self.sig_params)
+        self.vk.verify_prehashed(self.c, &self.sig)
     }
 }
 
@@ -128,7 +125,6 @@ where
         for item in self.signatures.iter() {
             let z = item.sig.z;
             let R = item.sig.R;
-            let vk = <C>::effective_pubkey_element(&item.vk, &item.sig_params);
 
             let blind = <<C::Group as Group>::Field>::random(&mut rng);
 
@@ -139,7 +135,7 @@ where
             Rs.push(R);
 
             VK_coeffs.push(<<C::Group as Group>::Field>::zero() + (blind * item.c.0));
-            VKs.push(vk);
+            VKs.push(item.vk.to_element());
         }
 
         let scalars = core::iter::once(&P_coeff_acc)
diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs
index f9cf0f08..45a023d3 100644
--- a/frost-core/src/keys.rs
+++ b/frost-core/src/keys.rs
@@ -119,11 +119,6 @@ where
     pub(crate) fn from_coefficients(coefficients: &[Scalar<C>], peer: Identifier<C>) -> Self {
         Self::new(evaluate_polynomial(peer, coefficients))
     }
-
-    /// Returns negated SigningShare
-    pub fn negate(&mut self) {
-        self.0 .0 = <<C::Group as Group>::Field>::negate(&self.to_scalar());
-    }
 }
 
 impl<C> Debug for SigningShare<C>
@@ -333,7 +328,7 @@ where
     }
 
     /// Returns VerifiableSecretSharingCommitment from a iterator of serialized
-    /// CoefficientCommitments (e.g. a Vec<Vec<u8>>).
+    /// CoefficientCommitments (e.g. a `Vec<Vec<u8>>`).
     pub fn deserialize<I, V>(serialized_coefficient_commitments: I) -> Result<Self, Error<C>>
     where
         I: IntoIterator<Item = V>,
@@ -635,11 +630,6 @@ where
             min_signers,
         }
     }
-
-    /// Negate `SigningShare`.
-    pub fn negate_signing_share(&mut self) {
-        self.signing_share.negate();
-    }
 }
 
 #[cfg(feature = "serialization")]
diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs
index 712a516a..cd58e8f5 100644
--- a/frost-core/src/keys/dkg.rs
+++ b/frost-core/src/keys/dkg.rs
@@ -38,7 +38,7 @@ use rand_core::{CryptoRng, RngCore};
 
 use crate::{
     Challenge, Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, Signature,
-    SigningKey,
+    SigningKey, VerifyingKey,
 };
 
 #[cfg(feature = "serialization")]
@@ -322,7 +322,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
 /// Generates the challenge for the proof of knowledge to a secret for the DKG.
 fn challenge<C>(
     identifier: Identifier<C>,
-    verifying_key: &Element<C>,
+    verifying_key: &VerifyingKey<C>,
     R: &Element<C>,
 ) -> Result<Challenge<C>, Error<C>>
 where
@@ -331,7 +331,7 @@ where
     let mut preimage = vec![];
 
     preimage.extend_from_slice(identifier.serialize().as_ref());
-    preimage.extend_from_slice(<C::Group>::serialize(verifying_key)?.as_ref());
+    preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
     preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
 
     Ok(Challenge(
@@ -354,23 +354,12 @@ pub(crate) fn compute_proof_of_knowledge<C: Ciphersuite, R: RngCore + CryptoRng>
     // > a_{i0} by calculating σ_i = (R_i, μ_i), such that k ← Z_q, R_i = g^k,
     // > c_i = H(i, Φ, g^{a_{i0}} , R_i), μ_i = k + a_{i0} · c_i, with Φ being
     // > a context string to prevent replay attacks.
-    let mut k = <<C::Group as Group>::Field>::random(&mut rng);
-    let mut R_i = <C::Group>::generator() * k;
-    k = <C>::effective_nonce_secret(k, &R_i);
-    R_i = <C>::effective_nonce_element(R_i);
-
-    let verifying_key = commitment.verifying_key()?;
-    let sig_params = Default::default();
-
-    let phi_ell0 = <C>::effective_pubkey_element(&verifying_key, &sig_params);
-
-    let c_i = challenge::<C>(identifier, &phi_ell0, &R_i)?;
+    let (k, R_i) = <C>::generate_nonce(&mut rng);
+    let c_i = challenge::<C>(identifier, &commitment.verifying_key()?, &R_i)?;
     let a_i0 = *coefficients
         .first()
         .expect("coefficients must have at least one element");
-    let a_i0_effective = <C>::effective_secret_key(a_i0, &verifying_key, &sig_params);
-
-    let mu_i = k + a_i0_effective * c_i.0;
+    let mu_i = k + a_i0 * c_i.0;
     Ok(Signature { R: R_i, z: mu_i })
 }
 
@@ -390,12 +379,9 @@ pub(crate) fn verify_proof_of_knowledge<C: Ciphersuite>(
     let ell = identifier;
     let R_ell = proof_of_knowledge.R;
     let mu_ell = proof_of_knowledge.z;
-
-    let verifying_key = commitment.verifying_key()?;
-    let phi_ell0 = <C>::effective_pubkey_element(&verifying_key, &Default::default());
+    let phi_ell0 = commitment.verifying_key()?;
     let c_ell = challenge::<C>(ell, &phi_ell0, &R_ell)?;
-
-    if R_ell != <C::Group>::generator() * mu_ell - phi_ell0 * c_ell.0 {
+    if R_ell != <C::Group>::generator() * mu_ell - phi_ell0.to_element() * c_ell.0 {
         return Err(Error::InvalidProofOfKnowledge { culprit: ell });
     }
     Ok(())
@@ -562,12 +548,16 @@ pub fn part3<C: Ciphersuite>(
             &round2_secret_package.commitment,
         )))
         .collect();
+    let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
 
-    C::dkg_output_finalize(
-        round2_secret_package.identifier,
-        commitments,
+    let key_package = KeyPackage {
+        header: Header::default(),
+        identifier: round2_secret_package.identifier,
         signing_share,
         verifying_share,
-        round2_secret_package.min_signers,
-    )
+        verifying_key: public_key_package.verifying_key,
+        min_signers: round2_secret_package.min_signers,
+    };
+
+    C::post_dkg(key_package, public_key_package)
 }
diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs
index 434a3816..e373e27a 100644
--- a/frost-core/src/lib.rs
+++ b/frost-core/src/lib.rs
@@ -57,7 +57,7 @@ use scalar_mul::VartimeMultiscalarMul;
 pub use serde;
 pub use signature::Signature;
 pub use signing_key::SigningKey;
-pub use traits::{Ciphersuite, Element, Field, Group, Scalar, SigningParameters};
+pub use traits::{Ciphersuite, Context, Element, Field, Group, Scalar};
 pub use verifying_key::VerifyingKey;
 
 /// A type refinement for the scalar field element representing the per-message _[challenge]_.
@@ -134,6 +134,8 @@ where
 /// Generates a random nonzero scalar.
 ///
 /// It assumes that the Scalar Eq/PartialEq implementation is constant-time.
+#[cfg_attr(feature = "internals", visibility::make(pub))]
+#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
 pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R) -> Scalar<C> {
     loop {
         let scalar = <<C::Group as Group>::Field>::random(rng);
@@ -351,54 +353,6 @@ fn derive_interpolating_value<C: Ciphersuite>(
     )
 }
 
-/// The data which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
-#[derive(Clone, Debug, PartialEq, Eq, Getters)]
-pub struct SigningTarget<C: Ciphersuite> {
-    #[cfg_attr(
-        feature = "serde",
-        serde(
-            serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
-            deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
-        )
-    )]
-    message: Vec<u8>,
-
-    #[cfg_attr(feature = "serde", serde(default))]
-    sig_params: C::SigningParameters,
-}
-
-impl<C: Ciphersuite> SigningTarget<C> {
-    /// Construct a signing target from a message and additional signing parameters.
-    pub fn new<T: AsRef<[u8]>, P: Into<C::SigningParameters>>(
-        message: T,
-        sig_params: P,
-    ) -> SigningTarget<C> {
-        SigningTarget {
-            message: message.as_ref().to_vec(),
-            sig_params: sig_params.into(),
-        }
-    }
-
-    /// Constructs a signing target from an arbitrary message.
-    /// This populates [the `sig_params` field][SigningTarget::sig_params] with
-    /// the [`Default`] instance of the [`Ciphersuite::SigningParameters`].
-    pub fn from_message<T: AsRef<[u8]>>(message: T) -> SigningTarget<C> {
-        SigningTarget {
-            message: message.as_ref().to_vec(),
-            sig_params: C::SigningParameters::default(),
-        }
-    }
-}
-
-impl<C: Ciphersuite, T: AsRef<[u8]>> From<T> for SigningTarget<C> {
-    fn from(message: T) -> Self {
-        Self::from_message(message)
-    }
-}
-
 /// Generated by the coordinator of the signing operation and distributed to
 /// each signing party
 #[derive(Clone, Debug, PartialEq, Eq, Getters)]
@@ -412,9 +366,18 @@ pub struct SigningPackage<C: Ciphersuite> {
     /// The set of commitments participants published in the first round of the
     /// protocol.
     signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
-    /// The message and parameters which each participant will use to sign.
-    /// Each signer should perform protocol-specific verification on the signing target.
-    sig_target: SigningTarget<C>,
+    /// Message which each participant will sign.
+    ///
+    /// Each signer should perform protocol-specific verification on the
+    /// message.
+    #[cfg_attr(
+        feature = "serde",
+        serde(
+            serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
+            deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
+        )
+    )]
+    message: Vec<u8>,
 }
 
 impl<C> SigningPackage<C>
@@ -424,19 +387,14 @@ where
     /// Create a new `SigningPackage`
     ///
     /// The `signing_commitments` are sorted by participant `identifier`.
-    ///
-    /// The `sig_target` can be any bytes-like type that implements `AsRef<[u8]>`.
-    /// Some ciphersuites like `frost-secp256k1-tr` allow customization of the signing
-    /// process by embedding additional parameters into a [`SigningTarget`], but this
-    /// is optional and not required by most ciphersuites.
     pub fn new(
         signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
-        sig_target: impl Into<SigningTarget<C>>,
+        message: &[u8],
     ) -> SigningPackage<C> {
         SigningPackage {
             header: Header::default(),
             signing_commitments,
-            sig_target: sig_target.into(),
+            message: message.to_vec(),
         }
     }
 
@@ -448,11 +406,6 @@ where
         self.signing_commitments.get(identifier).copied()
     }
 
-    /// Returns the message to be signed.
-    pub fn message(&self) -> &[u8] {
-        &self.sig_target.message
-    }
-
     /// Compute the preimages to H1 to compute the per-signer binding factors
     // We separate this out into its own method so it can be tested
     #[cfg_attr(feature = "internals", visibility::make(pub))]
@@ -473,7 +426,7 @@ where
         // The message is hashed with H4 to force the variable-length message
         // into a fixed-length byte string, same for hashing the variable-sized
         // (between runs of the protocol) set of group commitments, but with H5.
-        binding_factor_input_prefix.extend_from_slice(C::H4(self.message()).as_ref());
+        binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref());
         binding_factor_input_prefix.extend_from_slice(
             C::H5(&round1::encode_group_commitments(self.signing_commitments())?[..]).as_ref(),
         );
@@ -524,10 +477,10 @@ where
         self.0
     }
 
-    /// Check if group commitment is odd
+    /// Return the underlying element.
     #[cfg(feature = "internals")]
-    pub fn y_is_odd(&self) -> bool {
-        <C::Group as Group>::y_is_odd(&self.0)
+    pub fn from_element(element: Element<C>) -> Self {
+        Self(element)
     }
 }
 
@@ -630,13 +583,18 @@ where
         return Err(Error::UnknownIdentifier);
     }
 
+    let mut ctx = C::Context::default();
+
+    let (signing_package, signature_shares, pubkeys) =
+        <C>::pre_aggregate(&mut ctx, signing_package, signature_shares, pubkeys)?;
+
     // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
     // binding factor.
     let binding_factor_list: BindingFactorList<C> =
-        compute_binding_factor_list(signing_package, &pubkeys.verifying_key, &[])?;
+        compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
     // Compute the group commitment from signing commitments produced in round one.
-    let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
-    let R = <C>::effective_nonce_element(group_commitment.0);
+    let group_commitment =
+        <C>::compute_group_commitment(&mut ctx, &signing_package, &binding_factor_list)?;
 
     // The aggregation of the signature shares by summing them up, resulting in
     // a plain Schnorr signature.
@@ -650,13 +608,15 @@ where
         z = z + signature_share.to_scalar();
     }
 
-    let signature: Signature<C> =
-        <C>::aggregate_sig_finalize(z, R, &pubkeys.verifying_key, &signing_package.sig_target)?;
+    let signature = Signature {
+        R: group_commitment.0,
+        z,
+    };
 
     // Verify the aggregate signature
     let verification_result = pubkeys
         .verifying_key
-        .verify(signing_package.sig_target.clone(), &signature);
+        .verify(signing_package.message(), &signature);
 
     // Only if the verification of the aggregate signature failed; verify each share to find the cheater.
     // This approach is more efficient since we don't need to verify all shares
@@ -664,11 +624,11 @@ where
     #[cfg(feature = "cheater-detection")]
     if verification_result.is_err() {
         detect_cheater(
-            group_commitment,
-            &R,
-            pubkeys,
-            signing_package,
-            signature_shares,
+            &mut ctx,
+            &group_commitment,
+            &pubkeys,
+            &signing_package,
+            &signature_shares,
             &binding_factor_list,
         )?;
     }
@@ -681,8 +641,8 @@ where
 /// Optional cheater detection feature
 /// Each share is verified to find the cheater
 fn detect_cheater<C: Ciphersuite>(
-    group_commitment: GroupCommitment<C>,
-    effective_group_commitment: &Element<C>,
+    ctx: &mut C::Context,
+    group_commitment: &GroupCommitment<C>,
     pubkeys: &keys::PublicKeyPackage<C>,
     signing_package: &SigningPackage<C>,
     signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
@@ -690,9 +650,9 @@ fn detect_cheater<C: Ciphersuite>(
 ) -> Result<(), Error<C>> {
     // Compute the per-message challenge.
     let challenge = <C>::challenge(
-        effective_group_commitment,
+        &group_commitment.0,
         &pubkeys.verifying_key,
-        &signing_package.sig_target,
+        signing_package.message(),
     )?;
 
     // Verify the signature shares.
@@ -718,15 +678,14 @@ fn detect_cheater<C: Ciphersuite>(
             .to_group_commitment_share(binding_factor);
 
         // Compute relation values to verify this signature share.
-        signature_share.verify(
+        <C>::verify_share(
+            ctx,
+            signature_share,
             *signature_share_identifier,
             &R_share,
             signer_pubkey,
             lambda_i,
             &challenge,
-            &group_commitment,
-            &pubkeys.verifying_key,
-            &signing_package.sig_target.sig_params,
         )?;
     }
 
diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs
index ce5e28a5..694043b2 100644
--- a/frost-core/src/round1.rs
+++ b/frost-core/src/round1.rs
@@ -54,21 +54,22 @@ where
         Self::nonce_generate_from_random_bytes(secret, random_bytes)
     }
 
+    /// Create a nonce from a scalar.
+    #[cfg_attr(feature = "internals", visibility::make(pub))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
     fn from_scalar(scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar) -> Self {
         Self(SerializableScalar(scalar))
     }
 
+    /// Convert a nonce into a scalar.
+    #[cfg_attr(feature = "internals", visibility::make(pub))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
     pub(crate) fn to_scalar(
         self,
     ) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
         self.0 .0
     }
 
-    /// Negate `Nonce`.
-    pub fn negate(&mut self) {
-        self.0 .0 = <<C::Group as Group>::Field>::negate(&self.to_scalar());
-    }
-
     /// Generates a nonce from the given random bytes.
     /// This function allows testing and MUST NOT be made public.
     pub(crate) fn nonce_generate_from_random_bytes(
@@ -286,12 +287,6 @@ where
     pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
         Deserialize::deserialize(bytes)
     }
-
-    /// Negate `SigningNonces`.
-    pub fn negate_nonces(&mut self) {
-        self.binding.negate();
-        self.hiding.negate();
-    }
 }
 
 /// Published by each participant in the first round of the signing protocol.
@@ -370,6 +365,12 @@ where
 pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) Element<C>);
 
 impl<C: Ciphersuite> GroupCommitmentShare<C> {
+    /// Create from an element.
+    #[cfg_attr(feature = "internals", visibility::make(pub))]
+    pub(crate) fn from_element(element: Element<C>) -> Self {
+        Self(element)
+    }
+
     /// Return the underlying element.
     #[cfg_attr(feature = "internals", visibility::make(pub))]
     pub(crate) fn to_element(self) -> Element<C> {
diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs
index 00f9911d..012cb641 100644
--- a/frost-core/src/round2.rs
+++ b/frost-core/src/round2.rs
@@ -62,7 +62,6 @@ where
     #[cfg(any(feature = "cheater-detection", feature = "internals"))]
     #[cfg_attr(feature = "internals", visibility::make(pub))]
     #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
-    #[allow(clippy::too_many_arguments)]
     pub(crate) fn verify(
         &self,
         identifier: Identifier<C>,
@@ -70,16 +69,10 @@ where
         verifying_share: &frost::keys::VerifyingShare<C>,
         lambda_i: Scalar<C>,
         challenge: &Challenge<C>,
-        group_commitment: &frost::GroupCommitment<C>,
-        verifying_key: &frost::VerifyingKey<C>,
-        sig_params: &C::SigningParameters,
     ) -> Result<(), Error<C>> {
-        let commitment_share =
-            <C>::effective_commitment_share(*group_commitment_share, group_commitment);
-        let vsh = <C>::effective_verifying_share(verifying_share, verifying_key, sig_params);
-
         if (<C::Group>::generator() * self.to_scalar())
-            != (commitment_share + (vsh * challenge.0 * lambda_i))
+            != (group_commitment_share.to_element()
+                + (verifying_share.to_element() * challenge.0 * lambda_i))
         {
             return Err(Error::InvalidSignatureShare {
                 culprit: identifier,
@@ -150,37 +143,42 @@ pub fn sign<C: Ciphersuite>(
         return Err(Error::IncorrectCommitment);
     }
 
+    let mut ctx = C::Context::default();
+
+    let (signing_package, signer_nonces, key_package) =
+        <C>::pre_sign(&mut ctx, signing_package, signer_nonces, key_package)?;
+
     // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
     // binding factor.
     let binding_factor_list: BindingFactorList<C> =
-        compute_binding_factor_list(signing_package, &key_package.verifying_key, &[])?;
+        compute_binding_factor_list(&signing_package, &key_package.verifying_key, &[])?;
     let binding_factor: frost::BindingFactor<C> = binding_factor_list
         .get(&key_package.identifier)
         .ok_or(Error::UnknownIdentifier)?
         .clone();
 
     // Compute the group commitment from signing commitments produced in round one.
-    let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
+    let group_commitment =
+        <C>::compute_group_commitment(&mut ctx, &signing_package, &binding_factor_list)?;
 
     // Compute Lagrange coefficient.
-    let lambda_i = frost::derive_interpolating_value(key_package.identifier(), signing_package)?;
+    let lambda_i = frost::derive_interpolating_value(key_package.identifier(), &signing_package)?;
 
     // Compute the per-message challenge.
     let challenge = <C>::challenge(
         &group_commitment.0,
         &key_package.verifying_key,
-        &signing_package.sig_target,
+        signing_package.message(),
     )?;
 
     // Compute the Schnorr signature share.
     let signature_share = <C>::compute_signature_share(
-        signer_nonces,
+        &mut ctx,
+        &signer_nonces,
         binding_factor,
-        group_commitment,
         lambda_i,
-        key_package,
+        &key_package,
         challenge,
-        &signing_package.sig_target.sig_params,
     );
 
     Ok(signature_share)
diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs
index f03d1e45..91d3ade9 100644
--- a/frost-core/src/signature.rs
+++ b/frost-core/src/signature.rs
@@ -30,11 +30,61 @@ where
         Self { R, z }
     }
 
+    /// Converts default-encoded bytes as
+    /// [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
+    #[cfg(feature = "internals")]
+    pub fn default_deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
+        // To compute the expected length of the encoded point, encode the generator
+        // and get its length. Note that we can't use the identity because it can be encoded
+        // shorter in some cases (e.g. P-256, which uses SEC1 encoding).
+        let generator = <C::Group>::generator();
+        let mut R_bytes = Vec::from(<C::Group>::serialize(&generator)?.as_ref());
+        let R_bytes_len = R_bytes.len();
+
+        let one = <<C::Group as Group>::Field as Field>::zero();
+        let mut z_bytes =
+            Vec::from(<<C::Group as Group>::Field as Field>::serialize(&one).as_ref());
+        let z_bytes_len = z_bytes.len();
+
+        if bytes.len() != R_bytes_len + z_bytes_len {
+            return Err(Error::MalformedSignature);
+        }
+
+        R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?);
+
+        let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
+
+        // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]`
+        z_bytes[..].copy_from_slice(
+            bytes
+                .get(R_bytes_len..R_bytes_len + z_bytes_len)
+                .ok_or(Error::MalformedSignature)?,
+        );
+
+        let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
+
+        Ok(Self {
+            R: <C::Group>::deserialize(R_serialization)?,
+            z: <<C::Group as Group>::Field>::deserialize(z_serialization)?,
+        })
+    }
+
     /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
     pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
         C::deserialize_signature(bytes)
     }
 
+    /// Converts this signature to its default byte serialization.
+    #[cfg(feature = "internals")]
+    pub fn default_serialize(&self) -> Result<Vec<u8>, Error<C>> {
+        let mut bytes = Vec::<u8>::new();
+
+        bytes.extend(<C::Group>::serialize(&self.R)?.as_ref());
+        bytes.extend(<<C::Group as Group>::Field>::serialize(&self.z).as_ref());
+
+        Ok(bytes)
+    }
+
     /// Converts this signature to its byte serialization.
     pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
         <C>::serialize_signature(self)
diff --git a/frost-core/src/signing_key.rs b/frost-core/src/signing_key.rs
index c562c057..93a096c3 100644
--- a/frost-core/src/signing_key.rs
+++ b/frost-core/src/signing_key.rs
@@ -6,7 +6,7 @@ use rand_core::{CryptoRng, RngCore};
 
 use crate::{
     random_nonzero, serialization::SerializableScalar, Challenge, Ciphersuite, Error, Field, Group,
-    Scalar, Signature, SigningTarget, VerifyingKey,
+    Scalar, Signature, VerifyingKey,
 };
 
 /// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`].
@@ -40,25 +40,24 @@ where
     }
 
     /// Create a signature `msg` using this `SigningKey`.
-    pub fn sign<R: RngCore + CryptoRng>(
-        &self,
-        mut rng: R,
-        sig_target: impl Into<SigningTarget<C>>,
-    ) -> Signature<C> {
-        let sig_target = sig_target.into();
+    pub fn sign<R: RngCore + CryptoRng>(&self, rng: R, message: &[u8]) -> Signature<C> {
+        <C>::single_sign(self, rng, message)
+    }
 
+    /// Create a signature `msg` using this `SigningKey` using the default
+    /// signing.
+    #[cfg(feature = "internals")]
+    pub fn default_sign<R: RngCore + CryptoRng>(&self, mut rng: R, message: &[u8]) -> Signature<C> {
         let public = VerifyingKey::<C>::from(*self);
-        let secret = <C>::effective_secret_key(self.scalar, &public, &sig_target.sig_params);
 
-        let mut k = random_nonzero::<C, R>(&mut rng);
-        let mut R = <C::Group>::generator() * k;
-        k = <C>::effective_nonce_secret(k, &R);
-        R = <C>::effective_nonce_element(R);
+        let (k, R) = <C>::generate_nonce(&mut rng);
 
         // Generate Schnorr challenge
-        let c: Challenge<C> = <C>::challenge(&R, &public, &sig_target).expect("should not return error since that happens only if one of the inputs is the identity. R is not since k is nonzero. The verifying_key is not because signing keys are not allowed to be zero.");
+        let c: Challenge<C> = <C>::challenge(&R, &public, message).expect("should not return error since that happens only if one of the inputs is the identity. R is not since k is nonzero. The verifying_key is not because signing keys are not allowed to be zero.");
+
+        let z = k + (c.0 * self.scalar);
 
-        <C>::single_sig_finalize(k, R, secret, &c, &public, &sig_target.sig_params)
+        Signature { R, z }
     }
 
     /// Creates a SigningKey from a scalar. Returns an error if the scalar is zero.
diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs
index f5332273..7f164310 100644
--- a/frost-core/src/tests/ciphersuite_generic.rs
+++ b/frost-core/src/tests/ciphersuite_generic.rs
@@ -7,7 +7,7 @@ use crate as frost;
 use crate::round2::SignatureShare;
 use crate::{
     keys::PublicKeyPackage, Error, Field, Group, Identifier, Signature, SigningKey, SigningPackage,
-    SigningTarget, VerifyingKey,
+    VerifyingKey,
 };
 use alloc::vec::Vec;
 use rand_core::{CryptoRng, RngCore};
@@ -102,8 +102,7 @@ pub fn check_share_generation_fails_with_invalid_signers<C: Ciphersuite, R: RngC
 /// Test FROST signing with trusted dealer with a Ciphersuite.
 pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
     mut rng: R,
-    signing_target: SigningTarget<C>,
-) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>) {
+) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
     ////////////////////////////////////////////////////////////////////////////
     // Key generation
     ////////////////////////////////////////////////////////////////////////////
@@ -147,11 +146,10 @@ pub fn check_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
             .collect(),
         &mut rng,
         pubkeys.clone(),
-        signing_target.clone(),
     );
     assert_eq!(r, Err(Error::InvalidSignature));
 
-    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
 }
 
 /// Test FROST signing with trusted dealer fails with invalid numbers of signers.
@@ -196,8 +194,7 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     key_packages: BTreeMap<frost::Identifier<C>, frost::keys::KeyPackage<C>>,
     mut rng: R,
     pubkey_package: PublicKeyPackage<C>,
-    signing_target: SigningTarget<C>,
-) -> Result<(SigningTarget<C>, Signature<C>, VerifyingKey<C>), Error<C>> {
+) -> Result<(Vec<u8>, Signature<C>, VerifyingKey<C>), Error<C>> {
     let mut nonces_map: BTreeMap<frost::Identifier<C>, frost::round1::SigningNonces<C>> =
         BTreeMap::new();
     let mut commitments_map: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
@@ -225,7 +222,8 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     // - decide what message to sign
     // - take one (unused) commitment per signing participant
     let mut signature_shares = BTreeMap::new();
-    let signing_package = frost::SigningPackage::new(commitments_map, signing_target.clone());
+    let message = "message to sign".as_bytes();
+    let signing_package = SigningPackage::new(commitments_map, message);
 
     ////////////////////////////////////////////////////////////////////////////
     // Round 2: each participant generates their signature share
@@ -267,18 +265,11 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     // Aggregate (also verifies the signature shares)
     let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
 
-    // Check that the effective verifying key can be verified against the raw message,
-    // without exposing the SigningParameters.
-    pubkey_package
-        .verifying_key
-        .effective_key(signing_target.sig_params())
-        .verify(signing_target.message(), &group_signature)?;
-
     // Check that the threshold signature can be verified by the group public
     // key (the verification key).
     pubkey_package
         .verifying_key
-        .verify(signing_target.clone(), &group_signature)?;
+        .verify(message, &group_signature)?;
 
     // Check that the threshold signature can be verified by the group public
     // key (the verification key) from KeyPackage.verifying_key
@@ -287,11 +278,11 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
 
         key_package
             .verifying_key
-            .verify(signing_target.clone(), &group_signature)?;
+            .verify(message, &group_signature)?;
     }
 
     Ok((
-        signing_target,
+        message.to_owned(),
         group_signature,
         pubkey_package.verifying_key,
     ))
@@ -311,7 +302,7 @@ fn check_sign_errors<C: Ciphersuite + PartialEq>(
         .find(|&&id| id != key_package.identifier)
         .unwrap();
     commitments.remove(&id);
-    let signing_package = frost::SigningPackage::new(commitments, signing_package.sig_target);
+    let signing_package = frost::SigningPackage::new(commitments, signing_package.message());
 
     let r = frost::round2::sign(&signing_package, &signing_nonces, &key_package);
     assert_eq!(r, Err(Error::IncorrectNumberOfCommitments));
@@ -384,8 +375,7 @@ fn check_aggregate_invalid_share_identifier_for_verifying_shares<C: Ciphersuite
 /// Test FROST signing with DKG with a Ciphersuite.
 pub fn check_sign_with_dkg<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
     mut rng: R,
-    signing_target: SigningTarget<C>,
-) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>)
+) -> (Vec<u8>, Signature<C>, VerifyingKey<C>)
 where
     C::Group: core::cmp::PartialEq,
 {
@@ -542,7 +532,7 @@ where
     let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap());
 
     // Proceed with the signing test.
-    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
 }
 
 /// Check that calling dkg::part3() with distinct sets of participants fail.
@@ -586,8 +576,7 @@ fn check_part3_different_participants<C: Ciphersuite>(
 /// Identifiers.
 pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + CryptoRng>(
     mut rng: R,
-    signing_target: SigningTarget<C>,
-) -> (SigningTarget<C>, Signature<C>, VerifyingKey<C>) {
+) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
     // Check error cases first
     // Check repeated identifiers
 
@@ -653,7 +642,7 @@ pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + Crypt
         let key_package = frost::keys::KeyPackage::try_from(v).unwrap();
         key_packages.insert(k, key_package);
     }
-    check_sign(min_signers, key_packages, rng, pubkeys, signing_target).unwrap()
+    check_sign(min_signers, key_packages, rng, pubkeys).unwrap()
 }
 
 fn check_part2_error<C: Ciphersuite>(
diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs
index 940b0f53..29330b90 100644
--- a/frost-core/src/tests/refresh.rs
+++ b/frost-core/src/tests/refresh.rs
@@ -9,7 +9,7 @@ use crate::keys::refresh::{compute_refreshing_shares, refresh_share};
 use crate::{self as frost};
 use crate::{
     keys::{KeyPackage, PublicKeyPackage, SecretShare},
-    Ciphersuite, Error, Identifier, SigningTarget,
+    Ciphersuite, Error, Identifier,
 };
 
 use super::ciphersuite_generic::check_sign;
@@ -81,16 +81,7 @@ pub fn check_refresh_shares_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
     for (k, v) in new_shares {
         key_packages.insert(k, v.unwrap());
     }
-
-    let signing_target = SigningTarget::from_message(b"hello world");
-    check_sign(
-        MIN_SIGNERS,
-        key_packages,
-        rng,
-        new_pub_key_package,
-        signing_target,
-    )
-    .unwrap();
+    check_sign(MIN_SIGNERS, key_packages, rng, new_pub_key_package).unwrap();
 }
 
 /// We want to check that shares are refreshed with valid signers
diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs
index 73e52c66..2cc23ea9 100644
--- a/frost-core/src/traits.rs
+++ b/frost-core/src/traits.rs
@@ -4,18 +4,19 @@ use core::{
     fmt::Debug,
     ops::{Add, Mul, Sub},
 };
+use std::borrow::Cow;
 
 use alloc::{collections::BTreeMap, vec::Vec};
 use rand_core::{CryptoRng, RngCore};
 
 use crate::{
-    challenge,
-    keys::{
-        KeyPackage, PublicKeyPackage, SigningShare, VerifiableSecretSharingCommitment,
-        VerifyingShare,
-    },
-    round1, round2, BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError,
-    Header, Identifier, Signature, SigningTarget, VerifyingKey,
+    challenge, compute_group_commitment,
+    keys::{KeyPackage, PublicKeyPackage, VerifyingShare},
+    random_nonzero,
+    round1::{self},
+    round2::{self, SignatureShare},
+    BindingFactor, BindingFactorList, Challenge, Error, FieldError, GroupCommitment, GroupError,
+    Identifier, Signature, SigningKey, SigningPackage, VerifyingKey,
 };
 
 /// A prime order finite field GF(q) over which all scalar values for our prime order group can be
@@ -49,11 +50,6 @@ pub trait Field: Copy + Clone {
     /// element is zero.
     fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError>;
 
-    /// Computes the negation of the element of the scalar field
-    fn negate(_scalar: &Self::Scalar) -> Self::Scalar {
-        panic!("Not implemented");
-    }
-
     /// Generate a random scalar from the entire space [0, l-1]
     ///
     /// <https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.6>
@@ -127,11 +123,6 @@ pub trait Group: Copy + Clone + PartialEq {
     /// [`ScalarBaseMult()`]: https://datatracker.ietf.org/doc/html/rfc9591#section-3.1-4.10
     fn generator() -> Self::Element;
 
-    /// Check if element is odd
-    fn y_is_odd(_element: &Self::Element) -> bool {
-        panic!("Not implemented");
-    }
-
     /// A member function of a group _G_ that maps an [`Element`] to a unique
     /// byte array buf of fixed length Ne. This function raises an error if the
     /// element is the identity element of the group.
@@ -152,24 +143,15 @@ pub trait Group: Copy + Clone + PartialEq {
 /// An element of the [`Ciphersuite`] `C`'s [`Group`].
 pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
 
-/// This is a marker trait for types which are passed in to modify the signing logic of a [`Ciphersuite`].
+/// A context which can be used by Ciphersuite implementations to pass context
+/// between methods when using the overriding methods of the Ciphersuite trait.
 ///
-/// If the `serde` feature is enabled, any type implementing this trait must also implement
-/// [`serde::Serialize`] and [`serde::Deserialize`].
-#[cfg(feature = "serde")]
-pub trait SigningParameters:
-    Clone + Debug + Eq + PartialEq + Default + serde::Serialize + for<'d> serde::Deserialize<'d>
-{
-}
-
-/// This is a marker trait for types which are passed in to modify the signing logic of a [`Ciphersuite`].
-///
-/// If the `serde` feature is enabled, any type implementing this trait must also implement
-/// [`serde::Serialize`] and [`serde::Deserialize`].
-#[cfg(not(feature = "serde"))]
-pub trait SigningParameters: Clone + Debug + Eq + PartialEq + Default {}
+/// This trait is implemented for the unit type `()` which is useful for
+/// Ciphersuites implementations that don't care about the Context.
+pub trait Context: Default {}
 
-impl SigningParameters for () {}
+/// Implements Context for the unit type.
+impl Context for () {}
 
 /// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
 /// function.
@@ -193,9 +175,8 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// `Group::ScalarSerialization`
     type SignatureSerialization: AsRef<[u8]> + TryFrom<Vec<u8>>;
 
-    /// Additional parameters which should be provided to the ciphersuite's signing code
-    /// to produce an effective signature. Most ciphersuites will just set this to `()`.
-    type SigningParameters: SigningParameters;
+    /// Optional context. Most ciphersuites will just set this to `()`.
+    type Context: Context;
 
     /// [H1] for a FROST ciphersuite.
     ///
@@ -254,126 +235,160 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
         None
     }
 
-    /// Verify a signature for this ciphersuite. The default implementation uses the "cofactored"
-    /// equation (it multiplies by the cofactor returned by [`Group::cofactor()`]).
+    // The following are optional methods that allow customizing steps of the
+    // protocol if required.
+
+    /// Optional. Do regular (non-FROST) signing with a [`SigningKey`]. Called
+    /// by [`SigningKey::sign()`]. This is not used by FROST. Can be overriden
+    /// if required which is useful if FROST signing has been changed by the
+    /// other Ciphersuite trait methods and regular signing should be changed
+    /// accordingly to match.
+    fn single_sign<R: RngCore + CryptoRng>(
+        signing_key: &SigningKey<Self>,
+        rng: R,
+        message: &[u8],
+    ) -> Signature<Self> {
+        signing_key.default_sign(rng, message)
+    }
+
+    /// Optional. Verify a signature for this ciphersuite. Called by
+    /// [`VerifyingKey::verify()`]. The default implementation uses the
+    /// "cofactored" equation (it multiplies by the cofactor returned by
+    /// [`Group::cofactor()`]).
     ///
     /// # Cryptographic Safety
     ///
-    /// You may override this to provide a tailored implementation, but if the ciphersuite defines it,
-    /// it must also multiply by the cofactor to comply with the RFC. Note that batch verification
-    /// (see [`crate::batch::Verifier`]) also uses the default implementation regardless whether a
-    /// tailored implementation was provided.
+    /// You may override this to provide a tailored implementation, but if the
+    /// ciphersuite defines it, it must also multiply by the cofactor to comply
+    /// with the RFC. Note that batch verification (see
+    /// [`crate::batch::Verifier`]) also uses the default implementation
+    /// regardless whether a tailored implementation was provided.
     fn verify_signature(
-        sig_target: &SigningTarget<Self>,
+        message: &[u8],
         signature: &Signature<Self>,
         public_key: &VerifyingKey<Self>,
     ) -> Result<(), Error<Self>> {
-        let c = <Self>::challenge(&signature.R, public_key, sig_target)?;
+        let (message, signature, public_key) = <Self>::pre_verify(message, signature, public_key)?;
 
-        public_key.verify_prehashed(c, signature, &sig_target.sig_params)
+        let c = <Self>::challenge(&signature.R, &public_key, &message)?;
+
+        public_key.verify_prehashed(c, &signature)
     }
 
-    /// Generates the challenge as is required for Schnorr signatures.
-    ///
-    /// Deals in bytes, so that [FROST] and singleton signing and verification can use it with different
-    /// types.
-    ///
-    /// This is the only invocation of the H2 hash function from the [RFC].
-    ///
-    /// [FROST]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-challenge-computa
-    /// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-3.2
-    fn challenge(
-        R: &Element<Self>,
-        verifying_key: &VerifyingKey<Self>,
-        sig_target: &SigningTarget<Self>,
-    ) -> Result<Challenge<Self>, Error<Self>> {
-        challenge(R, verifying_key, &sig_target.message)
+    /// Optional. Pre-process [`round2::sign()`] inputs. The default
+    /// implementation returns them as-is. [`Cow`] is used so implementations
+    /// can choose to return the same passed reference or a modified clone.
+    #[allow(clippy::type_complexity)]
+    fn pre_sign<'a>(
+        _ctx: &mut Self::Context,
+        signing_package: &'a SigningPackage<Self>,
+        signer_nonces: &'a round1::SigningNonces<Self>,
+        key_package: &'a KeyPackage<Self>,
+    ) -> Result<
+        (
+            Cow<'a, SigningPackage<Self>>,
+            Cow<'a, round1::SigningNonces<Self>>,
+            Cow<'a, KeyPackage<Self>>,
+        ),
+        Error<Self>,
+    > {
+        Ok((
+            Cow::Borrowed(signing_package),
+            Cow::Borrowed(signer_nonces),
+            Cow::Borrowed(key_package),
+        ))
     }
 
-    /// Finalize an aggregated group signature. This is used by frost-sepc256k1-tr
-    /// to ensure the signature is valid under BIP340.
-    fn aggregate_sig_finalize(
-        z: <<Self::Group as Group>::Field as Field>::Scalar,
-        R: Element<Self>,
-        _verifying_key: &VerifyingKey<Self>,
-        _sig_target: &SigningTarget<Self>,
-    ) -> Result<Signature<Self>, Error<Self>> {
-        Ok(Signature { R, z })
+    /// Optional. Pre-process [`crate::aggregate()`] inputs. The default implementation
+    /// returns them as-is. [`Cow`] is used so implementations can choose to
+    /// return the same passed reference or a modified clone.
+    #[allow(clippy::type_complexity)]
+    fn pre_aggregate<'a>(
+        _ctx: &mut Self::Context,
+        signing_package: &'a SigningPackage<Self>,
+        signature_shares: &'a BTreeMap<Identifier<Self>, round2::SignatureShare<Self>>,
+        public_key_package: &'a PublicKeyPackage<Self>,
+    ) -> Result<
+        (
+            Cow<'a, SigningPackage<Self>>,
+            Cow<'a, BTreeMap<Identifier<Self>, round2::SignatureShare<Self>>>,
+            Cow<'a, PublicKeyPackage<Self>>,
+        ),
+        Error<Self>,
+    > {
+        Ok((
+            Cow::Borrowed(signing_package),
+            Cow::Borrowed(signature_shares),
+            Cow::Borrowed(public_key_package),
+        ))
     }
 
-    /// Finalize and output a single-signer Schnorr signature.
-    fn single_sig_finalize(
-        k: <<Self::Group as Group>::Field as Field>::Scalar,
-        R: Element<Self>,
-        secret: <<Self::Group as Group>::Field as Field>::Scalar,
-        challenge: &Challenge<Self>,
-        _verifying_key: &VerifyingKey<Self>,
-        _sig_params: &Self::SigningParameters,
-    ) -> Signature<Self> {
-        let z = k + (challenge.0 * secret);
-        Signature { R, z }
+    /// Optional. Pre-process [`VerifyingKey::verify()`] inputs. The default
+    /// implementation returns them as-is. [`Cow`] is used so implementations
+    /// can choose to return the same passed reference or a modified clone.
+    #[allow(clippy::type_complexity)]
+    fn pre_verify<'a>(
+        msg: &'a [u8],
+        signature: &'a Signature<Self>,
+        public_key: &'a VerifyingKey<Self>,
+    ) -> Result<
+        (
+            Cow<'a, [u8]>,
+            Cow<'a, Signature<Self>>,
+            Cow<'a, VerifyingKey<Self>>,
+        ),
+        Error<Self>,
+    > {
+        Ok((
+            Cow::Borrowed(msg),
+            Cow::Borrowed(signature),
+            Cow::Borrowed(public_key),
+        ))
     }
 
-    /// Converts a signature to its [`Ciphersuite::SignatureSerialization`] in bytes.
-    ///
-    /// The default implementation serializes a signature by serializing its `R` point and
-    /// `z` component independently, and then concatenating them.
-    fn serialize_signature(signature: &Signature<Self>) -> Result<Vec<u8>, Error<Self>> {
-        let mut bytes = vec![];
-        bytes.extend(<Self::Group>::serialize(&signature.R)?.as_ref());
-        bytes.extend(<<Self::Group as Group>::Field>::serialize(&signature.z).as_ref());
-        Ok(bytes)
+    /// Optional. Generate a nonce and a commitment to it. Used by
+    /// [`SigningKey`] for regular (non-FROST) signing and internally by the DKG
+    /// to generate proof-of-knowledge signatures.
+    fn generate_nonce<R: RngCore + CryptoRng>(
+        rng: &mut R,
+    ) -> (
+        <<Self::Group as Group>::Field as Field>::Scalar,
+        <Self::Group as Group>::Element,
+    ) {
+        let k = random_nonzero::<Self, R>(rng);
+        let R = <Self::Group>::generator() * k;
+        (k, R)
     }
 
-    /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
-    ///
-    /// The default implementation assumes the serialization is a serialized `R` point
-    /// followed by a serialized `z` component with no padding or extra fields.
-    fn deserialize_signature(bytes: &[u8]) -> Result<Signature<Self>, Error<Self>> {
-        // To compute the expected length of the encoded point, encode the generator
-        // and get its length. Note that we can't use the identity because it can be encoded
-        // shorter in some cases (e.g. P-256, which uses SEC1 encoding).
-        let generator = <Self::Group>::generator();
-        let mut R_bytes = Vec::from(<Self::Group>::serialize(&generator)?.as_ref());
-        let R_bytes_len = R_bytes.len();
-
-        let one = <<Self::Group as Group>::Field as Field>::zero();
-        let mut z_bytes =
-            Vec::from(<<Self::Group as Group>::Field as Field>::serialize(&one).as_ref());
-        let z_bytes_len = z_bytes.len();
-
-        if bytes.len() != R_bytes_len + z_bytes_len {
-            return Err(Error::MalformedSignature);
-        }
-
-        R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?);
-
-        let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
-
-        // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]`
-        z_bytes[..].copy_from_slice(
-            bytes
-                .get(R_bytes_len..R_bytes_len + z_bytes_len)
-                .ok_or(Error::MalformedSignature)?,
-        );
-
-        let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?;
-
-        Ok(Signature {
-            R: <Self::Group>::deserialize(R_serialization)?,
-            z: <<Self::Group as Group>::Field>::deserialize(z_serialization)?,
-        })
+    /// Optional. Compute the group commitment. Called by [`round2::sign()`] and
+    /// [`crate::aggregate()`].
+    fn compute_group_commitment(
+        _context: &mut Self::Context,
+        signing_package: &SigningPackage<Self>,
+        binding_factor_list: &BindingFactorList<Self>,
+    ) -> Result<GroupCommitment<Self>, Error<Self>> {
+        compute_group_commitment(signing_package, binding_factor_list)
+    }
+
+    /// Optional. Generates the challenge as is required for Schnorr signatures.
+    /// Called by [`round2::sign()`] and [`crate::aggregate()`].
+    fn challenge(
+        R: &Element<Self>,
+        verifying_key: &VerifyingKey<Self>,
+        message: &[u8],
+    ) -> Result<Challenge<Self>, Error<Self>> {
+        challenge(R, verifying_key, message)
     }
 
-    /// Compute the signature share for a particular signer on a given challenge.
+    /// Optional. Compute the signature share for a particular signer on a given
+    /// challenge. Called by [`round2::sign()`].
     fn compute_signature_share(
+        _ctx: &mut Self::Context,
         signer_nonces: &round1::SigningNonces<Self>,
         binding_factor: BindingFactor<Self>,
-        _group_commitment: GroupCommitment<Self>,
         lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
         key_package: &KeyPackage<Self>,
         challenge: Challenge<Self>,
-        _sig_params: &Self::SigningParameters,
     ) -> round2::SignatureShare<Self> {
         round2::compute_signature_share(
             signer_nonces,
@@ -384,98 +399,50 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
         )
     }
 
-    /// Compute the effective group element which should be used for signature operations
-    /// for the given verifying key.
-    ///
-    /// In frost-sepc256k1-tr, this is used to commit the key to taptree merkle root hashes.
-    fn effective_pubkey_element(
-        verifying_key: &VerifyingKey<Self>,
-        _sig_params: &Self::SigningParameters,
-    ) -> <Self::Group as Group>::Element {
-        verifying_key.to_element()
-    }
-
-    /// Compute the effective nonce element which should be used for signature operations.
-    ///
-    /// In frost-sepc256k1-tr, this negates the nonce if it has an odd parity.
-    fn effective_nonce_element(
-        R: <Self::Group as Group>::Element,
-    ) -> <Self::Group as Group>::Element {
-        R
-    }
-
-    /// Compute the effective secret key which should be used for signature operations
-    /// for the given verifying key.
-    ///
-    /// In frost-sepc256k1-tr, this is used to commit the key to taptree merkle root hashes.
-    fn effective_secret_key(
-        secret: <<Self::Group as Group>::Field as Field>::Scalar,
-        _public: &VerifyingKey<Self>,
-        _sig_params: &Self::SigningParameters,
-    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
-        secret
-    }
-
-    /// Compute the effective nonce secret which should be used for signature operations.
-    ///
-    /// In frost-sepc256k1-tr, this negates the nonce if it has an odd parity.
-    fn effective_nonce_secret(
-        nonce: <<Self::Group as Group>::Field as Field>::Scalar,
-        _R: &Element<Self>,
-    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
-        nonce
+    /// Optional. Verify a signing share. Called by [`crate::aggregate()`] if
+    /// cheater detection is enabled.
+    fn verify_share(
+        _ctx: &mut Self::Context,
+        signature_share: &SignatureShare<Self>,
+        identifier: Identifier<Self>,
+        group_commitment_share: &round1::GroupCommitmentShare<Self>,
+        verifying_share: &VerifyingShare<Self>,
+        lambda_i: Scalar<Self>,
+        challenge: &Challenge<Self>,
+    ) -> Result<(), Error<Self>> {
+        signature_share.verify(
+            identifier,
+            group_commitment_share,
+            verifying_share,
+            lambda_i,
+            challenge,
+        )
     }
 
-    /// Compute the effective nonce commitment share which should be used for
-    /// FROST signing.
+    /// Optional. Converts a signature to its
+    /// [`Ciphersuite::SignatureSerialization`] in bytes.
     ///
-    /// In frost-sepc256k1-tr, this negates the commitment share if the group's final
-    /// commitment has an odd parity.
-    fn effective_commitment_share(
-        group_commitment_share: round1::GroupCommitmentShare<Self>,
-        _group_commitment: &GroupCommitment<Self>,
-    ) -> <Self::Group as Group>::Element {
-        group_commitment_share.to_element()
+    /// The default implementation serializes a signature by serializing its `R`
+    /// point and `z` component independently, and then concatenating them.
+    fn serialize_signature(signature: &Signature<Self>) -> Result<Vec<u8>, Error<Self>> {
+        signature.default_serialize()
     }
 
-    /// Compute the effective verifying share which should be used for FROST
-    /// partial signature verification.
+    /// Optional. Converts bytes as [`Ciphersuite::SignatureSerialization`] into
+    /// a `Signature<C>`.
     ///
-    /// In frost-sepc256k1-tr, this negates the verifying share if the group's final
-    /// verifying key has an odd parity.
-    fn effective_verifying_share(
-        verifying_share: &VerifyingShare<Self>,
-        _verifying_key: &VerifyingKey<Self>,
-        _sig_params: &Self::SigningParameters,
-    ) -> <Self::Group as Group>::Element {
-        verifying_share.to_element()
+    /// The default implementation assumes the serialization is a serialized `R`
+    /// point followed by a serialized `z` component with no padding or extra
+    /// fields.
+    fn deserialize_signature(bytes: &[u8]) -> Result<Signature<Self>, Error<Self>> {
+        Signature::<Self>::default_deserialize(bytes)
     }
 
-    /// Construct the key packages from the output of a successful DKG execution.
-    /// The signing share and verifying share have already been verified and the
-    /// identifier belongs to our signer.
-    ///
-    /// In frost-sepc256k1-tr, this adds a hash-based tweak to the group key
-    /// to prevent peers from inserting rogue tapscript tweaks into the group's
-    /// joint public key.
-    fn dkg_output_finalize(
-        identifier: Identifier<Self>,
-        commitments: BTreeMap<Identifier<Self>, &VerifiableSecretSharingCommitment<Self>>,
-        signing_share: SigningShare<Self>,
-        verifying_share: VerifyingShare<Self>,
-        min_signers: u16,
+    /// Post-process the output of the DKG for a given participant.
+    fn post_dkg(
+        key_package: KeyPackage<Self>,
+        public_key_package: PublicKeyPackage<Self>,
     ) -> Result<(KeyPackage<Self>, PublicKeyPackage<Self>), Error<Self>> {
-        let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
-
-        let key_package = KeyPackage {
-            header: Header::default(),
-            identifier,
-            signing_share,
-            verifying_share,
-            verifying_key: public_key_package.verifying_key,
-            min_signers,
-        };
-
         Ok((key_package, public_key_package))
     }
 }
diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs
index ec5c9f69..24f05401 100644
--- a/frost-core/src/verifying_key.rs
+++ b/frost-core/src/verifying_key.rs
@@ -5,10 +5,7 @@ use alloc::{string::ToString, vec::Vec};
 #[cfg(any(test, feature = "test-impl"))]
 use hex::FromHex;
 
-use crate::{
-    serialization::SerializableElement, Challenge, Ciphersuite, Error, Group, Signature,
-    SigningTarget,
-};
+use crate::{serialization::SerializableElement, Challenge, Ciphersuite, Error, Group, Signature};
 
 /// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -42,18 +39,6 @@ where
         self.element.0
     }
 
-    /// Return the effective verifying key given the specific signing parameters
-    /// to be verified against. For most ciphersuites, this simply returns the
-    /// same verifying key unchanged.
-    pub fn effective_key(self, sig_params: &C::SigningParameters) -> Self {
-        VerifyingKey::new(<C>::effective_pubkey_element(&self, sig_params))
-    }
-
-    /// Check if VerifyingKey is odd
-    pub fn y_is_odd(&self) -> bool {
-        <C::Group as Group>::y_is_odd(&self.to_element())
-    }
-
     /// Deserialize from bytes
     pub fn deserialize(bytes: &[u8]) -> Result<VerifyingKey<C>, Error<C>> {
         Ok(Self::new(SerializableElement::deserialize(bytes)?.0))
@@ -66,22 +51,20 @@ where
 
     /// Verify a purported `signature` with a pre-hashed [`Challenge`] made by this verification
     /// key.
+    #[cfg_attr(feature = "internals", visibility::make(pub))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
     pub(crate) fn verify_prehashed(
         &self,
         challenge: Challenge<C>,
         signature: &Signature<C>,
-        sig_params: &C::SigningParameters,
     ) -> Result<(), Error<C>> {
         // Verify check is h * ( - z * B + R  + c * A) == 0
         //                 h * ( z * B - c * A - R) == 0
         //
         // where h is the cofactor
-        let R = signature.R;
-        let vk = C::effective_pubkey_element(self, sig_params);
-
         let zB = C::Group::generator() * signature.z;
-        let cA = vk * challenge.0;
-        let check = (zB - cA - R) * C::Group::cofactor();
+        let cA = self.element.0 * challenge.0;
+        let check = (zB - cA - signature.R) * C::Group::cofactor();
 
         if check == C::Group::identity() {
             Ok(())
@@ -90,13 +73,9 @@ where
         }
     }
 
-    /// Verify a purported `signature` over `sig_target` made by this verification key.
-    pub fn verify(
-        &self,
-        sig_target: impl Into<SigningTarget<C>>,
-        signature: &Signature<C>,
-    ) -> Result<(), Error<C>> {
-        C::verify_signature(&sig_target.into(), signature, self)
+    /// Verify a purported `signature` over `msg` made by this verification key.
+    pub fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<(), Error<C>> {
+        C::verify_signature(msg, signature, self)
     }
 
     /// Computes the group public key given the group commitment.
diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs
index 42a25933..d3477d74 100644
--- a/frost-ed25519/src/lib.rs
+++ b/frost-ed25519/src/lib.rs
@@ -163,14 +163,6 @@ const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ed25519Sha512;
 
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-pub type SigningParameters = ();
-
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<Ed25519Sha512>;
-
 impl Ciphersuite for Ed25519Sha512 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -180,7 +172,7 @@ impl Ciphersuite for Ed25519Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
-    type SigningParameters = ();
+    type Context = ();
 
     /// H1 for FROST(Ed25519, SHA-512)
     ///
diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs
index 320b8707..6c564788 100644
--- a/frost-ed25519/tests/integration_tests.rs
+++ b/frost-ed25519/tests/integration_tests.rs
@@ -12,10 +12,7 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(rng);
 }
 
 #[test]
@@ -187,10 +184,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(rng);
 }
 
 #[test]
@@ -342,7 +336,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ed25519Sha512,
         _,
-    >(rng, b"message".into());
+    >(rng);
 }
 
 #[test]
diff --git a/frost-ed25519/tests/interoperability_tests.rs b/frost-ed25519/tests/interoperability_tests.rs
index 10c7032b..9c27193f 100644
--- a/frost-ed25519/tests/interoperability_tests.rs
+++ b/frost-ed25519/tests/interoperability_tests.rs
@@ -12,13 +12,12 @@ fn check_interoperability_in_sign_with_dkg() {
     // and the interoperability check. A smaller number of iterations is used
     // because DKG takes longer and otherwise the test would be too slow.
     for _ in 0..32 {
-        let (target, group_signature, group_pubkey) =
+        let (msg, group_signature, group_pubkey) =
             frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed25519Sha512, _>(
                 rng.clone(),
-                b"message".into(),
             );
 
-        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+        helpers::verify_signature(&msg, group_signature, group_pubkey);
     }
 }
 
@@ -29,14 +28,13 @@ fn check_interoperability_in_sign_with_dealer() {
     // Test with multiple keys/signatures to better exercise the key generation
     // and the interoperability check.
     for _ in 0..256 {
-        let (target, group_signature, group_pubkey) =
+        let (msg, group_signature, group_pubkey) =
             frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed25519Sha512, _>(
                 rng.clone(),
-                b"message".into(),
             );
 
         // Check that the threshold signature can be verified by the `ed25519_dalek` crate
         // public key (interoperability test)
-        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+        helpers::verify_signature(&msg, group_signature, group_pubkey);
     }
 }
diff --git a/frost-ed25519/tests/recreation_tests.rs b/frost-ed25519/tests/recreation_tests.rs
index 3bdb64ba..0b1d44f1 100644
--- a/frost-ed25519/tests/recreation_tests.rs
+++ b/frost-ed25519/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-ed25519/tests/serde_tests.rs b/frost-ed25519/tests/serde_tests.rs
index 1a2a6a4f..9f722797 100644
--- a/frost-ed25519/tests/serde_tests.rs
+++ b/frost-ed25519/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs
index f4f5bbd2..2db75121 100644
--- a/frost-ed448/src/lib.rs
+++ b/frost-ed448/src/lib.rs
@@ -157,14 +157,6 @@ const CONTEXT_STRING: &str = "FROST-ED448-SHAKE256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ed448Shake256;
 
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-pub type SigningParameters = ();
-
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<Ed448Shake256>;
-
 impl Ciphersuite for Ed448Shake256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -174,7 +166,7 @@ impl Ciphersuite for Ed448Shake256 {
 
     type SignatureSerialization = [u8; 114];
 
-    type SigningParameters = ();
+    type Context = ();
 
     /// H1 for FROST(Ed448, SHAKE256)
     ///
diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs
index eaf4336b..70061503 100644
--- a/frost-ed448/tests/integration_tests.rs
+++ b/frost-ed448/tests/integration_tests.rs
@@ -12,10 +12,7 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed448Shake256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ed448Shake256, _>(rng);
 }
 
 #[test]
@@ -187,10 +184,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed448Shake256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ed448Shake256, _>(rng);
 }
 
 #[test]
@@ -342,7 +336,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ed448Shake256,
         _,
-    >(rng, b"message".into());
+    >(rng);
 }
 
 #[test]
diff --git a/frost-ed448/tests/recreation_tests.rs b/frost-ed448/tests/recreation_tests.rs
index 0f0c14b4..e51f8b6e 100644
--- a/frost-ed448/tests/recreation_tests.rs
+++ b/frost-ed448/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-ed448/tests/serde_tests.rs b/frost-ed448/tests/serde_tests.rs
index 71de30c2..3b5c667a 100644
--- a/frost-ed448/tests/serde_tests.rs
+++ b/frost-ed448/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs
index b8b61b87..ba4a462c 100644
--- a/frost-p256/src/lib.rs
+++ b/frost-p256/src/lib.rs
@@ -175,14 +175,6 @@ const CONTEXT_STRING: &str = "FROST-P256-SHA256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct P256Sha256;
 
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-pub type SigningParameters = ();
-
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<P256Sha256>;
-
 impl Ciphersuite for P256Sha256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -192,7 +184,7 @@ impl Ciphersuite for P256Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
-    type SigningParameters = ();
+    type Context = ();
 
     /// H1 for FROST(P-256, SHA-256)
     ///
diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs
index c1c9347e..8d44312d 100644
--- a/frost-p256/tests/integration_tests.rs
+++ b/frost-p256/tests/integration_tests.rs
@@ -12,10 +12,7 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<P256Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<P256Sha256, _>(rng);
 }
 
 #[test]
@@ -187,10 +184,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<P256Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<P256Sha256, _>(rng);
 }
 
 #[test]
@@ -339,7 +333,6 @@ fn check_sign_with_dealer_and_identifiers() {
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<P256Sha256, _>(
         rng,
-        b"message".into(),
     );
 }
 
diff --git a/frost-p256/tests/recreation_tests.rs b/frost-p256/tests/recreation_tests.rs
index 0a96090f..a24e57f5 100644
--- a/frost-p256/tests/recreation_tests.rs
+++ b/frost-p256/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-p256/tests/serde_tests.rs b/frost-p256/tests/serde_tests.rs
index 23e7507f..c1475814 100644
--- a/frost-p256/tests/serde_tests.rs
+++ b/frost-p256/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs
index 3785d827..b627442b 100644
--- a/frost-ristretto255/src/lib.rs
+++ b/frost-ristretto255/src/lib.rs
@@ -149,14 +149,6 @@ const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Ristretto255Sha512;
 
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-pub type SigningParameters = ();
-
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<Ristretto255Sha512>;
-
 impl Ciphersuite for Ristretto255Sha512 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -166,7 +158,7 @@ impl Ciphersuite for Ristretto255Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
-    type SigningParameters = ();
+    type Context = ();
 
     /// H1 for FROST(ristretto255, SHA-512)
     ///
diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs
index 307d5762..af536ac3 100644
--- a/frost-ristretto255/tests/integration_tests.rs
+++ b/frost-ristretto255/tests/integration_tests.rs
@@ -12,10 +12,7 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ristretto255Sha512, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Ristretto255Sha512, _>(rng);
 }
 
 #[test]
@@ -188,10 +185,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ristretto255Sha512, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Ristretto255Sha512, _>(rng);
 }
 
 #[test]
@@ -343,7 +337,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Ristretto255Sha512,
         _,
-    >(rng, b"message".into());
+    >(rng);
 }
 
 #[test]
diff --git a/frost-ristretto255/tests/recreation_tests.rs b/frost-ristretto255/tests/recreation_tests.rs
index ab06c93a..a8ed937c 100644
--- a/frost-ristretto255/tests/recreation_tests.rs
+++ b/frost-ristretto255/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-ristretto255/tests/serde_tests.rs b/frost-ristretto255/tests/serde_tests.rs
index 1bc680e4..faf1769a 100644
--- a/frost-ristretto255/tests/serde_tests.rs
+++ b/frost-ristretto255/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/frost-secp256k1-tr/README.md b/frost-secp256k1-tr/README.md
index 9022e25c..f4d2205f 100644
--- a/frost-secp256k1-tr/README.md
+++ b/frost-secp256k1-tr/README.md
@@ -1,4 +1,4 @@
-An implementation of Schnorr signatures on the secp256k1 curve for both single and threshold numbers
+An implementation of Schnorr signatures on the secp256k1 curve (Taproot) for both single and threshold numbers
 of signers (FROST).
 
 ## Example: key generation with trusted dealer and FROST signing
diff --git a/frost-secp256k1-tr/benches/bench.rs b/frost-secp256k1-tr/benches/bench.rs
index 3d51c3f1..e9097bdd 100644
--- a/frost-secp256k1-tr/benches/bench.rs
+++ b/frost-secp256k1-tr/benches/bench.rs
@@ -6,13 +6,13 @@ use frost_secp256k1_tr::*;
 fn bench_secp256k1_batch_verify(c: &mut Criterion) {
     let mut rng = thread_rng();
 
-    frost_core::benches::bench_batch_verify::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
+    frost_core::benches::bench_batch_verify::<Secp256K1Sha256TR, _>(c, "secp256k1", &mut rng);
 }
 
 fn bench_secp256k1_sign(c: &mut Criterion) {
     let mut rng = thread_rng();
 
-    frost_core::benches::bench_sign::<Secp256K1Sha256, _>(c, "secp256k1", &mut rng);
+    frost_core::benches::bench_sign::<Secp256K1Sha256TR, _>(c, "secp256k1", &mut rng);
 }
 
 criterion_group!(benches, bench_secp256k1_batch_verify, bench_secp256k1_sign);
diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs
index 88bce01d..9b538030 100644
--- a/frost-secp256k1-tr/src/keys/repairable.rs
+++ b/frost-secp256k1-tr/src/keys/repairable.rs
@@ -10,7 +10,7 @@ use alloc::collections::BTreeMap;
 // (if it were below, the position of the import would vary between ciphersuites
 //  after `cargo fmt`)
 use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
-use crate::{Error, Secp256K1Sha256};
+use crate::{Error, Secp256K1Sha256TR};
 
 use super::{SecretShare, VerifiableSecretSharingCommitment};
 
@@ -38,7 +38,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
 ///
 /// Returns a scalar
 pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar {
-    frost::keys::repairable::repair_share_step_2::<Secp256K1Sha256>(deltas_j)
+    frost::keys::repairable::repair_share_step_2::<Secp256K1Sha256TR>(deltas_j)
 }
 
 /// Step 3 of RTS
@@ -61,7 +61,7 @@ mod tests {
     use rand::thread_rng;
     use serde_json::Value;
 
-    use crate::Secp256K1Sha256;
+    use crate::Secp256K1Sha256TR;
 
     lazy_static! {
         pub static ref REPAIR_SHARE: Value =
@@ -73,18 +73,20 @@ mod tests {
     fn check_repair_share_step_1() {
         let rng = thread_rng();
 
-        frost_core::tests::repairable::check_repair_share_step_1::<Secp256K1Sha256, _>(rng);
+        frost_core::tests::repairable::check_repair_share_step_1::<Secp256K1Sha256TR, _>(rng);
     }
 
     #[test]
     fn check_repair_share_step_2() {
-        frost_core::tests::repairable::check_repair_share_step_2::<Secp256K1Sha256>(&REPAIR_SHARE);
+        frost_core::tests::repairable::check_repair_share_step_2::<Secp256K1Sha256TR>(
+            &REPAIR_SHARE,
+        );
     }
 
     #[test]
     fn check_repair_share_step_3() {
         let rng = thread_rng();
-        frost_core::tests::repairable::check_repair_share_step_3::<Secp256K1Sha256, _>(
+        frost_core::tests::repairable::check_repair_share_step_3::<Secp256K1Sha256TR, _>(
             rng,
             &REPAIR_SHARE,
         );
@@ -94,7 +96,7 @@ mod tests {
     fn check_repair_share_step_1_fails_with_invalid_min_signers() {
         let rng = thread_rng();
         frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::<
-            Secp256K1Sha256,
+            Secp256K1Sha256TR,
             _,
         >(rng);
     }
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index a34680b5..91486c23 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -7,6 +7,7 @@
 
 extern crate alloc;
 
+use alloc::borrow::Cow;
 use alloc::borrow::ToOwned;
 use alloc::collections::BTreeMap;
 use alloc::vec::Vec;
@@ -26,7 +27,10 @@ use k256::{
 use rand_core::{CryptoRng, RngCore};
 use sha2::{Digest, Sha256};
 
-use frost_core as frost;
+use frost_core::{self as frost, compute_group_commitment, random_nonzero};
+
+use keys::EvenY;
+use keys::Tweak;
 
 #[cfg(test)]
 mod tests;
@@ -40,7 +44,7 @@ pub use frost_core::{
 pub use rand_core;
 
 /// An error.
-pub type Error = frost_core::Error<Secp256K1Sha256>;
+pub type Error = frost_core::Error<Secp256K1Sha256TR>;
 
 /// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field.
 #[derive(Clone, Copy)]
@@ -68,10 +72,6 @@ impl Field for Secp256K1ScalarField {
         }
     }
 
-    fn negate(scalar: &Self::Scalar) -> Self::Scalar {
-        -scalar
-    }
-
     fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
         Scalar::random(rng)
     }
@@ -126,10 +126,6 @@ impl Group for Secp256K1Group {
         ProjectivePoint::GENERATOR
     }
 
-    fn y_is_odd(element: &Self::Element) -> bool {
-        element.to_affine().y_is_odd().into()
-    }
-
     fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
         if *element == Self::identity() {
             return Err(GroupError::InvalidIdentityElement);
@@ -185,7 +181,7 @@ const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-TR-v1";
 
 /// An implementation of the FROST(secp256k1, SHA-256) ciphersuite.
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct Secp256K1Sha256;
+pub struct Secp256K1Sha256TR;
 
 /// Digest the hasher to a Scalar
 fn hasher_to_scalar(hasher: Sha256) -> Scalar {
@@ -206,7 +202,7 @@ fn tagged_hash(tag: &str) -> Sha256 {
 
 /// Create a BIP341 compliant taproot tweak
 fn tweak<T: AsRef<[u8]>>(
-    public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
+    public_key: &<<Secp256K1Sha256TR as Ciphersuite>::Group as Group>::Element,
     merkle_root: Option<T>,
 ) -> Scalar {
     match merkle_root {
@@ -220,50 +216,31 @@ fn tweak<T: AsRef<[u8]>>(
     }
 }
 
-/// Create a BIP341 compliant tweaked public key
-fn tweaked_public_key<T: AsRef<[u8]>>(
-    public_key: &VerifyingKey,
-    merkle_root: Option<T>,
-) -> <<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element {
-    let mut pk = public_key.to_element();
-    if pk.to_affine().y_is_odd().into() {
-        pk = -pk;
-    }
-    ProjectivePoint::GENERATOR * tweak(&pk, merkle_root) + pk
+// Negate a Nonce
+fn negate_nonce(nonce: &frost_core::round1::Nonce<S>) -> frost_core::round1::Nonce<S> {
+    frost_core::round1::Nonce::<S>::from_scalar(-nonce.to_scalar())
 }
 
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<S>;
-
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-#[derive(Debug, Clone, Eq, PartialEq, Default)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-pub struct SigningParameters {
-    /// The tapscript merkle tree root which must be committed to and agreed upon
-    /// in advance by all participants in the signing round.
-    ///
-    /// If set to `None` (the default), then no taproot tweak will be committed to in the signature.
-    /// Best practice suggested by BIP341 is to commit to an empty merkle root in cases
-    /// where no tapscript tweak is needed, i.e. by supplying `&[0; u8]` as the merkle root.
-    /// This prevents hiding of taproot commitments inside a linearly aggregated key.
-    ///
-    /// However, for FROST, this is not strictly required as the group key cannot be
-    /// poisoned as long as the DKG procedure is conducted correctly.
-    /// Thus, the [`Default`] trait implementation of taproot `SigningParameters`
-    /// sets `tapscript_merkle_root` to `None`.
-    ///
-    /// If 3rd party observers outside the FROST group must be able to verify there
-    /// is no hidden script-spending path embedded in the FROST group's taproot output key,
-    /// then you should set `tapscript_merkle_root` to `Some(vec![])`, which proves
-    /// the tapscript commitment for the tweaked output key is unspendable.
-    pub tapscript_merkle_root: Option<Vec<u8>>,
+// Negate a SigningNonces
+fn negate_nonces(signing_nonces: &round1::SigningNonces) -> round1::SigningNonces {
+    // TODO: this recomputes commitments which is expensive, and not needed.
+    // Create an `internals` SigningNonces::from_nonces_and_commitments or
+    // something similar.
+    round1::SigningNonces::from_nonces(
+        negate_nonce(signing_nonces.hiding()),
+        negate_nonce(signing_nonces.binding()),
+    )
 }
 
-impl frost_core::SigningParameters for SigningParameters {}
+/// TODO
+#[derive(Default)]
+pub struct Context {
+    is_group_commitment_even: bool,
+}
+
+impl frost_core::Context for Context {}
 
-impl Ciphersuite for Secp256K1Sha256 {
+impl Ciphersuite for Secp256K1Sha256TR {
     const ID: &'static str = CONTEXT_STRING;
 
     type Group = Secp256K1Group;
@@ -272,7 +249,7 @@ impl Ciphersuite for Secp256K1Sha256 {
 
     type SignatureSerialization = [u8; 64];
 
-    type SigningParameters = SigningParameters;
+    type Context = Context;
 
     /// H1 for FROST(secp256k1, SHA-256)
     ///
@@ -327,68 +304,176 @@ impl Ciphersuite for Secp256K1Sha256 {
         ))
     }
 
-    /// Generates the challenge as is required for Schnorr signatures.
+    // Sign, negating the key if required by BIP-340.
+    fn single_sign<R: RngCore + CryptoRng>(
+        signing_key: &SigningKey,
+        rng: R,
+        message: &[u8],
+    ) -> Signature {
+        let signing_key = signing_key.into_even_y(None);
+        signing_key.default_sign(rng, message)
+    }
+
+    // Preprocess sign inputs, negating the keys in the KeyPackage if required
+    // by BIP-340.
+    fn pre_sign<'a>(
+        _ctx: &mut Self::Context,
+        signing_package: &'a SigningPackage,
+        signer_nonces: &'a round1::SigningNonces,
+        key_package: &'a keys::KeyPackage,
+    ) -> Result<
+        (
+            Cow<'a, SigningPackage>,
+            Cow<'a, round1::SigningNonces>,
+            Cow<'a, keys::KeyPackage>,
+        ),
+        Error,
+    > {
+        Ok((
+            Cow::Borrowed(signing_package),
+            Cow::Borrowed(signer_nonces),
+            Cow::Owned(key_package.clone().into_even_y(None)),
+        ))
+    }
+
+    // Preprocess sign inputs, negating the keys in the PublicKeyPackage if
+    // required by BIP-340.
+    fn pre_aggregate<'a>(
+        _ctx: &mut Self::Context,
+        signing_package: &'a SigningPackage,
+        signature_shares: &'a BTreeMap<Identifier, round2::SignatureShare>,
+        public_key_package: &'a keys::PublicKeyPackage,
+    ) -> Result<
+        (
+            Cow<'a, SigningPackage>,
+            Cow<'a, BTreeMap<Identifier, round2::SignatureShare>>,
+            Cow<'a, keys::PublicKeyPackage>,
+        ),
+        Error,
+    > {
+        Ok((
+            Cow::Borrowed(signing_package),
+            Cow::Borrowed(signature_shares),
+            Cow::Owned(public_key_package.clone().into_even_y(None)),
+        ))
+    }
+
+    // Preprocess verify inputs, negating the VerifyingKey if required by
+    // BIP-340.
+    fn pre_verify<'a>(
+        message: &'a [u8],
+        signature: &'a Signature,
+        public_key: &'a VerifyingKey,
+    ) -> Result<(Cow<'a, [u8]>, Cow<'a, Signature>, Cow<'a, VerifyingKey>), Error> {
+        let public_key = public_key.into_even_y(None);
+        Ok((
+            Cow::Borrowed(message),
+            Cow::Borrowed(signature),
+            Cow::Owned(public_key),
+        ))
+    }
+
+    // Generate a nonce, negating it if required by BIP-340.
+    fn generate_nonce<R: RngCore + CryptoRng>(
+        rng: &mut R,
+    ) -> (
+        <<Self::Group as Group>::Field as Field>::Scalar,
+        <Self::Group as Group>::Element,
+    ) {
+        let k = random_nonzero::<Self, R>(rng);
+        let R = <Self::Group>::generator() * k;
+        if R.to_affine().y_is_odd().into() {
+            (-k, -R)
+        } else {
+            (k, R)
+        }
+    }
+
+    // Compute the group commitment, negating if required by BIP-340. Note that
+    // only at this point it is possible to check if negation will be required
+    // or not. If it is, it will require negating the participant's nonces (when
+    // signing) or their group commitment share (when aggregating). This is
+    // signaled by setting the `is_group_commitment_even` flag in the Context.
+    fn compute_group_commitment(
+        ctx: &mut Self::Context,
+        signing_package: &SigningPackage,
+        binding_factor_list: &frost_core::BindingFactorList<S>,
+    ) -> Result<GroupCommitment<S>, Error> {
+        let group_commitment = compute_group_commitment(signing_package, binding_factor_list)?;
+        ctx.is_group_commitment_even =
+            (!group_commitment.clone().to_element().to_affine().y_is_odd()).into();
+        let group_commitment = if !ctx.is_group_commitment_even {
+            GroupCommitment::<S>::from_element(-group_commitment.to_element())
+        } else {
+            group_commitment
+        };
+
+        Ok(group_commitment)
+    }
+
+    // Compute the challenge. Per BIP-340, only the X coordinate of R and
+    // verifying_key are hashed, unlike vanilla FROST.
     fn challenge(
         R: &Element<S>,
         verifying_key: &VerifyingKey,
-        sig_target: &SigningTarget,
+        message: &[u8],
     ) -> Result<Challenge<S>, Error> {
         let mut preimage = vec![];
-        let tweaked_pk = tweaked_public_key(
-            verifying_key,
-            sig_target.sig_params().tapscript_merkle_root.as_ref(),
-        );
         preimage.extend_from_slice(&R.to_affine().x());
-        preimage.extend_from_slice(&tweaked_pk.to_affine().x());
-        preimage.extend_from_slice(sig_target.message().as_ref());
+        preimage.extend_from_slice(&verifying_key.to_element().to_affine().x());
+        preimage.extend_from_slice(message);
         Ok(Challenge::from_scalar(S::H2(&preimage[..])))
     }
 
-    /// Finalizes the signature by negating it depending on whether
-    /// the group [`VerifyingKey`] is even or odd parity.
-    fn aggregate_sig_finalize(
-        z_raw: <<Self::Group as Group>::Field as Field>::Scalar,
-        R: Element<Self>,
-        verifying_key: &VerifyingKey,
-        sig_target: &SigningTarget,
-    ) -> Result<Signature, Error> {
-        let challenge = Self::challenge(&R, verifying_key, sig_target)?;
-
-        let t = tweak(
-            &verifying_key.to_element(),
-            sig_target.sig_params().tapscript_merkle_root.as_ref(),
-        );
-        let tc = t * challenge.to_scalar();
-        let tweaked_pubkey = tweaked_public_key(
-            verifying_key,
-            sig_target.sig_params().tapscript_merkle_root.as_ref(),
-        );
-        let z_tweaked = if tweaked_pubkey.to_affine().y_is_odd().into() {
-            z_raw - tc
+    /// Compute a signature share, negating the nonces if required by BIP-340.
+    fn compute_signature_share(
+        ctx: &mut Self::Context,
+        signer_nonces: &round1::SigningNonces,
+        binding_factor: frost::BindingFactor<S>,
+        lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
+        key_package: &frost::keys::KeyPackage<S>,
+        challenge: Challenge<S>,
+    ) -> round2::SignatureShare {
+        let signer_nonces = if !ctx.is_group_commitment_even {
+            negate_nonces(signer_nonces)
         } else {
-            z_raw + tc
+            signer_nonces.clone()
         };
-        Ok(Signature::new(R, z_tweaked))
+
+        frost::round2::compute_signature_share(
+            &signer_nonces,
+            binding_factor,
+            lambda_i,
+            key_package,
+            challenge,
+        )
     }
 
-    /// Finalize a single-signer BIP340 Schnorr signature.
-    fn single_sig_finalize(
-        k: <<Self::Group as Group>::Field as Field>::Scalar,
-        R: Element<Self>,
-        secret: <<Self::Group as Group>::Field as Field>::Scalar,
+    /// Verify a signature share, negating the group commitment share if
+    /// required by BIP-340.
+    fn verify_share(
+        ctx: &mut Self::Context,
+        signature_share: &frost_core::round2::SignatureShare<S>,
+        identifier: Identifier,
+        group_commitment_share: &frost_core::round1::GroupCommitmentShare<S>,
+        verifying_share: &frost_core::keys::VerifyingShare<S>,
+        lambda_i: Scalar,
         challenge: &Challenge<S>,
-        verifying_key: &VerifyingKey,
-        sig_params: &SigningParameters,
-    ) -> Signature {
-        let tweaked_pubkey =
-            tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref());
-        let c = challenge.to_scalar();
-        let z = if tweaked_pubkey.to_affine().y_is_odd().into() {
-            k - (c * secret)
+    ) -> Result<(), Error> {
+        let group_commitment_share = if !ctx.is_group_commitment_even {
+            frost_core::round1::GroupCommitmentShare::from_element(
+                -group_commitment_share.to_element(),
+            )
         } else {
-            k + (c * secret)
+            *group_commitment_share
         };
-        Signature::new(R, z)
+        signature_share.verify(
+            identifier,
+            &group_commitment_share,
+            verifying_share,
+            lambda_i,
+            challenge,
+        )
     }
 
     /// Serialize a signature in compact BIP340 format, with an x-only R point.
@@ -421,174 +506,27 @@ impl Ciphersuite for Secp256K1Sha256 {
         Ok(Signature::new(R, z))
     }
 
-    /// Compute a signature share, negating if required by BIP340.
-    fn compute_signature_share(
-        signer_nonces: &round1::SigningNonces,
-        binding_factor: frost::BindingFactor<S>,
-        group_commitment: GroupCommitment<S>,
-        lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
-        key_package: &frost::keys::KeyPackage<S>,
-        challenge: Challenge<S>,
-        sig_params: &SigningParameters,
-    ) -> round2::SignatureShare {
-        let mut sn = signer_nonces.clone();
-        if group_commitment.y_is_odd() {
-            sn.negate_nonces();
-        }
-
-        let mut kp = key_package.clone();
-        let public_key = key_package.verifying_key();
-        let pubkey_is_odd: bool = public_key.y_is_odd();
-        let tweaked_pubkey_is_odd: bool =
-            tweaked_public_key(public_key, sig_params.tapscript_merkle_root.as_ref())
-                .to_affine()
-                .y_is_odd()
-                .into();
-        if pubkey_is_odd != tweaked_pubkey_is_odd {
-            kp.negate_signing_share();
-        }
-
-        frost::round2::compute_signature_share(&sn, binding_factor, lambda_i, &kp, challenge)
-    }
-
-    /// Computes the effective pubkey point by tweaking the verifying key with a
-    /// provably unspendable taproot tweak.
-    fn effective_pubkey_element(
-        public_key: &VerifyingKey,
-        sig_params: &SigningParameters,
-    ) -> <Self::Group as Group>::Element {
-        let tweaked_pubkey =
-            tweaked_public_key(public_key, sig_params.tapscript_merkle_root.as_ref());
-        if Self::Group::y_is_odd(&tweaked_pubkey) {
-            -tweaked_pubkey
-        } else {
-            tweaked_pubkey
-        }
-    }
-
-    /// Ensures the nonce has an even Y coordinate.
-    fn effective_nonce_element(
-        R: <Self::Group as Group>::Element,
-    ) -> <Self::Group as Group>::Element {
-        if Self::Group::y_is_odd(&R) {
-            -R
-        } else {
-            R
-        }
-    }
-
-    /// Ensures the secret key is negated if the public key has odd parity.
-    fn effective_secret_key(
-        secret: <<Self::Group as Group>::Field as Field>::Scalar,
-        public_key: &VerifyingKey,
-        sig_params: &SigningParameters,
-    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
-        let t = tweak(
-            &public_key.to_element(),
-            sig_params.tapscript_merkle_root.as_ref(),
-        );
-        if Self::Group::y_is_odd(&public_key.to_element()) {
-            -secret + t
-        } else {
-            secret + t
-        }
-    }
-
-    /// Ensures the nonce secret is negated if the public nonce point has odd parity.
-    fn effective_nonce_secret(
-        nonce: <<Self::Group as Group>::Field as Field>::Scalar,
-        R: &Element<Self>,
-    ) -> <<Self::Group as Group>::Field as Field>::Scalar {
-        if R.to_affine().y_is_odd().into() {
-            -nonce
-        } else {
-            nonce
-        }
-    }
-
-    /// Ensures the commitment share is negated if the group's commitment has odd parity.
-    fn effective_commitment_share(
-        group_commitment_share: frost::round1::GroupCommitmentShare<Self>,
-        group_commitment: &GroupCommitment<Self>,
-    ) -> Element<Self> {
-        if group_commitment
-            .clone()
-            .to_element()
-            .to_affine()
-            .y_is_odd()
-            .into()
-        {
-            -group_commitment_share.to_element()
-        } else {
-            group_commitment_share.to_element()
-        }
-    }
-
-    /// Calculate a verifying share compatible with taproot, depending on the parity
-    /// of the tweaked vs untweaked verifying key.
-    fn effective_verifying_share(
-        verifying_share: &keys::VerifyingShare,
-        verifying_key: &VerifyingKey,
-        sig_params: &SigningParameters,
-    ) -> <Self::Group as Group>::Element {
-        let pubkey_is_odd: bool = verifying_key.to_element().to_affine().y_is_odd().into();
-        let tweaked_pubkey_is_odd: bool =
-            tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref())
-                .to_affine()
-                .y_is_odd()
-                .into();
-
-        let vs = verifying_share.to_element();
-        if pubkey_is_odd != tweaked_pubkey_is_odd {
-            -vs
-        } else {
-            vs
-        }
-    }
-
-    /// We add an unusable taproot tweak to the group key computed by a DKG run,
-    /// to prevent peers from inserting rogue tapscript tweaks into the group's
-    /// joint public key.
-    fn dkg_output_finalize(
-        identifier: Identifier,
-        commitments: BTreeMap<Identifier, &keys::VerifiableSecretSharingCommitment>,
-        signing_share: keys::SigningShare,
-        verifying_share: keys::VerifyingShare,
-        min_signers: u16,
+    /// Post-process the DKG output. We add an unusable taproot tweak to the
+    /// group key computed by a DKG run, to prevent peers from inserting rogue
+    /// tapscript tweaks into the group's joint public key.
+    fn post_dkg(
+        key_package: keys::KeyPackage,
+        public_key_package: keys::PublicKeyPackage,
     ) -> Result<(keys::KeyPackage, keys::PublicKeyPackage), Error> {
-        let untweaked_public_key_package =
-            keys::PublicKeyPackage::from_dkg_commitments(&commitments)?;
-
-        let untweaked_vk = untweaked_public_key_package.verifying_key().to_element();
-        let t = tweak(&untweaked_vk, Some(vec![])); // unspendable script path
-        let tG = ProjectivePoint::GENERATOR * t;
-
-        let tweaked_verifying_shares: BTreeMap<Identifier, keys::VerifyingShare> =
-            untweaked_public_key_package
-                .verifying_shares()
-                .clone()
-                .into_iter()
-                .map(|(id, share)| (id, keys::VerifyingShare::new(share.to_element() + tG)))
-                .collect();
-
-        let tweaked_verifying_key = VerifyingKey::new(untweaked_vk + tG);
-
-        let key_package = keys::KeyPackage::new(
-            identifier,
-            keys::SigningShare::new(signing_share.to_scalar() + t),
-            keys::VerifyingShare::new(verifying_share.to_element() + tG),
-            tweaked_verifying_key,
-            min_signers,
-        );
-
-        let public_key_package =
-            keys::PublicKeyPackage::new(tweaked_verifying_shares, tweaked_verifying_key);
-
-        Ok((key_package, public_key_package))
+        // From BIP-341:
+        // > If the spending conditions do not require a script path, the output
+        // > key should commit to an unspendable script path instead of having
+        // > no script path. This can be achieved by computing the output key
+        // > point as Q = P + int(hashTapTweak(bytes(P)))G.
+        let merkle_root = key_package.verifying_key().to_element().to_affine().x();
+        Ok((
+            key_package.tweak(Some(merkle_root)),
+            public_key_package.tweak(Some(merkle_root)),
+        ))
     }
 }
 
-impl RandomizedCiphersuite for Secp256K1Sha256 {
+impl RandomizedCiphersuite for Secp256K1Sha256TR {
     fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
         Some(hash_to_scalar(
             (CONTEXT_STRING.to_owned() + "randomizer").as_bytes(),
@@ -597,7 +535,7 @@ impl RandomizedCiphersuite for Secp256K1Sha256 {
     }
 }
 
-type S = Secp256K1Sha256;
+type S = Secp256K1Sha256TR;
 
 /// A FROST(secp256k1, SHA-256) participant identifier.
 pub type Identifier = frost::Identifier<S>;
@@ -695,6 +633,167 @@ pub mod keys {
     /// ensure that they received the correct (and same) value.
     pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment<S>;
 
+    /// Trait for ensuring the group public key has an even Y coordinate.
+    ///
+    /// In BIP-320, public keys are encoded with only the X coordinate, which
+    /// means that two Y coordinates are possible. The specification says that
+    /// the coordinate which is even must be used. Alternatively, something
+    /// equivalent can be accomplished by simply converting any existing
+    /// (non-encoded) public key to have an even Y coordinate.
+    ///
+    /// This trait is used to enable this procedure, by changing the private and
+    /// public keys to ensure that the public key has a even Y coordinate. This
+    /// is done by simply negating both keys if Y is even (in a field, negating
+    /// is equivalent to computing p - x where p is the prime modulus. Since p
+    /// is odd, if x is odd then the result will be even). Fortunately this
+    /// works even after Shamir secret sharing, in the individual signing and
+    /// verifying shares, since it's linear.
+    pub trait EvenY {
+        /// Return if the given type has a group public key with an even Y
+        /// coordinate.
+        fn has_even_y(&self) -> bool;
+
+        /// Convert the given type to make sure the group public key has an even
+        /// Y coordinate. `is_even` can be specified if evenness was already
+        /// determined beforehand.
+        fn into_even_y(self, is_even: Option<bool>) -> Self;
+    }
+
+    impl EvenY for PublicKeyPackage {
+        fn has_even_y(&self) -> bool {
+            let verifying_key = self.verifying_key();
+            (!verifying_key.to_element().to_affine().y_is_odd()).into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                // Negate verifying key
+                let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
+                // Recreate verifying share map with negated VerifyingShares
+                // values.
+                let verifying_shares: BTreeMap<_, _> = self
+                    .verifying_shares()
+                    .iter()
+                    .map(|(i, vs)| {
+                        let vs = VerifyingShare::new(-vs.to_element());
+                        (*i, vs)
+                    })
+                    .collect();
+                PublicKeyPackage::new(verifying_shares, verifying_key)
+            } else {
+                self
+            }
+        }
+    }
+
+    impl EvenY for KeyPackage {
+        fn has_even_y(&self) -> bool {
+            let verifying_key = self.verifying_key();
+            (!verifying_key.to_element().to_affine().y_is_odd()).into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                // Negate all components
+                let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
+                let signing_share = SigningShare::new(-self.signing_share().to_scalar());
+                let verifying_share = VerifyingShare::new(-self.verifying_share().to_element());
+                KeyPackage::new(
+                    *self.identifier(),
+                    signing_share,
+                    verifying_share,
+                    verifying_key,
+                    *self.min_signers(),
+                )
+            } else {
+                self
+            }
+        }
+    }
+
+    impl EvenY for VerifyingKey {
+        fn has_even_y(&self) -> bool {
+            (!self.to_element().to_affine().y_is_odd()).into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                VerifyingKey::new(-self.to_element())
+            } else {
+                self
+            }
+        }
+    }
+
+    impl EvenY for SigningKey {
+        fn has_even_y(&self) -> bool {
+            (!Into::<VerifyingKey>::into(self)
+                .to_element()
+                .to_affine()
+                .y_is_odd())
+            .into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                SigningKey::from_scalar(-self.to_scalar())
+                    .expect("the original SigningKey must be nonzero")
+            } else {
+                self
+            }
+        }
+    }
+
+    /// Trait for tweaking a key component following BIP-341
+    pub trait Tweak: EvenY {
+        /// Convert the given type to add a tweak.
+        fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self;
+    }
+
+    impl Tweak for PublicKeyPackage {
+        fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
+            let t = tweak(&self.verifying_key().to_element(), merkle_root);
+            let tp = ProjectivePoint::GENERATOR * t;
+            let public_key_package = self.into_even_y(None);
+            let verifying_key =
+                VerifyingKey::new(public_key_package.verifying_key().to_element() + tp);
+            // Recreate verifying share map with negated VerifyingShares
+            // values.
+            let verifying_shares: BTreeMap<_, _> = public_key_package
+                .verifying_shares()
+                .iter()
+                .map(|(i, vs)| {
+                    let vs = VerifyingShare::new(vs.to_element() + tp);
+                    (*i, vs)
+                })
+                .collect();
+            PublicKeyPackage::new(verifying_shares, verifying_key)
+        }
+    }
+
+    impl Tweak for KeyPackage {
+        fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
+            let t = tweak(&self.verifying_key().to_element(), merkle_root);
+            let tp = ProjectivePoint::GENERATOR * t;
+            let key_package = self.into_even_y(None);
+            let verifying_key = VerifyingKey::new(key_package.verifying_key().to_element() + tp);
+            let signing_share = SigningShare::new(key_package.signing_share().to_scalar() + t);
+            let verifying_share =
+                VerifyingShare::new(key_package.verifying_share().to_element() + tp);
+            KeyPackage::new(
+                *key_package.identifier(),
+                signing_share,
+                verifying_share,
+                verifying_key,
+                *key_package.min_signers(),
+            )
+        }
+    }
+
     pub mod dkg;
     pub mod repairable;
 }
@@ -739,6 +838,8 @@ pub type SigningPackage = frost::SigningPackage<S>;
 
 /// FROST(secp256k1, SHA-256) Round 2 functionality and types, for signature share generation.
 pub mod round2 {
+    use keys::Tweak;
+
     use super::*;
 
     /// A FROST(secp256k1, SHA-256) participant's signature share, which the Coordinator will aggregate with all other signer's
@@ -760,6 +861,21 @@ pub mod round2 {
     ) -> Result<SignatureShare, Error> {
         frost::round2::sign(signing_package, signer_nonces, key_package)
     }
+
+    /// Same as [`sign()`], but using a Taproot tweak as specified in BIP-341.
+    pub fn sign_with_tweak(
+        signing_package: &SigningPackage,
+        signer_nonces: &round1::SigningNonces,
+        key_package: &keys::KeyPackage,
+        merkle_root: Option<&[u8]>,
+    ) -> Result<SignatureShare, Error> {
+        if merkle_root.is_some() {
+            let key_package = key_package.clone().tweak(merkle_root);
+            frost::round2::sign(signing_package, signer_nonces, &key_package)
+        } else {
+            frost::round2::sign(signing_package, signer_nonces, key_package)
+        }
+    }
 }
 
 /// A Schnorr signature on FROST(secp256k1, SHA-256).
@@ -783,9 +899,24 @@ pub type Signature = frost_core::Signature<S>;
 pub fn aggregate(
     signing_package: &SigningPackage,
     signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
-    pubkeys: &keys::PublicKeyPackage,
+    public_key_package: &keys::PublicKeyPackage,
 ) -> Result<Signature, Error> {
-    frost::aggregate(signing_package, signature_shares, pubkeys)
+    frost::aggregate(signing_package, signature_shares, public_key_package)
+}
+
+/// Same as [`aggregate()`], but using a Taproot tweak as specified in BIP-341.
+pub fn aggregate_with_tweak(
+    signing_package: &SigningPackage,
+    signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
+    public_key_package: &keys::PublicKeyPackage,
+    merkle_root: Option<&[u8]>,
+) -> Result<Signature, Error> {
+    if merkle_root.is_some() {
+        let public_key_package = public_key_package.clone().tweak(merkle_root);
+        frost::aggregate(signing_package, signature_shares, &public_key_package)
+    } else {
+        frost::aggregate(signing_package, signature_shares, public_key_package)
+    }
 }
 
 /// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256).
diff --git a/frost-secp256k1-tr/src/tests/batch.rs b/frost-secp256k1-tr/src/tests/batch.rs
index b87d22a9..f88793a3 100644
--- a/frost-secp256k1-tr/src/tests/batch.rs
+++ b/frost-secp256k1-tr/src/tests/batch.rs
@@ -6,19 +6,19 @@ use crate::*;
 fn check_batch_verify() {
     let rng = thread_rng();
 
-    frost_core::tests::batch::batch_verify::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::batch::batch_verify::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
 fn check_bad_batch_verify() {
     let rng = thread_rng();
 
-    frost_core::tests::batch::bad_batch_verify::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::batch::bad_batch_verify::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
 fn empty_batch_verify() {
     let rng = thread_rng();
 
-    frost_core::tests::batch::empty_batch_verify::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::batch::empty_batch_verify::<Secp256K1Sha256TR, _>(rng);
 }
diff --git a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs
index d1b6c22c..a63259c2 100644
--- a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs
+++ b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs
@@ -15,7 +15,7 @@ lazy_static! {
 fn check_serialization_of_coefficient_commitment() {
     let rng = thread_rng();
     frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(rng);
 }
@@ -24,14 +24,14 @@ fn check_serialization_of_coefficient_commitment() {
 fn check_create_coefficient_commitment() {
     let rng = thread_rng();
     frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(rng);
 }
 #[test]
 fn check_create_coefficient_commitment_error() {
     frost_core::tests::coefficient_commitment::check_create_coefficient_commitment_error::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
     >(&ELEMENTS);
 }
 
@@ -40,7 +40,7 @@ fn check_get_value_of_coefficient_commitment() {
     let rng = thread_rng();
 
     frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(rng);
 }
diff --git a/frost-secp256k1-tr/src/tests/deserialize.rs b/frost-secp256k1-tr/src/tests/deserialize.rs
index a744832b..7d4c630b 100644
--- a/frost-secp256k1-tr/src/tests/deserialize.rs
+++ b/frost-secp256k1-tr/src/tests/deserialize.rs
@@ -2,18 +2,18 @@ use crate::*;
 
 #[test]
 fn check_deserialize_non_canonical() {
-    let mut encoded_generator = <Secp256K1Sha256 as Ciphersuite>::Group::serialize(
-        &<Secp256K1Sha256 as Ciphersuite>::Group::generator(),
+    let mut encoded_generator = <Secp256K1Sha256TR as Ciphersuite>::Group::serialize(
+        &<Secp256K1Sha256TR as Ciphersuite>::Group::generator(),
     )
     .unwrap();
 
-    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_generator);
+    let r = <Secp256K1Sha256TR as Ciphersuite>::Group::deserialize(&encoded_generator);
     assert!(r.is_ok());
 
     // The first byte should be 0x02 or 0x03. Set other value to
     // create a non-canonical encoding.
     encoded_generator[0] = 0xFF;
-    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_generator);
+    let r = <Secp256K1Sha256TR as Ciphersuite>::Group::deserialize(&encoded_generator);
     assert_eq!(r, Err(GroupError::MalformedElement));
 
     // Besides the first byte, it is still possible to get non-canonical encodings.
@@ -23,7 +23,7 @@ fn check_deserialize_non_canonical() {
             .unwrap()
             .try_into()
             .unwrap();
-    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_point);
+    let r = <Secp256K1Sha256TR as Ciphersuite>::Group::deserialize(&encoded_point);
     assert_eq!(r, Err(GroupError::MalformedElement));
 }
 
@@ -33,6 +33,6 @@ fn check_deserialize_identity() {
     // allow us to change that. Try to send something similar.
     let encoded_identity = [0u8; 33];
 
-    let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_identity);
+    let r = <Secp256K1Sha256TR as Ciphersuite>::Group::deserialize(&encoded_identity);
     assert_eq!(r, Err(GroupError::MalformedElement));
 }
diff --git a/frost-secp256k1-tr/src/tests/proptests.rs b/frost-secp256k1-tr/src/tests/proptests.rs
index dd598569..cad88c33 100644
--- a/frost-secp256k1-tr/src/tests/proptests.rs
+++ b/frost-secp256k1-tr/src/tests/proptests.rs
@@ -19,7 +19,7 @@ proptest! {
 
         // Create a test case for each signature type.
         let msg = b"test message for proptests";
-        let mut sig = SignatureCase::<Secp256K1Sha256>::new(rng, msg.to_vec());
+        let mut sig = SignatureCase::<Secp256K1Sha256TR>::new(rng, msg.to_vec());
 
         // Apply tweaks to each case.
         for t in &tweaks {
diff --git a/frost-secp256k1-tr/src/tests/vss_commitment.rs b/frost-secp256k1-tr/src/tests/vss_commitment.rs
index 1a09195a..80fb1ca7 100644
--- a/frost-secp256k1-tr/src/tests/vss_commitment.rs
+++ b/frost-secp256k1-tr/src/tests/vss_commitment.rs
@@ -14,19 +14,21 @@ lazy_static! {
 #[test]
 fn check_serialize_vss_commitment() {
     let rng = thread_rng();
-    frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
 fn check_deserialize_vss_commitment() {
     let rng = thread_rng();
-    frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<Secp256K1Sha256TR, _>(
+        rng,
+    );
 }
 
 #[test]
 fn check_deserialize_vss_commitment_error() {
     let rng = thread_rng();
-    frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::<Secp256K1Sha256, _>(
+    frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::<Secp256K1Sha256TR, _>(
         rng, &ELEMENTS,
     );
 }
@@ -34,5 +36,7 @@ fn check_deserialize_vss_commitment_error() {
 #[test]
 fn check_compute_public_key_package() {
     let rng = thread_rng();
-    frost_core::tests::vss_commitment::check_compute_public_key_package::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::vss_commitment::check_compute_public_key_package::<Secp256K1Sha256TR, _>(
+        rng,
+    );
 }
diff --git a/frost-secp256k1-tr/tests/helpers/mod.rs b/frost-secp256k1-tr/tests/helpers/mod.rs
index 6b899101..0de6147c 100644
--- a/frost-secp256k1-tr/tests/helpers/mod.rs
+++ b/frost-secp256k1-tr/tests/helpers/mod.rs
@@ -2,15 +2,15 @@
 // and each one uses only part of the module.
 #![allow(dead_code)]
 
-use frost_secp256k1_tr::Secp256K1Sha256;
+use frost_secp256k1_tr::Secp256K1Sha256TR;
 use secp256k1::Secp256k1;
 
 pub mod samples;
 
 pub fn verify_signature(
     msg: &[u8],
-    group_signature: frost_core::Signature<Secp256K1Sha256>,
-    group_pubkey: frost_core::VerifyingKey<Secp256K1Sha256>,
+    group_signature: &frost_core::Signature<Secp256K1Sha256TR>,
+    group_pubkey: &frost_core::VerifyingKey<Secp256K1Sha256TR>,
 ) {
     let secp = Secp256k1::new();
     let sig = secp256k1::schnorr::Signature::from_byte_array(
diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs
index 839f3f57..11ef99e3 100644
--- a/frost-secp256k1-tr/tests/helpers/samples.rs
+++ b/frost-secp256k1-tr/tests/helpers/samples.rs
@@ -14,7 +14,7 @@ use frost_secp256k1_tr::{
     Field, Signature, SigningPackage, VerifyingKey,
 };
 
-type C = frost_secp256k1_tr::Secp256K1Sha256;
+type C = frost_secp256k1_tr::Secp256K1Sha256TR;
 
 fn element1() -> Element<C> {
     <C as Ciphersuite>::Group::generator()
diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs
index ddbb6540..9187285b 100644
--- a/frost-secp256k1-tr/tests/integration_tests.rs
+++ b/frost-secp256k1-tr/tests/integration_tests.rs
@@ -5,17 +5,14 @@ use serde_json::Value;
 
 #[test]
 fn check_zero_key_fails() {
-    frost_core::tests::ciphersuite_generic::check_zero_key_fails::<Secp256K1Sha256>();
+    frost_core::tests::ciphersuite_generic::check_zero_key_fails::<Secp256K1Sha256TR>();
 }
 
 #[test]
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
@@ -27,7 +24,7 @@ fn check_dkg_part1_fails_with_invalid_signers_min_signers() {
     let error = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -38,10 +35,10 @@ fn check_dkg_part1_fails_with_min_signers_greater_than_max() {
 
     let min_signers = 3;
     let max_signers = 2;
-    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+    let error: frost_core::Error<Secp256K1Sha256TR> = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -55,7 +52,7 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() {
     let error = Error::InvalidMaxSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -64,23 +61,24 @@ fn check_dkg_part1_fails_with_invalid_signers_max_signers() {
 fn check_rts() {
     let rng = thread_rng();
 
-    frost_core::tests::repairable::check_rts::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::repairable::check_rts::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
 fn check_refresh_shares_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::refresh::check_refresh_shares_with_dealer::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::refresh::check_refresh_shares_with_dealer::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
 fn check_refresh_shares_with_dealer_serialisation() {
     let rng = thread_rng();
 
-    frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::<Secp256K1Sha256, _>(
-        rng,
-    );
+    frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::<
+        Secp256K1Sha256TR,
+        _,
+    >(rng);
 }
 
 #[test]
@@ -88,7 +86,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() {
     let rng = thread_rng();
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(rng);
 }
@@ -107,7 +105,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() {
     let error = Error::InvalidMinSigners;
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(max_signers, min_signers, &identifiers, error, rng);
 }
@@ -123,10 +121,10 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s
     ];
     let min_signers = 3;
     let max_signers = 3;
-    let error: frost_core::Error<Secp256K1Sha256> = Error::IncorrectNumberOfIdentifiers;
+    let error: frost_core::Error<Secp256K1Sha256TR> = Error::IncorrectNumberOfIdentifiers;
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(max_signers, min_signers, &identifiers, error, rng);
 }
@@ -142,10 +140,10 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() {
     ];
     let min_signers = 6;
     let max_signers = 4;
-    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+    let error: frost_core::Error<Secp256K1Sha256TR> = Error::InvalidMinSigners;
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(max_signers, min_signers, &identifiers, error, rng);
 }
@@ -159,7 +157,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() {
     let error = Error::InvalidMaxSigners;
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(max_signers, min_signers, &identifiers, error, rng);
 }
@@ -178,7 +176,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
     let error = Error::UnknownIdentifier;
 
     frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(max_signers, min_signers, &identifiers, error, rng);
 }
@@ -187,10 +185,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
@@ -202,7 +197,7 @@ fn check_sign_with_dealer_fails_with_invalid_min_signers() {
     let error = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -213,10 +208,10 @@ fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() {
 
     let min_signers = 3;
     let max_signers = 2;
-    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+    let error: frost_core::Error<Secp256K1Sha256TR> = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -230,7 +225,7 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() {
     let error = Error::InvalidMaxSigners;
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -240,7 +235,7 @@ fn check_sign_with_dealer_fails_with_invalid_max_signers() {
 #[test]
 fn check_share_generation_secp256k1_tr_sha256() {
     let rng = thread_rng();
-    frost_core::tests::ciphersuite_generic::check_share_generation::<Secp256K1Sha256, _>(rng);
+    frost_core::tests::ciphersuite_generic::check_share_generation::<Secp256K1Sha256TR, _>(rng);
 }
 
 #[test]
@@ -252,7 +247,7 @@ fn check_share_generation_fails_with_invalid_min_signers() {
     let error = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -263,10 +258,10 @@ fn check_share_generation_fails_with_min_signers_greater_than_max() {
 
     let min_signers = 3;
     let max_signers = 2;
-    let error: frost_core::Error<Secp256K1Sha256> = Error::InvalidMinSigners;
+    let error: frost_core::Error<Secp256K1Sha256TR> = Error::InvalidMinSigners;
 
     frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -280,7 +275,7 @@ fn check_share_generation_fails_with_invalid_max_signers() {
     let error = Error::InvalidMaxSigners;
 
     frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(min_signers, max_signers, error, rng);
 }
@@ -299,29 +294,29 @@ lazy_static! {
 
 #[test]
 fn check_sign_with_test_vectors() {
-    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256>(&VECTORS);
+    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256TR>(&VECTORS);
 }
 
 #[test]
 fn check_sign_with_test_vectors_dkg() {
-    frost_core::tests::vectors_dkg::check_dkg_keygen::<Secp256K1Sha256>(&VECTORS_DKG);
+    frost_core::tests::vectors_dkg::check_dkg_keygen::<Secp256K1Sha256TR>(&VECTORS_DKG);
 }
 
 #[test]
 fn check_sign_with_test_vectors_with_big_identifiers() {
-    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256>(
+    frost_core::tests::vectors::check_sign_with_test_vectors::<Secp256K1Sha256TR>(
         &VECTORS_BIG_IDENTIFIER,
     );
 }
 
 #[test]
 fn check_error_culprit() {
-    frost_core::tests::ciphersuite_generic::check_error_culprit::<Secp256K1Sha256>();
+    frost_core::tests::ciphersuite_generic::check_error_culprit::<Secp256K1Sha256TR>();
 }
 
 #[test]
 fn check_identifier_derivation() {
-    frost_core::tests::ciphersuite_generic::check_identifier_derivation::<Secp256K1Sha256>();
+    frost_core::tests::ciphersuite_generic::check_identifier_derivation::<Secp256K1Sha256TR>();
 }
 
 // Explicit test which is used in a documentation snippet
@@ -340,24 +335,25 @@ fn check_sign_with_dealer_and_identifiers() {
     let rng = thread_rng();
 
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
-    >(rng, b"message".into());
+    >(rng);
 }
 
 #[test]
 fn check_sign_with_missing_identifier() {
     let rng = thread_rng();
-    frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::<Secp256K1Sha256, _>(
-        rng,
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::<
+        Secp256K1Sha256TR,
+        _,
+    >(rng);
 }
 
 #[test]
 fn check_sign_with_incorrect_commitments() {
     let rng = thread_rng();
     frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::<
-        Secp256K1Sha256,
+        Secp256K1Sha256TR,
         _,
     >(rng);
 }
diff --git a/frost-secp256k1-tr/tests/interoperability_tests.rs b/frost-secp256k1-tr/tests/interoperability_tests.rs
index 165d7b6c..b2e3f9a0 100644
--- a/frost-secp256k1-tr/tests/interoperability_tests.rs
+++ b/frost-secp256k1-tr/tests/interoperability_tests.rs
@@ -1,10 +1,22 @@
 use frost_secp256k1_tr::*;
 
-use crate::Secp256K1Sha256;
+use crate::Secp256K1Sha256TR;
 use rand::thread_rng;
 
 mod helpers;
 
+#[test]
+fn check_interoperability_in_regular_sign() {
+    let mut rng = thread_rng();
+
+    for _ in 0..256 {
+        let signing_key = SigningKey::new(&mut rng);
+        let verifying_key = signing_key.into();
+        let signature = signing_key.sign(&mut rng, b"message");
+        helpers::verify_signature(b"message", &signature, &verifying_key);
+    }
+}
+
 #[test]
 fn check_interoperability_in_sign_with_dkg() {
     let rng = thread_rng();
@@ -13,13 +25,12 @@ fn check_interoperability_in_sign_with_dkg() {
     // and the interoperability check. A smaller number of iterations is used
     // because DKG takes longer and otherwise the test would be too slow.
     for _ in 0..32 {
-        let (target, group_signature, group_pubkey) =
-            frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
+        let (message, group_signature, group_pubkey) =
+            frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256TR, _>(
                 rng.clone(),
-                b"message".into(),
             );
 
-        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+        helpers::verify_signature(&message, &group_signature, &group_pubkey);
     }
 }
 
@@ -30,14 +41,13 @@ fn check_interoperability_in_sign_with_dealer() {
     // Test with multiple keys/signatures to better exercise the key generation
     // and the interoperability check.
     for _ in 0..256 {
-        let (target, group_signature, group_pubkey) =
-            frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
+        let (message, group_signature, group_pubkey) =
+            frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256TR, _>(
                 rng.clone(),
-                b"message".into(),
             );
 
         // Check that the threshold signature can be verified by the `ed25519_dalek` crate
         // public key (interoperability test)
-        helpers::verify_signature(target.message(), group_signature, group_pubkey);
+        helpers::verify_signature(&message, &group_signature, &group_pubkey);
     }
 }
diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs
index 6b3ce890..477de37e 100644
--- a/frost-secp256k1-tr/tests/recreation_tests.rs
+++ b/frost-secp256k1-tr/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-secp256k1-tr/tests/rerandomized_tests.rs b/frost-secp256k1-tr/tests/rerandomized_tests.rs
index 79bb5aa3..67e14313 100644
--- a/frost-secp256k1-tr/tests/rerandomized_tests.rs
+++ b/frost-secp256k1-tr/tests/rerandomized_tests.rs
@@ -1,4 +1,4 @@
-use frost_secp256k1_tr::Secp256K1Sha256;
+use frost_secp256k1_tr::Secp256K1Sha256TR;
 use rand::thread_rng;
 
 #[test]
@@ -6,5 +6,5 @@ fn check_randomized_sign_with_dealer() {
     let rng = thread_rng();
 
     let (_msg, _group_signature, _group_pubkey) =
-        frost_rerandomized::tests::check_randomized_sign_with_dealer::<Secp256K1Sha256, _>(rng);
+        frost_rerandomized::tests::check_randomized_sign_with_dealer::<Secp256K1Sha256TR, _>(rng);
 }
diff --git a/frost-secp256k1-tr/tests/serde_tests.rs b/frost-secp256k1-tr/tests/serde_tests.rs
index fb8ce73b..62a70e70 100644
--- a/frost-secp256k1-tr/tests/serde_tests.rs
+++ b/frost-secp256k1-tr/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
index cc62ef11..b398e5e1 100644
--- a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
+++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap
@@ -2,4 +2,4 @@
 source: frost-secp256k1-tr/tests/serialization_tests.rs
 expression: "hex::encode(&bytes)"
 ---
-00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c6400
+00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c64
diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs
index 7ae33ad5..f95a32b7 100644
--- a/frost-secp256k1-tr/tests/tweaking_tests.rs
+++ b/frost-secp256k1-tr/tests/tweaking_tests.rs
@@ -1,96 +1,79 @@
+use std::{error::Error, vec};
+
 use frost_secp256k1_tr::*;
-use rand::thread_rng;
-use secp256k1::Secp256k1;
+use keys::Tweak;
 
 mod helpers;
 
 #[test]
-fn check_tweaked_signing_key() {
-    let signing_key = SigningKey::deserialize(&[0xAA; 32]).unwrap();
-    let untweaked_verifying_key = VerifyingKey::from(signing_key);
-
-    let mut rng = rand::thread_rng();
-    let message = b"message";
-
-    let untweaked_signature = signing_key.sign(&mut rng, &message);
-
-    helpers::verify_signature(message, untweaked_signature, untweaked_verifying_key);
-
-    untweaked_verifying_key
-        .verify(&message, &untweaked_signature)
-        .expect("untweaked signature should be valid under untweaked verifying key");
-
-    let signing_target = SigningTarget::new(
-        &message,
-        SigningParameters {
-            tapscript_merkle_root: Some(vec![]),
-        },
-    );
-
-    let tweaked_signature = signing_key.sign(&mut rng, signing_target.clone());
-
-    untweaked_verifying_key
-        .verify(&message, &tweaked_signature)
-        .expect_err("tweaked signature should not be valid under untweaked verifying key");
-
-    let tweaked_verifying_key = untweaked_verifying_key.effective_key(signing_target.sig_params());
-    tweaked_verifying_key
-        .verify(&message, &tweaked_signature)
-        .expect("tweaked signature should be valid under tweaked verifying key");
-
-    untweaked_verifying_key
-        .verify(signing_target.clone(), &tweaked_signature)
-        .expect(
-            "tweaked signature should be valid under untweaked verifying key\
-             when signing params are provided",
-        );
-
-    helpers::verify_signature(message, tweaked_signature, tweaked_verifying_key);
-}
-
-#[test]
-fn check_tweaked_sign_with_dkg() {
-    let rng = thread_rng();
-
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
-        rng,
-        SigningTarget::new(
-            b"message",
-            SigningParameters {
-                tapscript_merkle_root: Some(vec![]),
-            },
-        ),
-    );
-}
-#[test]
-fn check_tweaked_sign_with_dealer() {
-    let rng = thread_rng();
-
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
-        rng,
-        SigningTarget::new(
-            b"message",
-            SigningParameters {
-                tapscript_merkle_root: Some(vec![]),
-            },
-        ),
-    );
-}
-
-#[test]
-fn check_tweaked_sign_with_dealer_and_identifiers() {
-    let rng = thread_rng();
-
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
-        Secp256K1Sha256,
-        _,
-    >(
-        rng,
-        SigningTarget::new(
-            b"message",
-            SigningParameters {
-                tapscript_merkle_root: Some(vec![]),
-            },
-        ),
-    );
+fn check_tweaked_sign_with_dealer() -> Result<(), Box<dyn Error>> {
+    use frost_secp256k1_tr as frost;
+    use rand::thread_rng;
+    use std::collections::BTreeMap;
+
+    let merkle_root: Vec<u8> = vec![];
+
+    let mut rng = thread_rng();
+    let max_signers = 5;
+    let min_signers = 3;
+    let (shares, pubkey_package) = frost::keys::generate_with_dealer(
+        max_signers,
+        min_signers,
+        frost::keys::IdentifierList::Default,
+        &mut rng,
+    )?;
+    let mut key_packages: BTreeMap<_, _> = BTreeMap::new();
+    for (identifier, secret_share) in shares {
+        let key_package = frost::keys::KeyPackage::try_from(secret_share)?;
+        key_packages.insert(identifier, key_package);
+    }
+
+    let mut nonces_map = BTreeMap::new();
+    let mut commitments_map = BTreeMap::new();
+
+    for participant_index in 1..=min_signers {
+        let participant_identifier = participant_index.try_into().expect("should be nonzero");
+        let key_package = &key_packages[&participant_identifier];
+        let (nonces, commitments) = frost::round1::commit(key_package.signing_share(), &mut rng);
+        nonces_map.insert(participant_identifier, nonces);
+        commitments_map.insert(participant_identifier, commitments);
+    }
+
+    let mut signature_shares = BTreeMap::new();
+    let message = "message to sign".as_bytes();
+    let signing_package = frost::SigningPackage::new(commitments_map, message);
+
+    for participant_identifier in nonces_map.keys() {
+        let key_package = &key_packages[participant_identifier];
+        let nonces = &nonces_map[participant_identifier];
+        let signature_share = frost::round2::sign_with_tweak(
+            &signing_package,
+            nonces,
+            key_package,
+            Some(&merkle_root),
+        )?;
+        signature_shares.insert(*participant_identifier, signature_share);
+    }
+
+    let group_signature = frost::aggregate_with_tweak(
+        &signing_package,
+        &signature_shares,
+        &pubkey_package,
+        Some(&merkle_root),
+    )?;
+
+    pubkey_package
+        .verifying_key()
+        .verify(message, &group_signature)
+        .expect_err("signature should not be valid for untweaked pubkey_package");
+
+    let pubkey_package = pubkey_package.tweak(Some(&merkle_root));
+    pubkey_package
+        .verifying_key()
+        .verify(message, &group_signature)
+        .expect("signature should be valid for tweaked pubkey_package");
+
+    helpers::verify_signature(message, &group_signature, pubkey_package.verifying_key());
+
+    Ok(())
 }
diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs
index 5489b48d..2f432e4a 100644
--- a/frost-secp256k1/src/lib.rs
+++ b/frost-secp256k1/src/lib.rs
@@ -175,14 +175,6 @@ const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v1";
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub struct Secp256K1Sha256;
 
-/// The ciphersuite-specific signing parameters which are fed into
-/// signing code to ensure correctly compliant signatures are computed.
-pub type SigningParameters = ();
-
-/// The message target which the group's signature should commit to. Includes
-/// a message byte vector, and a set of ciphersuite-specific parameters.
-pub type SigningTarget = frost_core::SigningTarget<Secp256K1Sha256>;
-
 impl Ciphersuite for Secp256K1Sha256 {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -192,7 +184,7 @@ impl Ciphersuite for Secp256K1Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
-    type SigningParameters = ();
+    type Context = ();
 
     /// H1 for FROST(secp256k1, SHA-256)
     ///
diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs
index 4cdd2294..9581384b 100644
--- a/frost-secp256k1/tests/integration_tests.rs
+++ b/frost-secp256k1/tests/integration_tests.rs
@@ -12,10 +12,7 @@ fn check_zero_key_fails() {
 fn check_sign_with_dkg() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dkg::<Secp256K1Sha256, _>(rng);
 }
 
 #[test]
@@ -187,10 +184,7 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() {
 fn check_sign_with_dealer() {
     let rng = thread_rng();
 
-    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(
-        rng,
-        b"message".into(),
-    );
+    frost_core::tests::ciphersuite_generic::check_sign_with_dealer::<Secp256K1Sha256, _>(rng);
 }
 
 #[test]
@@ -342,7 +336,7 @@ fn check_sign_with_dealer_and_identifiers() {
     frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::<
         Secp256K1Sha256,
         _,
-    >(rng, b"message".into());
+    >(rng);
 }
 
 #[test]
diff --git a/frost-secp256k1/tests/recreation_tests.rs b/frost-secp256k1/tests/recreation_tests.rs
index 1158385b..bb2f8315 100644
--- a/frost-secp256k1/tests/recreation_tests.rs
+++ b/frost-secp256k1/tests/recreation_tests.rs
@@ -41,9 +41,9 @@ fn check_signing_package_recreation() {
     let signing_package = samples::signing_package();
 
     let commitments = signing_package.signing_commitments();
-    let sig_target = signing_package.sig_target();
+    let message = signing_package.message();
 
-    let new_signing_package = SigningPackage::new(commitments.clone(), sig_target.clone());
+    let new_signing_package = SigningPackage::new(commitments.clone(), message);
     assert!(signing_package == new_signing_package);
 }
 
diff --git a/frost-secp256k1/tests/serde_tests.rs b/frost-secp256k1/tests/serde_tests.rs
index a092d930..82a0735d 100644
--- a/frost-secp256k1/tests/serde_tests.rs
+++ b/frost-secp256k1/tests/serde_tests.rs
@@ -112,9 +112,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
     assert!(signing_package == decoded_signing_package);
@@ -135,9 +133,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -157,9 +153,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -178,9 +172,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      }
+      "message": "68656c6c6f20776f726c64"
     }"#;
     assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
 
@@ -200,9 +192,7 @@ fn check_signing_package_serialization() {
           "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
         }
       },
-      "sig_target": {
-        "message": "68656c6c6f20776f726c64"
-      },
+      "message": "68656c6c6f20776f726c64",
       "extra": 1
     }
     "#;
diff --git a/gencode/src/main.rs b/gencode/src/main.rs
index 8a91e576..8a420fca 100644
--- a/gencode/src/main.rs
+++ b/gencode/src/main.rs
@@ -121,17 +121,14 @@ fn write_docs(
     // To be able to replace the documentation properly, start from the end, which
     // will keep the string positions consistent
     for (old_name, _, old_start, old_end) in old_docs.iter().rev() {
-        let new_doc = docs
-            .get(old_name)
-            .unwrap_or_else(|| {
-                panic!(
-                    "documentation for {} is not available in base file",
-                    old_name
-                )
-            })
-            .1
-            .clone();
-
+        let new_doc = docs.get(old_name).map(|v| v.1.clone());
+        let Some(new_doc) = new_doc else {
+            eprintln!(
+                "WARNING: documentation for {} is not available in base file. This can mean it's a specific type for the ciphersuite, or that there is a bug in gencode",
+                old_name
+            );
+            continue;
+        };
         // Replaces ciphersuite-references in documentation
         let mut new_doc = new_doc.to_string();
         for (old_n, new_n) in zip(original_suite_strings.iter(), new_suite_strings.iter()) {
@@ -299,8 +296,8 @@ fn main() -> ExitCode {
         (
             "frost-secp256k1-tr",
             &[
-                "Secp256K1Sha256",
-                "secp256k1 curve",
+                "Secp256K1Sha256TR",
+                "secp256k1 curve (Taproot)",
                 "Secp256K1",
                 "FROST(secp256k1, SHA-256)",
                 "FROST-secp256k1-SHA256-TR-v1",

From d90169e452bb2c9bc4b379809dda60074336f803 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Sun, 27 Oct 2024 22:35:09 +0000
Subject: [PATCH 08/13] Update PoK test vector to use nonce which generates an
 even-parity point

Uses r = e99ae2676eab512a3572c7b7655d633642a717250af57a7e0ccd5f9618b69f3f
---
 frost-secp256k1-tr/tests/helpers/vectors_dkg.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
index 048f620e..5df7b6e3 100644
--- a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
+++ b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
@@ -26,7 +26,7 @@
       "signing_key": "2619be8223b23e0453ddc630a4d164e81f7d8a9e07af33c4d4d02190df8bec13",
       "coefficient": "f7ba0cbbffbea8aaceb3ade54bdbf35bafb1cda15b65ad490e0c63dd069a7c9f",
       "vss_commitments": ["03ef10370a008cd95e179dc51e2cb7828f30b72d254e5166484f927c84ab326582", "022ce0dac0db217ba326fbbe3e6132d45e2a4bfa0a0c3790d91eacce9a1c2d6a10"],
-      "proof_of_knowledge": "19df66bda7724ccfa6a5ea76aac9cc167880d55717fe6887b89aeea94408cc9ce47b65a55f9d00479e9d3ea2c7402e81803e2e724d45d70c2cb93e3b0deb5f78",
+      "proof_of_knowledge": "a319dd51cf64b3896c22f54154812d4ae76cfa95f46f53ef69241fd702456fef32da76cc93d3a541ca495b723e793ee90c32440da5f314e2e58a2dc30550314a",
       "signing_shares": {
         "1": "b489a711942526abbb5330a8215d2e740f7dbddec3452006993a8cea3ac278cb",
         "3": "20255dc07b1fb78bdf90bd85fd2389c988c8250faee11826656a09142fa9fc97"

From f9237f5bc2f58b385b56f10164103bdcf4890a4d Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Sun, 27 Oct 2024 22:38:33 +0000
Subject: [PATCH 09/13] BIP341 key package tweaks shouldn't cause key negation

---
 frost-secp256k1-tr/src/lib.rs | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index 91486c23..97820770 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -518,10 +518,10 @@ impl Ciphersuite for Secp256K1Sha256TR {
         // > key should commit to an unspendable script path instead of having
         // > no script path. This can be achieved by computing the output key
         // > point as Q = P + int(hashTapTweak(bytes(P)))G.
-        let merkle_root = key_package.verifying_key().to_element().to_affine().x();
+        let merkle_root = [0u8; 0];
         Ok((
-            key_package.tweak(Some(merkle_root)),
-            public_key_package.tweak(Some(merkle_root)),
+            key_package.tweak(Some(&merkle_root)),
+            public_key_package.tweak(Some(&merkle_root)),
         ))
     }
 }
@@ -749,7 +749,7 @@ pub mod keys {
     }
 
     /// Trait for tweaking a key component following BIP-341
-    pub trait Tweak: EvenY {
+    pub trait Tweak {
         /// Convert the given type to add a tweak.
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self;
     }
@@ -758,12 +758,10 @@ pub mod keys {
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
             let t = tweak(&self.verifying_key().to_element(), merkle_root);
             let tp = ProjectivePoint::GENERATOR * t;
-            let public_key_package = self.into_even_y(None);
-            let verifying_key =
-                VerifyingKey::new(public_key_package.verifying_key().to_element() + tp);
+            let verifying_key = VerifyingKey::new(self.verifying_key().to_element() + tp);
             // Recreate verifying share map with negated VerifyingShares
             // values.
-            let verifying_shares: BTreeMap<_, _> = public_key_package
+            let verifying_shares: BTreeMap<_, _> = self
                 .verifying_shares()
                 .iter()
                 .map(|(i, vs)| {
@@ -779,17 +777,15 @@ pub mod keys {
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
             let t = tweak(&self.verifying_key().to_element(), merkle_root);
             let tp = ProjectivePoint::GENERATOR * t;
-            let key_package = self.into_even_y(None);
-            let verifying_key = VerifyingKey::new(key_package.verifying_key().to_element() + tp);
-            let signing_share = SigningShare::new(key_package.signing_share().to_scalar() + t);
-            let verifying_share =
-                VerifyingShare::new(key_package.verifying_share().to_element() + tp);
+            let verifying_key = VerifyingKey::new(self.verifying_key().to_element() + tp);
+            let signing_share = SigningShare::new(self.signing_share().to_scalar() + t);
+            let verifying_share = VerifyingShare::new(self.verifying_share().to_element() + tp);
             KeyPackage::new(
-                *key_package.identifier(),
+                *self.identifier(),
                 signing_share,
                 verifying_share,
                 verifying_key,
-                *key_package.min_signers(),
+                *self.min_signers(),
             )
         }
     }

From ebb9f27c9817cccc3e022d29bc7c8473bf884923 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Sun, 3 Nov 2024 05:41:58 +0000
Subject: [PATCH 10/13] prune the Context type, instead negate signature.R
 before verifying

With a couple of small adjustments to the code, we can remove the
need for this extra associated type on the Ciphersuite crate. Accepting
signature with odd-parity nonce values is OK, because BIP340 discard
the nonce parity bit anyway.
---
 frost-core/src/lib.rs         | 13 ++----
 frost-core/src/round2.rs      |  9 ++--
 frost-core/src/traits.rs      | 35 +++-------------
 frost-ed25519/src/lib.rs      |  2 -
 frost-ed448/src/lib.rs        |  2 -
 frost-p256/src/lib.rs         |  2 -
 frost-ristretto255/src/lib.rs |  2 -
 frost-secp256k1-tr/src/lib.rs | 79 +++++++++++++++++------------------
 frost-secp256k1/src/lib.rs    |  2 -
 9 files changed, 50 insertions(+), 96 deletions(-)

diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs
index e373e27a..82053d9f 100644
--- a/frost-core/src/lib.rs
+++ b/frost-core/src/lib.rs
@@ -57,7 +57,7 @@ use scalar_mul::VartimeMultiscalarMul;
 pub use serde;
 pub use signature::Signature;
 pub use signing_key::SigningKey;
-pub use traits::{Ciphersuite, Context, Element, Field, Group, Scalar};
+pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
 pub use verifying_key::VerifyingKey;
 
 /// A type refinement for the scalar field element representing the per-message _[challenge]_.
@@ -583,18 +583,15 @@ where
         return Err(Error::UnknownIdentifier);
     }
 
-    let mut ctx = C::Context::default();
-
     let (signing_package, signature_shares, pubkeys) =
-        <C>::pre_aggregate(&mut ctx, signing_package, signature_shares, pubkeys)?;
+        <C>::pre_aggregate(signing_package, signature_shares, pubkeys)?;
 
     // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
     // binding factor.
     let binding_factor_list: BindingFactorList<C> =
         compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
     // Compute the group commitment from signing commitments produced in round one.
-    let group_commitment =
-        <C>::compute_group_commitment(&mut ctx, &signing_package, &binding_factor_list)?;
+    let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
 
     // The aggregation of the signature shares by summing them up, resulting in
     // a plain Schnorr signature.
@@ -624,7 +621,6 @@ where
     #[cfg(feature = "cheater-detection")]
     if verification_result.is_err() {
         detect_cheater(
-            &mut ctx,
             &group_commitment,
             &pubkeys,
             &signing_package,
@@ -641,7 +637,6 @@ where
 /// Optional cheater detection feature
 /// Each share is verified to find the cheater
 fn detect_cheater<C: Ciphersuite>(
-    ctx: &mut C::Context,
     group_commitment: &GroupCommitment<C>,
     pubkeys: &keys::PublicKeyPackage<C>,
     signing_package: &SigningPackage<C>,
@@ -679,7 +674,7 @@ fn detect_cheater<C: Ciphersuite>(
 
         // Compute relation values to verify this signature share.
         <C>::verify_share(
-            ctx,
+            group_commitment,
             signature_share,
             *signature_share_identifier,
             &R_share,
diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs
index 012cb641..3d863996 100644
--- a/frost-core/src/round2.rs
+++ b/frost-core/src/round2.rs
@@ -143,10 +143,8 @@ pub fn sign<C: Ciphersuite>(
         return Err(Error::IncorrectCommitment);
     }
 
-    let mut ctx = C::Context::default();
-
     let (signing_package, signer_nonces, key_package) =
-        <C>::pre_sign(&mut ctx, signing_package, signer_nonces, key_package)?;
+        <C>::pre_sign(signing_package, signer_nonces, key_package)?;
 
     // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
     // binding factor.
@@ -158,8 +156,7 @@ pub fn sign<C: Ciphersuite>(
         .clone();
 
     // Compute the group commitment from signing commitments produced in round one.
-    let group_commitment =
-        <C>::compute_group_commitment(&mut ctx, &signing_package, &binding_factor_list)?;
+    let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
 
     // Compute Lagrange coefficient.
     let lambda_i = frost::derive_interpolating_value(key_package.identifier(), &signing_package)?;
@@ -173,7 +170,7 @@ pub fn sign<C: Ciphersuite>(
 
     // Compute the Schnorr signature share.
     let signature_share = <C>::compute_signature_share(
-        &mut ctx,
+        &group_commitment,
         &signer_nonces,
         binding_factor,
         lambda_i,
diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs
index 2cc23ea9..34912ce3 100644
--- a/frost-core/src/traits.rs
+++ b/frost-core/src/traits.rs
@@ -10,13 +10,13 @@ use alloc::{collections::BTreeMap, vec::Vec};
 use rand_core::{CryptoRng, RngCore};
 
 use crate::{
-    challenge, compute_group_commitment,
+    challenge,
     keys::{KeyPackage, PublicKeyPackage, VerifyingShare},
     random_nonzero,
     round1::{self},
     round2::{self, SignatureShare},
-    BindingFactor, BindingFactorList, Challenge, Error, FieldError, GroupCommitment, GroupError,
-    Identifier, Signature, SigningKey, SigningPackage, VerifyingKey,
+    BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError, Identifier,
+    Signature, SigningKey, SigningPackage, VerifyingKey,
 };
 
 /// A prime order finite field GF(q) over which all scalar values for our prime order group can be
@@ -143,16 +143,6 @@ pub trait Group: Copy + Clone + PartialEq {
 /// An element of the [`Ciphersuite`] `C`'s [`Group`].
 pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
 
-/// A context which can be used by Ciphersuite implementations to pass context
-/// between methods when using the overriding methods of the Ciphersuite trait.
-///
-/// This trait is implemented for the unit type `()` which is useful for
-/// Ciphersuites implementations that don't care about the Context.
-pub trait Context: Default {}
-
-/// Implements Context for the unit type.
-impl Context for () {}
-
 /// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
 /// function.
 ///
@@ -175,9 +165,6 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// `Group::ScalarSerialization`
     type SignatureSerialization: AsRef<[u8]> + TryFrom<Vec<u8>>;
 
-    /// Optional context. Most ciphersuites will just set this to `()`.
-    type Context: Context;
-
     /// [H1] for a FROST ciphersuite.
     ///
     /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
@@ -280,7 +267,6 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// can choose to return the same passed reference or a modified clone.
     #[allow(clippy::type_complexity)]
     fn pre_sign<'a>(
-        _ctx: &mut Self::Context,
         signing_package: &'a SigningPackage<Self>,
         signer_nonces: &'a round1::SigningNonces<Self>,
         key_package: &'a KeyPackage<Self>,
@@ -304,7 +290,6 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// return the same passed reference or a modified clone.
     #[allow(clippy::type_complexity)]
     fn pre_aggregate<'a>(
-        _ctx: &mut Self::Context,
         signing_package: &'a SigningPackage<Self>,
         signature_shares: &'a BTreeMap<Identifier<Self>, round2::SignatureShare<Self>>,
         public_key_package: &'a PublicKeyPackage<Self>,
@@ -360,16 +345,6 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
         (k, R)
     }
 
-    /// Optional. Compute the group commitment. Called by [`round2::sign()`] and
-    /// [`crate::aggregate()`].
-    fn compute_group_commitment(
-        _context: &mut Self::Context,
-        signing_package: &SigningPackage<Self>,
-        binding_factor_list: &BindingFactorList<Self>,
-    ) -> Result<GroupCommitment<Self>, Error<Self>> {
-        compute_group_commitment(signing_package, binding_factor_list)
-    }
-
     /// Optional. Generates the challenge as is required for Schnorr signatures.
     /// Called by [`round2::sign()`] and [`crate::aggregate()`].
     fn challenge(
@@ -383,7 +358,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// Optional. Compute the signature share for a particular signer on a given
     /// challenge. Called by [`round2::sign()`].
     fn compute_signature_share(
-        _ctx: &mut Self::Context,
+        _group_commitment: &GroupCommitment<Self>,
         signer_nonces: &round1::SigningNonces<Self>,
         binding_factor: BindingFactor<Self>,
         lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
@@ -402,7 +377,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static {
     /// Optional. Verify a signing share. Called by [`crate::aggregate()`] if
     /// cheater detection is enabled.
     fn verify_share(
-        _ctx: &mut Self::Context,
+        _group_commitment: &GroupCommitment<Self>,
         signature_share: &SignatureShare<Self>,
         identifier: Identifier<Self>,
         group_commitment_share: &round1::GroupCommitmentShare<Self>,
diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs
index d3477d74..1e33b2d9 100644
--- a/frost-ed25519/src/lib.rs
+++ b/frost-ed25519/src/lib.rs
@@ -172,8 +172,6 @@ impl Ciphersuite for Ed25519Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
-    type Context = ();
-
     /// H1 for FROST(Ed25519, SHA-512)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.1-2.4.2.2
diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs
index 2db75121..4ceb707e 100644
--- a/frost-ed448/src/lib.rs
+++ b/frost-ed448/src/lib.rs
@@ -166,8 +166,6 @@ impl Ciphersuite for Ed448Shake256 {
 
     type SignatureSerialization = [u8; 114];
 
-    type Context = ();
-
     /// H1 for FROST(Ed448, SHAKE256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.3-2.4.2.2
diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs
index ba4a462c..3e798f4b 100644
--- a/frost-p256/src/lib.rs
+++ b/frost-p256/src/lib.rs
@@ -184,8 +184,6 @@ impl Ciphersuite for P256Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
-    type Context = ();
-
     /// H1 for FROST(P-256, SHA-256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.4-2.4.2.2
diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs
index b627442b..0929c228 100644
--- a/frost-ristretto255/src/lib.rs
+++ b/frost-ristretto255/src/lib.rs
@@ -158,8 +158,6 @@ impl Ciphersuite for Ristretto255Sha512 {
 
     type SignatureSerialization = [u8; 64];
 
-    type Context = ();
-
     /// H1 for FROST(ristretto255, SHA-512)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.2-2.4.2.2
diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index 97820770..d90f5769 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -27,7 +27,7 @@ use k256::{
 use rand_core::{CryptoRng, RngCore};
 use sha2::{Digest, Sha256};
 
-use frost_core::{self as frost, compute_group_commitment, random_nonzero};
+use frost_core::{self as frost, random_nonzero};
 
 use keys::EvenY;
 use keys::Tweak;
@@ -232,14 +232,6 @@ fn negate_nonces(signing_nonces: &round1::SigningNonces) -> round1::SigningNonce
     )
 }
 
-/// TODO
-#[derive(Default)]
-pub struct Context {
-    is_group_commitment_even: bool,
-}
-
-impl frost_core::Context for Context {}
-
 impl Ciphersuite for Secp256K1Sha256TR {
     const ID: &'static str = CONTEXT_STRING;
 
@@ -249,8 +241,6 @@ impl Ciphersuite for Secp256K1Sha256TR {
 
     type SignatureSerialization = [u8; 64];
 
-    type Context = Context;
-
     /// H1 for FROST(secp256k1, SHA-256)
     ///
     /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1
@@ -317,7 +307,6 @@ impl Ciphersuite for Secp256K1Sha256TR {
     // Preprocess sign inputs, negating the keys in the KeyPackage if required
     // by BIP-340.
     fn pre_sign<'a>(
-        _ctx: &mut Self::Context,
         signing_package: &'a SigningPackage,
         signer_nonces: &'a round1::SigningNonces,
         key_package: &'a keys::KeyPackage,
@@ -339,7 +328,6 @@ impl Ciphersuite for Secp256K1Sha256TR {
     // Preprocess sign inputs, negating the keys in the PublicKeyPackage if
     // required by BIP-340.
     fn pre_aggregate<'a>(
-        _ctx: &mut Self::Context,
         signing_package: &'a SigningPackage,
         signature_shares: &'a BTreeMap<Identifier, round2::SignatureShare>,
         public_key_package: &'a keys::PublicKeyPackage,
@@ -358,7 +346,7 @@ impl Ciphersuite for Secp256K1Sha256TR {
         ))
     }
 
-    // Preprocess verify inputs, negating the VerifyingKey if required by
+    // Preprocess verify inputs, negating the VerifyingKey and `signature.R` if required by
     // BIP-340.
     fn pre_verify<'a>(
         message: &'a [u8],
@@ -366,9 +354,10 @@ impl Ciphersuite for Secp256K1Sha256TR {
         public_key: &'a VerifyingKey,
     ) -> Result<(Cow<'a, [u8]>, Cow<'a, Signature>, Cow<'a, VerifyingKey>), Error> {
         let public_key = public_key.into_even_y(None);
+        let signature = signature.into_even_y(None);
         Ok((
             Cow::Borrowed(message),
-            Cow::Borrowed(signature),
+            Cow::Owned(signature),
             Cow::Owned(public_key),
         ))
     }
@@ -389,28 +378,6 @@ impl Ciphersuite for Secp256K1Sha256TR {
         }
     }
 
-    // Compute the group commitment, negating if required by BIP-340. Note that
-    // only at this point it is possible to check if negation will be required
-    // or not. If it is, it will require negating the participant's nonces (when
-    // signing) or their group commitment share (when aggregating). This is
-    // signaled by setting the `is_group_commitment_even` flag in the Context.
-    fn compute_group_commitment(
-        ctx: &mut Self::Context,
-        signing_package: &SigningPackage,
-        binding_factor_list: &frost_core::BindingFactorList<S>,
-    ) -> Result<GroupCommitment<S>, Error> {
-        let group_commitment = compute_group_commitment(signing_package, binding_factor_list)?;
-        ctx.is_group_commitment_even =
-            (!group_commitment.clone().to_element().to_affine().y_is_odd()).into();
-        let group_commitment = if !ctx.is_group_commitment_even {
-            GroupCommitment::<S>::from_element(-group_commitment.to_element())
-        } else {
-            group_commitment
-        };
-
-        Ok(group_commitment)
-    }
-
     // Compute the challenge. Per BIP-340, only the X coordinate of R and
     // verifying_key are hashed, unlike vanilla FROST.
     fn challenge(
@@ -427,14 +394,14 @@ impl Ciphersuite for Secp256K1Sha256TR {
 
     /// Compute a signature share, negating the nonces if required by BIP-340.
     fn compute_signature_share(
-        ctx: &mut Self::Context,
+        group_commitment: &GroupCommitment<S>,
         signer_nonces: &round1::SigningNonces,
         binding_factor: frost::BindingFactor<S>,
         lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
         key_package: &frost::keys::KeyPackage<S>,
         challenge: Challenge<S>,
     ) -> round2::SignatureShare {
-        let signer_nonces = if !ctx.is_group_commitment_even {
+        let signer_nonces = if !group_commitment.has_even_y() {
             negate_nonces(signer_nonces)
         } else {
             signer_nonces.clone()
@@ -452,7 +419,7 @@ impl Ciphersuite for Secp256K1Sha256TR {
     /// Verify a signature share, negating the group commitment share if
     /// required by BIP-340.
     fn verify_share(
-        ctx: &mut Self::Context,
+        group_commitment: &GroupCommitment<S>,
         signature_share: &frost_core::round2::SignatureShare<S>,
         identifier: Identifier,
         group_commitment_share: &frost_core::round1::GroupCommitmentShare<S>,
@@ -460,7 +427,7 @@ impl Ciphersuite for Secp256K1Sha256TR {
         lambda_i: Scalar,
         challenge: &Challenge<S>,
     ) -> Result<(), Error> {
-        let group_commitment_share = if !ctx.is_group_commitment_even {
+        let group_commitment_share = if !group_commitment.has_even_y() {
             frost_core::round1::GroupCommitmentShare::from_element(
                 -group_commitment_share.to_element(),
             )
@@ -728,6 +695,36 @@ pub mod keys {
         }
     }
 
+    impl EvenY for GroupCommitment<S> {
+        fn has_even_y(&self) -> bool {
+            (!self.clone().to_element().to_affine().y_is_odd()).into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                Self::from_element(-self.to_element())
+            } else {
+                self
+            }
+        }
+    }
+
+    impl EvenY for Signature {
+        fn has_even_y(&self) -> bool {
+            (!self.R().to_affine().y_is_odd()).into()
+        }
+
+        fn into_even_y(self, is_even: Option<bool>) -> Self {
+            let is_even = is_even.unwrap_or_else(|| self.has_even_y());
+            if !is_even {
+                Self::new(-*self.R(), *self.z())
+            } else {
+                self
+            }
+        }
+    }
+
     impl EvenY for SigningKey {
         fn has_even_y(&self) -> bool {
             (!Into::<VerifyingKey>::into(self)
diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs
index 2f432e4a..4d25266b 100644
--- a/frost-secp256k1/src/lib.rs
+++ b/frost-secp256k1/src/lib.rs
@@ -184,8 +184,6 @@ impl Ciphersuite for Secp256K1Sha256 {
 
     type SignatureSerialization = [u8; 65];
 
-    type Context = ();
-
     /// H1 for FROST(secp256k1, SHA-256)
     ///
     /// [spec]: https://datatracker.ietf.org/doc/html/rfc9591#section-6.5-2.4.2.2

From 5a66946db8e22b153248869df6cc6de26422ce34 Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Fri, 8 Nov 2024 22:13:02 +0000
Subject: [PATCH 11/13] proper TapTweak point-addition operates on even
 internal key representation

Thanks to @conradoplg for spotting this. The internal key is supposed
to be represented as an even-parity point when adding the TapTweak
point t*G. I added a regression test to ensure the tweaked verifying
key and its parity match the BIP341 spec.
---
 frost-secp256k1-tr/src/lib.rs                 | 20 +++--
 .../tests/helpers/vectors_dkg.json            | 20 ++---
 frost-secp256k1-tr/tests/tweaking_tests.rs    | 80 +++++++++++++++++--
 3 files changed, 92 insertions(+), 28 deletions(-)

diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs
index d90f5769..56aa52ca 100644
--- a/frost-secp256k1-tr/src/lib.rs
+++ b/frost-secp256k1-tr/src/lib.rs
@@ -746,7 +746,7 @@ pub mod keys {
     }
 
     /// Trait for tweaking a key component following BIP-341
-    pub trait Tweak {
+    pub trait Tweak: EvenY {
         /// Convert the given type to add a tweak.
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self;
     }
@@ -755,10 +755,12 @@ pub mod keys {
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
             let t = tweak(&self.verifying_key().to_element(), merkle_root);
             let tp = ProjectivePoint::GENERATOR * t;
-            let verifying_key = VerifyingKey::new(self.verifying_key().to_element() + tp);
+            let public_key_package = self.into_even_y(None);
+            let verifying_key =
+                VerifyingKey::new(public_key_package.verifying_key().to_element() + tp);
             // Recreate verifying share map with negated VerifyingShares
             // values.
-            let verifying_shares: BTreeMap<_, _> = self
+            let verifying_shares: BTreeMap<_, _> = public_key_package
                 .verifying_shares()
                 .iter()
                 .map(|(i, vs)| {
@@ -774,15 +776,17 @@ pub mod keys {
         fn tweak<T: AsRef<[u8]>>(self, merkle_root: Option<T>) -> Self {
             let t = tweak(&self.verifying_key().to_element(), merkle_root);
             let tp = ProjectivePoint::GENERATOR * t;
-            let verifying_key = VerifyingKey::new(self.verifying_key().to_element() + tp);
-            let signing_share = SigningShare::new(self.signing_share().to_scalar() + t);
-            let verifying_share = VerifyingShare::new(self.verifying_share().to_element() + tp);
+            let key_package = self.into_even_y(None);
+            let verifying_key = VerifyingKey::new(key_package.verifying_key().to_element() + tp);
+            let signing_share = SigningShare::new(key_package.signing_share().to_scalar() + t);
+            let verifying_share =
+                VerifyingShare::new(key_package.verifying_share().to_element() + tp);
             KeyPackage::new(
-                *self.identifier(),
+                *key_package.identifier(),
                 signing_share,
                 verifying_share,
                 verifying_key,
-                *self.min_signers(),
+                *key_package.min_signers(),
             )
         }
     }
diff --git a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
index 5df7b6e3..9fcbc839 100644
--- a/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
+++ b/frost-secp256k1-tr/tests/helpers/vectors_dkg.json
@@ -7,7 +7,7 @@
     "hash": "SHA-256"
   },
   "inputs": {
-    "verifying_key": "02409611d2fd36025b75caa15f1d70f6d5cfea9cc4254d29580075fdf832d934db",
+    "verifying_key": "03849089de77b56bd35fcbfc70bf38e73448131090acc75d538a5cea63cc3dcefe",
     "1": {
       "identifier": 1,
       "signing_key": "68e3f6904c6043973515a36bf7801a71597da35733f21305d75a5234f06e4529",
@@ -18,8 +18,8 @@
         "2": "1dd3cb3e2370e6af22917415f0ad584514807b58b3cc40d2230a26e115f02771",
         "3": "dd25ee86acd01f996618aa0d1153f5e8fbc929a8e8a18b8f0a15f91d087217e2"
       },
-      "verifying_share": "03487e21b8658bffc7903fa2f6435bdae7a417990a27698c273a14f72bece665f3",
-      "signing_share": "051f56860fd1225af141494c573ce33da026e1c5f8c5099b73c9ad28675a8389"
+      "verifying_share": "02a8bf413b5d7af0e692fba967540cde8009f161a4d721f8c88649c1933bbb7531",
+      "signing_share": "f1be455a8ec9ab86ef8438f23a5cfdf70153aa2785d4bebba83e0840403e4bf3"
     },
     "2": {
       "identifier": 2,
@@ -27,12 +27,7 @@
       "coefficient": "f7ba0cbbffbea8aaceb3ade54bdbf35bafb1cda15b65ad490e0c63dd069a7c9f",
       "vss_commitments": ["03ef10370a008cd95e179dc51e2cb7828f30b72d254e5166484f927c84ab326582", "022ce0dac0db217ba326fbbe3e6132d45e2a4bfa0a0c3790d91eacce9a1c2d6a10"],
       "proof_of_knowledge": "a319dd51cf64b3896c22f54154812d4ae76cfa95f46f53ef69241fd702456fef32da76cc93d3a541ca495b723e793ee90c32440da5f314e2e58a2dc30550314a",
-      "signing_shares": {
-        "1": "b489a711942526abbb5330a8215d2e740f7dbddec3452006993a8cea3ac278cb",
-        "3": "20255dc07b1fb78bdf90bd85fd2389c988c8250faee11826656a09142fa9fc97"
-      },
-      "verifying_share": "02aab83a9c57284041ae6add2d5e2ea43d715c6607e03c4c22ca5aae10086076f4",
-      "signing_share": "65abaabc81c1d4827c8bd148a3d6f47b37d7b811e213c9fc3e1a3e573a56feae"
+      "verifying_share": "029ecb3a4db28a82e7b8d600d42711b02790dde3f063f0ecec6f812c1c5d7dcefc"
     },
     "3": {
       "identifier": 3,
@@ -40,12 +35,7 @@
       "coefficient": "42ff6f39ce4f97f279781378ebcf93df47add84d75882cd31b266e83f76e25f6",
       "vss_commitments": ["02da186c3863c5600b471a2799cb6f15ae4d8315a2f225c177798880e75ac820a0", "03e6a36e7fa4b117c1aa428886672e3a35d926bb4c585a9b07d8ee9a3387420067"],
       "proof_of_knowledge": "6e115d9e63fd15d432b380ccf1ec4ed03340fcf96caeae8985aedb5f905b1a65dc422ffe5878988fbbc55454857736c7755d9c8f5ee6822c8833ea21d54dba36",
-      "signing_shares": {
-        "1": "da5c7f5238079835fe71f746364bb8756a7dcb228aeea686fa2aaa44dfec929c",
-        "2": "0d47e4b622ee3804bff8cfe088653efefe865cce0c065aecbf7e318182b89e2d"
-      },
-      "verifying_share": "031f1571625e54d9bb0e58e228abe5430460ab9d414bced42250fd09526476290b",
-      "signing_share": "c637fef2f3b286aa07d65944f07105b8cf888e5dcb628a5d086acf860d5379d3"
+      "verifying_share": "02c98b3c2e9f4bde4cf90dc9c7be639e5adda6ea09fc605239880a22cb836f7145"
     }
   }
 }
diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs
index f95a32b7..83b7a032 100644
--- a/frost-secp256k1-tr/tests/tweaking_tests.rs
+++ b/frost-secp256k1-tr/tests/tweaking_tests.rs
@@ -1,7 +1,11 @@
 use std::{error::Error, vec};
 
-use frost_secp256k1_tr::*;
+use k256::elliptic_curve::point::AffineCoordinates;
+use k256::ProjectivePoint;
 use keys::Tweak;
+use sha2::{Digest, Sha256};
+
+use frost_secp256k1_tr::*;
 
 mod helpers;
 
@@ -11,7 +15,7 @@ fn check_tweaked_sign_with_dealer() -> Result<(), Box<dyn Error>> {
     use rand::thread_rng;
     use std::collections::BTreeMap;
 
-    let merkle_root: Vec<u8> = vec![];
+    let merkle_root: Vec<u8> = vec![12; 32];
 
     let mut rng = thread_rng();
     let max_signers = 5;
@@ -67,13 +71,79 @@ fn check_tweaked_sign_with_dealer() -> Result<(), Box<dyn Error>> {
         .verify(message, &group_signature)
         .expect_err("signature should not be valid for untweaked pubkey_package");
 
-    let pubkey_package = pubkey_package.tweak(Some(&merkle_root));
-    pubkey_package
+    let pubkey_package_tweaked = pubkey_package.clone().tweak(Some(&merkle_root));
+    pubkey_package_tweaked
         .verifying_key()
         .verify(message, &group_signature)
         .expect("signature should be valid for tweaked pubkey_package");
 
-    helpers::verify_signature(message, &group_signature, pubkey_package.verifying_key());
+    helpers::verify_signature(
+        message,
+        &group_signature,
+        pubkey_package_tweaked.verifying_key(),
+    );
+
+    // Confirm the internal (untweaked) group key can be provided to access
+    // script spending paths under the output (tweaked) group key.
+    let (expected_parity, expected_tr_output_pubkey) = taproot_tweak_pubkey(
+        pubkey_package
+            .verifying_key()
+            .to_element()
+            .to_affine()
+            .x()
+            .into(),
+        &merkle_root,
+    );
+
+    let tr_output_point = pubkey_package_tweaked
+        .verifying_key()
+        .to_element()
+        .to_affine();
+
+    let tr_output_pubkey: [u8; 32] = tr_output_point.x().into();
+    let tr_output_parity: bool = tr_output_point.y_is_odd().into();
+
+    assert_eq!(
+        tr_output_pubkey, expected_tr_output_pubkey,
+        "taproot output pubkey does not match"
+    );
+
+    assert_eq!(
+        tr_output_parity, expected_parity,
+        "taproot output pubkey parity bit does not match"
+    );
 
     Ok(())
 }
+
+/// Emulates the BIP341 helper function:
+///
+///   def taproot_tweak_pubkey(pubkey, h):
+///       t = int_from_bytes(tagged_hash("TapTweak", pubkey + h))
+///       if t >= SECP256K1_ORDER:
+///           raise ValueError
+///       P = lift_x(int_from_bytes(pubkey))
+///       if P is None:
+///           raise ValueError
+///       Q = point_add(P, point_mul(G, t))
+///       return 0 if has_even_y(Q) else 1, bytes_from_int(x(Q))
+///
+fn taproot_tweak_pubkey(pubkey: [u8; 32], merkle_root: &[u8]) -> (bool, [u8; 32]) {
+    let prefix = Sha256::digest(b"TapTweak");
+    let tweak_hash = Sha256::new()
+        .chain_update(&prefix)
+        .chain_update(&prefix)
+        .chain_update(&pubkey)
+        .chain_update(merkle_root)
+        .finalize();
+    let t = k256::Scalar::from(
+        k256::elliptic_curve::ScalarPrimitive::new(k256::U256::from_be_slice(&tweak_hash)).unwrap(),
+    );
+
+    let mut pubkey_even_bytes = [0x02; 33];
+    pubkey_even_bytes[1..].copy_from_slice(&pubkey);
+    let pubkey_even = Secp256K1Group::deserialize(&pubkey_even_bytes).unwrap();
+
+    let tr_output_key = (pubkey_even + ProjectivePoint::GENERATOR * t).to_affine();
+    (tr_output_key.y_is_odd().into(), tr_output_key.x().into())
+}

From aa4b5fbd5f0e4fea5455570218b951bda5c908ce Mon Sep 17 00:00:00 2001
From: conduition <conduition@proton.me>
Date: Fri, 8 Nov 2024 22:22:26 +0000
Subject: [PATCH 12/13] clippy test fixes

---
 frost-secp256k1-tr/tests/tweaking_tests.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/frost-secp256k1-tr/tests/tweaking_tests.rs b/frost-secp256k1-tr/tests/tweaking_tests.rs
index 83b7a032..3fc74aef 100644
--- a/frost-secp256k1-tr/tests/tweaking_tests.rs
+++ b/frost-secp256k1-tr/tests/tweaking_tests.rs
@@ -131,9 +131,9 @@ fn check_tweaked_sign_with_dealer() -> Result<(), Box<dyn Error>> {
 fn taproot_tweak_pubkey(pubkey: [u8; 32], merkle_root: &[u8]) -> (bool, [u8; 32]) {
     let prefix = Sha256::digest(b"TapTweak");
     let tweak_hash = Sha256::new()
-        .chain_update(&prefix)
-        .chain_update(&prefix)
-        .chain_update(&pubkey)
+        .chain_update(prefix)
+        .chain_update(prefix)
+        .chain_update(pubkey)
         .chain_update(merkle_root)
         .finalize();
     let t = k256::Scalar::from(

From 59d1da299c7c04afc2be9c1a5f8a63a1b6cb35b9 Mon Sep 17 00:00:00 2001
From: Conrado Gouvea <conradoplg@gmail.com>
Date: Mon, 11 Nov 2024 19:10:43 -0300
Subject: [PATCH 13/13] fix no-std issues and warnings

---
 frost-core/src/lib.rs    | 2 ++
 frost-core/src/traits.rs | 3 +--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs
index 70534fa5..7d78e2df 100644
--- a/frost-core/src/lib.rs
+++ b/frost-core/src/lib.rs
@@ -636,8 +636,10 @@ where
 
     Ok(signature)
 }
+
 /// Optional cheater detection feature
 /// Each share is verified to find the cheater
+#[cfg(feature = "cheater-detection")]
 fn detect_cheater<C: Ciphersuite>(
     group_commitment: &GroupCommitment<C>,
     pubkeys: &keys::PublicKeyPackage<C>,
diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs
index a9c8ca05..4e3f959d 100644
--- a/frost-core/src/traits.rs
+++ b/frost-core/src/traits.rs
@@ -4,9 +4,8 @@ use core::{
     fmt::Debug,
     ops::{Add, Mul, Sub},
 };
-use std::borrow::Cow;
 
-use alloc::{collections::BTreeMap, vec::Vec};
+use alloc::{borrow::Cow, collections::BTreeMap, vec::Vec};
 use rand_core::{CryptoRng, RngCore};
 
 use crate::{