encapsulate BIP341 tapscript commitment in new SigningTarget type

I added the SigningTarget type, which encapsulates both the message
to be signed, and also the tapscript merkle root which the signature
should commit to. This allows a FROST group to dynamically select
what taproot tweak they would like to commit their signatures to,
and to optionally elect not to commit to any tweak at all if desired.
This commit is contained in:
conduition 2024-03-02 20:47:49 +00:00 committed by zebra-lucky
parent c63a3ca699
commit e5b3f5dd90
30 changed files with 399 additions and 123 deletions

View File

@ -22,6 +22,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>,
}
@ -32,9 +33,15 @@ where
{
fn from((vk, sig, msg): (VerifyingKey<C>, Signature<C>, &'msg M)) -> Self {
// Compute c now to avoid dependency on the msg lifetime.
let c = <C>::challenge(&sig.R, &vk, msg.as_ref());
let sig_target = SigningTarget::from_message(msg);
let c = <C>::challenge(&sig.R, &vk, &sig_target);
Self { vk, sig, c }
Self {
vk,
sig,
sig_params: sig_target.sig_params,
c,
}
}
}
@ -50,7 +57,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)
}
}
@ -119,7 +127,7 @@ where
for item in self.signatures.iter() {
let z = item.sig.z;
let R = <C>::effective_nonce_element(item.sig.R);
let vk = <C>::effective_pubkey_element(&item.vk);
let vk = <C>::effective_pubkey_element(&item.vk, &item.sig_params);
let blind = <<C::Group as Group>::Field>::random(&mut rng);

View File

@ -51,7 +51,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};
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]_.
@ -340,6 +340,53 @@ 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)]
@ -353,18 +400,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>
@ -374,14 +412,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(),
}
}
@ -393,6 +436,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))]
@ -412,7 +460,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(),
);
@ -592,7 +640,7 @@ where
z,
group_commitment.0,
&pubkeys.verifying_key,
signing_package.message().as_slice(),
&signing_package.sig_target,
);
// Verify the aggregate signature
@ -609,7 +657,7 @@ where
let challenge = <C>::challenge(
&group_commitment.0,
&pubkeys.verifying_key,
signing_package.message().as_slice(),
&signing_package.sig_target,
);
// Verify the signature shares.
@ -643,6 +691,7 @@ where
&challenge,
&group_commitment,
&pubkeys.verifying_key,
&signing_package.sig_target.sig_params,
)?;
}

View File

@ -93,10 +93,11 @@ where
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);
let vsh = <C>::effective_verifying_share(&verifying_share, &verifying_key, &sig_params);
if (<C::Group>::generator() * self.share)
!= (commitment_share + (vsh * challenge.0 * lambda_i))
@ -222,7 +223,7 @@ pub fn sign<C: Ciphersuite>(
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.
@ -233,6 +234,7 @@ pub fn sign<C: Ciphersuite>(
lambda_i,
key_package,
challenge,
&signing_package.sig_target.sig_params,
);
Ok(signature_share)

View File

@ -3,7 +3,8 @@
use rand_core::{CryptoRng, RngCore};
use crate::{
random_nonzero, Challenge, Ciphersuite, Error, Field, Group, Scalar, Signature, VerifyingKey,
random_nonzero, Challenge, Ciphersuite, Error, Field, Group, Scalar, Signature, SigningTarget,
VerifyingKey,
};
/// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`].
@ -45,19 +46,25 @@ where
<<C::Group as Group>::Field as Field>::serialize(&self.scalar)
}
/// Create a signature `msg` using this `SigningKey`.
pub fn sign<R: RngCore + CryptoRng>(&self, mut rng: R, msg: &[u8]) -> Signature<C> {
/// Create a signature on the given `sig_target` 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();
let public = VerifyingKey::<C>::from(*self);
let secret = <C>::effective_secret_key(self.scalar, &public);
let secret = <C>::effective_secret_key(self.scalar, &public, &sig_target.sig_params);
let mut k = random_nonzero::<C, R>(&mut rng);
let R = <C::Group>::generator() * k;
k = <C>::effective_nonce_secret(k, &R);
// Generate Schnorr challenge
let c: Challenge<C> = <C>::challenge(&R, &public, msg);
let c: Challenge<C> = <C>::challenge(&R, &public, &sig_target);
<C>::single_sig_finalize(k, R, secret, &c, &public)
<C>::single_sig_finalize(k, R, secret, &c, &public, &sig_target.sig_params)
}
/// Creates a SigningKey from a scalar.

View File

@ -301,7 +301,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));

View File

@ -311,6 +311,10 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
// Check that the generated signature matches the test vector signature
let group_signature = group_signature_result.unwrap();
assert_eq!(
hex::encode(group_signature.serialize()),
hex::encode(signature_bytes.clone()),
);
assert_eq!(group_signature.serialize().as_ref(), signature_bytes);
// Aggregate the FROST signature from our signature shares

View File

@ -11,7 +11,7 @@ use crate::{
challenge,
keys::{KeyPackage, VerifyingShare},
round1, round2, BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError,
Signature, VerifyingKey,
Signature, SigningTarget, VerifyingKey,
};
/// A prime order finite field GF(q) over which all scalar values for our prime order group can be
@ -149,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.
///
@ -170,6 +189,10 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
/// `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.
@ -237,13 +260,14 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
/// (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 = <Self>::challenge(&signature.R, public_key, msg);
let sig_target = sig_target.into();
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.
@ -258,9 +282,9 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
fn challenge(
R: &Element<Self>,
verifying_key: &VerifyingKey<Self>,
msg: &[u8],
sig_target: &SigningTarget<Self>,
) -> Challenge<Self> {
challenge(R, verifying_key, msg)
challenge(R, verifying_key, &sig_target.message)
}
/// Finalize an aggregated group signature. This is used by frost-sepc256k1-tr
@ -269,7 +293,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
z: <<Self::Group as Group>::Field as Field>::Scalar,
R: Element<Self>,
_verifying_key: &VerifyingKey<Self>,
_msg: &[u8],
_sig_target: &SigningTarget<Self>,
) -> Signature<Self> {
Signature { R, z }
}
@ -281,6 +305,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
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 }
@ -294,6 +319,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
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,
@ -310,6 +336,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
/// 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()
}
@ -330,6 +357,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
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
}
@ -364,6 +392,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
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()
}

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Debug};
#[cfg(any(test, feature = "test-impl"))]
use hex::FromHex;
use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature};
use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature, SigningTarget};
#[cfg(feature = "serde")]
use crate::serialization::ElementSerialization;
@ -63,13 +63,14 @@ 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 = C::effective_nonce_element(signature.R);
let vk = C::effective_pubkey_element(&self);
let vk = C::effective_pubkey_element(&self, sig_params);
let zB = C::Group::generator() * signature.z;
let cA = vk * challenge.0;
@ -82,9 +83,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.

View File

@ -155,6 +155,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;
@ -164,6 +172,8 @@ impl Ciphersuite for Ed25519Sha512 {
type SignatureSerialization = [u8; 64];
type SigningParameters = ();
/// H1 for FROST(Ed25519, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.1

View File

@ -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);
}

View File

@ -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
}
"#;

View File

@ -150,6 +150,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;
@ -159,6 +167,8 @@ impl Ciphersuite for Ed448Shake256 {
type SignatureSerialization = [u8; 114];
type SigningParameters = ();
/// H1 for FROST(Ed448, SHAKE256)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.1

View File

@ -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);
}

View File

@ -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
}
"#;

View File

@ -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://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.1

View File

@ -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);
}

View File

@ -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
}
"#;

View File

@ -141,6 +141,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;
@ -150,6 +158,8 @@ impl Ciphersuite for Ristretto255Sha512 {
type SignatureSerialization = [u8; 64];
type SigningParameters = SigningParameters;
/// H1 for FROST(ristretto255, SHA-512)
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.1

View File

@ -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);
}

View File

@ -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
}
"#;

View File

@ -26,6 +26,7 @@ document-features = "0.2.7"
frost-core = { path = "../frost-core", version = "1.0.0" }
frost-rerandomized = { path = "../frost-rerandomized", version = "1.0.0" }
k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"] }
serde = { version = "1.0.160", features = ["derive"], optional = true }
rand_core = "0.6"
sha2 = "0.10.2"
@ -49,7 +50,7 @@ serialization = ["serde", "frost-core/serialization"]
## 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"]
serde = ["frost-core/serde", "dep:serde"]
## Enable cheater detection
cheater-detection = ["frost-core/cheater-detection"]

View File

@ -34,10 +34,6 @@ pub use frost_core::{
pub use rand_core;
/// The tapscript path is provably unspendable by committing to an empty merkle root.
/// Perhaps we can support taptree commitments in the future.
const UNSPENDABLE_MERKLE_ROOT: [u8; 0] = [];
/// An error.
pub type Error = frost_core::Error<Secp256K1Sha256>;
@ -210,20 +206,25 @@ fn tagged_hash(tag: &str) -> Sha256 {
}
/// Create a BIP341 compliant taproot tweak
fn tweak(
fn tweak<T: AsRef<[u8]>>(
public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
merkle_root: &[u8],
merkle_root: Option<T>,
) -> Scalar {
let mut hasher = tagged_hash("TapTweak");
hasher.update(public_key.to_affine().x());
hasher.update(merkle_root);
hasher_to_scalar(hasher)
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(
fn tweaked_public_key<T: AsRef<[u8]>>(
public_key: &VerifyingKey,
merkle_root: &[u8],
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() {
@ -232,6 +233,37 @@ fn tweaked_public_key(
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;
@ -241,6 +273,8 @@ impl Ciphersuite for Secp256K1Sha256 {
type SignatureSerialization = [u8; 65];
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
@ -295,12 +329,19 @@ impl Ciphersuite for Secp256K1Sha256 {
}
/// Generates the challenge as is required for Schnorr signatures.
fn challenge(R: &Element<S>, verifying_key: &VerifyingKey, msg: &[u8]) -> Challenge<S> {
fn challenge(
R: &Element<S>,
verifying_key: &VerifyingKey,
sig_target: &SigningTarget,
) -> Challenge<S> {
let mut preimage = vec![];
let tweaked_pk = tweaked_public_key(&verifying_key, &UNSPENDABLE_MERKLE_ROOT);
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(msg);
preimage.extend_from_slice(sig_target.message().as_ref());
Challenge::from_scalar(S::H2(&preimage[..]))
}
@ -310,13 +351,19 @@ impl Ciphersuite for Secp256K1Sha256 {
z_raw: <<Self::Group as Group>::Field as Field>::Scalar,
R: Element<Self>,
verifying_key: &VerifyingKey,
msg: &[u8],
sig_target: &SigningTarget,
) -> Signature {
let challenge = Self::challenge(&R, verifying_key, msg);
let challenge = Self::challenge(&R, verifying_key, &sig_target);
let t = tweak(verifying_key.element(), &UNSPENDABLE_MERKLE_ROOT);
let t = tweak(
verifying_key.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, &UNSPENDABLE_MERKLE_ROOT);
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 {
@ -332,8 +379,10 @@ impl Ciphersuite for Secp256K1Sha256 {
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, &UNSPENDABLE_MERKLE_ROOT);
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)
@ -352,6 +401,7 @@ impl Ciphersuite for Secp256K1Sha256 {
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() {
@ -361,10 +411,11 @@ impl Ciphersuite for Secp256K1Sha256 {
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, &UNSPENDABLE_MERKLE_ROOT)
.to_affine()
.y_is_odd()
.into();
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();
}
@ -374,8 +425,12 @@ impl Ciphersuite for Secp256K1Sha256 {
/// Computes the effective pubkey point by tweaking the verifying key with a
/// provably unspendable taproot tweak.
fn effective_pubkey_element(public_key: &VerifyingKey) -> <Self::Group as Group>::Element {
let tweaked_pubkey = tweaked_public_key(public_key, &UNSPENDABLE_MERKLE_ROOT);
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 {
@ -398,11 +453,16 @@ impl Ciphersuite for Secp256K1Sha256 {
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.element(),
sig_params.tapscript_merkle_root.as_ref(),
);
if Self::Group::y_is_odd(public_key.element()) {
-secret + tweak(public_key.element(), &UNSPENDABLE_MERKLE_ROOT)
-secret + t
} else {
secret + tweak(public_key.element(), &UNSPENDABLE_MERKLE_ROOT)
secret + t
}
}
@ -441,10 +501,11 @@ impl Ciphersuite for Secp256K1Sha256 {
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, &UNSPENDABLE_MERKLE_ROOT)
tweaked_public_key(verifying_key, sig_params.tapscript_merkle_root.as_ref())
.to_affine()
.y_is_odd()
.into();

View File

@ -1091,19 +1091,19 @@
"outputs": [
{
"identifier": 129,
"sig_share": "8ec041c83e92c21d349ee6335434d284d33bbe1dfddc9d0a40972d6bb7d2e539"
"sig_share": "e0f12b2fe652921203e8653ca6eeecef1a3ff64e11ac8dbd3912a7ac50b212c5"
},
{
"identifier": 256,
"sig_share": "5fa76c4dc2cded6680d660dc1fc7a705c467d612ddf20f9ef1f41461fa99c765"
"sig_share": "6bf65426790fd1b730ac7873a7b8ea0ce37aebf3c137d7ffd9b30cc2df32bf40"
},
{
"identifier": 257,
"sig_share": "87533d65930f6daafac1b08089a2fc2044b193c552fd9151affd014613458b0d"
"sig_share": "46da290402d61ae2b6bb95ffcfbbd8ecd93b5059b7cf104514655dddfcc845b6"
}
]
},
"final_output": {
"sig": "0354997f922511dba38d16973092a331f4039477bf4a2d77b438a317862795b267b68989a15d1822b71b981109fe784d9b288f2c2e722983b27568869d61f38f2a"
"sig": "0354997f922511dba38d16973092a331f4039477bf4a2d77b438a317862795b26793c1a85a62387eabeb5073b01e63afea1c4755b4db6ad5c66758b3c05c76d67a"
}
}

View File

@ -63,15 +63,15 @@
"outputs": [
{
"identifier": 1,
"sig_share": "971bbcb0383c0fc47a710a8cae41a123cb44808328ce30880a8b66f1f15766fb"
"sig_share": "2ffc305d1694fd84108b84d98306a1af807c6ad9bc3a2d8e448a09643202a15b"
},
{
"identifier": 3,
"sig_share": "33d0471177d384d3ec22731abbdd942851c78fbdbed0fb54149d657820228b55"
"sig_share": "a8c392566ea29e852b4080a028bf5547166c87e703e4fb7136d4ebef65f99b3f"
}
]
},
"final_output": {
"sig": "030c776a9516a77808b70a31e74f1464814a6fcf897fb3a6bd84c7a9a9a7a5bcb8c86bda8b2fa8e74c949a8e4915b1f5dc3e9c8ab9cd98f9d513ef05a2cfb03363"
"sig": "030c776a9516a77808b70a31e74f1464814a6fcf897fb3a6bd84c7a9a9a7a5bcb8d8bfc2b385379c093bcc0579abc5f6f696e8f2c0c01f28ff7b5ef55397fc3c9a"
}
}

View File

@ -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);
}

View File

@ -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
}
"#;

View File

@ -2,4 +2,4 @@
source: frost-secp256k1-tr/tests/serialization_tests.rs
expression: "hex::encode(&bytes)"
---
00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c64
00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c6400

View File

@ -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://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1

View File

@ -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);
}

View File

@ -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
}
"#;