Refactor Ciphersuite taproot methods for universal applicability (#2)
The taproot compatibility methods added to Ciphersuite were very specific to taproot. I renamed them, and tidied up their usage to remove unnecessary if/else branches. This should make them applicable to any ciphersuite which needs to modify such internal processes of the FROST algorithm. This change should be a pure refactor with no logical changes.
This commit is contained in:
parent
a66b9a2e7c
commit
142556fabd
|
@ -118,12 +118,8 @@ where
|
|||
|
||||
for item in self.signatures.iter() {
|
||||
let z = item.sig.z;
|
||||
let mut R = item.sig.R;
|
||||
let mut vk = item.vk.element;
|
||||
if <C>::is_taproot_compat() {
|
||||
R = <C>::taproot_compat_R(&item.sig.R);
|
||||
vk = <C>::tweaked_public_key(&item.vk.element);
|
||||
}
|
||||
let R = <C>::effective_nonce_element(item.sig.R);
|
||||
let vk = <C>::effective_pubkey_element(&item.vk);
|
||||
|
||||
let blind = <<C::Group as Group>::Field>::random(&mut rng);
|
||||
|
||||
|
|
|
@ -588,19 +588,12 @@ where
|
|||
z = z + signature_share.share;
|
||||
}
|
||||
|
||||
if <C>::is_taproot_compat() {
|
||||
let challenge = <C>::challenge(
|
||||
&group_commitment.0,
|
||||
&pubkeys.verifying_key,
|
||||
signing_package.message().as_slice(),
|
||||
);
|
||||
z = <C>::aggregate_tweak_z(z, &challenge, &pubkeys.verifying_key.element);
|
||||
}
|
||||
|
||||
let signature = Signature {
|
||||
R: group_commitment.0,
|
||||
let signature: Signature<C> = <C>::aggregate_sig_finalize(
|
||||
z,
|
||||
};
|
||||
group_commitment.0,
|
||||
&pubkeys.verifying_key,
|
||||
signing_package.message().as_slice(),
|
||||
);
|
||||
|
||||
// Verify the aggregate signature
|
||||
let verification_result = pubkeys
|
||||
|
|
|
@ -404,6 +404,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.
|
||||
|
|
|
@ -94,15 +94,10 @@ where
|
|||
group_commitment: &frost::GroupCommitment<C>,
|
||||
verifying_key: &frost::VerifyingKey<C>,
|
||||
) -> Result<(), Error<C>> {
|
||||
let mut commitment_share = group_commitment_share.0;
|
||||
let mut vsh = verifying_share.0;
|
||||
if <C>::is_taproot_compat() {
|
||||
commitment_share = <C>::taproot_compat_commitment_share(
|
||||
&group_commitment_share.0,
|
||||
&group_commitment.0,
|
||||
);
|
||||
vsh = <C>::taproot_compat_verifying_share(&verifying_share.0, &verifying_key.element);
|
||||
}
|
||||
let commitment_share =
|
||||
<C>::effective_commitment_share(group_commitment_share.clone(), &group_commitment);
|
||||
let vsh = <C>::effective_verifying_share(&verifying_share, &verifying_key);
|
||||
|
||||
if (<C::Group>::generator() * self.share)
|
||||
!= (commitment_share + (vsh * challenge.0 * lambda_i))
|
||||
{
|
||||
|
@ -231,26 +226,14 @@ pub fn sign<C: Ciphersuite>(
|
|||
);
|
||||
|
||||
// Compute the Schnorr signature share.
|
||||
if <C>::is_taproot_compat() {
|
||||
let signature_share = <C>::compute_taproot_compat_signature_share(
|
||||
signer_nonces,
|
||||
binding_factor,
|
||||
group_commitment,
|
||||
lambda_i,
|
||||
key_package,
|
||||
challenge,
|
||||
);
|
||||
let signature_share = <C>::compute_signature_share(
|
||||
signer_nonces,
|
||||
binding_factor,
|
||||
group_commitment,
|
||||
lambda_i,
|
||||
key_package,
|
||||
challenge,
|
||||
);
|
||||
|
||||
Ok(signature_share)
|
||||
} else {
|
||||
let signature_share = compute_signature_share(
|
||||
signer_nonces,
|
||||
binding_factor,
|
||||
lambda_i,
|
||||
key_package,
|
||||
challenge,
|
||||
);
|
||||
|
||||
Ok(signature_share)
|
||||
}
|
||||
Ok(signature_share)
|
||||
}
|
||||
|
|
|
@ -48,26 +48,16 @@ where
|
|||
/// Create a signature `msg` using this `SigningKey`.
|
||||
pub fn sign<R: RngCore + CryptoRng>(&self, mut rng: R, msg: &[u8]) -> Signature<C> {
|
||||
let public = VerifyingKey::<C>::from(*self);
|
||||
let mut secret = self.scalar;
|
||||
if <C>::is_taproot_compat() {
|
||||
secret = <C>::tweaked_secret_key(secret, &public.element);
|
||||
}
|
||||
let secret = <C>::effective_secret_key(self.scalar, &public);
|
||||
|
||||
let mut k = random_nonzero::<C, R>(&mut rng);
|
||||
let R = <C::Group>::generator() * k;
|
||||
if <C>::is_taproot_compat() {
|
||||
k = <C>::taproot_compat_nonce(k, &R);
|
||||
}
|
||||
k = <C>::effective_nonce_secret(k, &R);
|
||||
|
||||
// Generate Schnorr challenge
|
||||
let c: Challenge<C> = <C>::challenge(&R, &public, msg);
|
||||
|
||||
if <C>::is_taproot_compat() {
|
||||
let z = <C>::tweaked_z(k, secret, c.0, &public.element);
|
||||
Signature { R, z }
|
||||
} else {
|
||||
let z = k + (c.0 * secret);
|
||||
Signature { R, z }
|
||||
}
|
||||
<C>::single_sig_finalize(k, R, secret, &c, &public)
|
||||
}
|
||||
|
||||
/// Creates a SigningKey from a scalar.
|
||||
|
|
|
@ -7,7 +7,12 @@ use std::{
|
|||
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::{challenge, Challenge, Error, FieldError, GroupError, Signature, VerifyingKey};
|
||||
use crate::{
|
||||
challenge,
|
||||
keys::{KeyPackage, VerifyingShare},
|
||||
round1, round2, BindingFactor, Challenge, Error, FieldError, GroupCommitment, GroupError,
|
||||
Signature, VerifyingKey,
|
||||
};
|
||||
|
||||
/// A prime order finite field GF(q) over which all scalar values for our prime order group can be
|
||||
/// multiplied are defined.
|
||||
|
@ -258,94 +263,115 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
|
|||
challenge(R, verifying_key, msg)
|
||||
}
|
||||
|
||||
/// determine code is taproot compatible (used in frost-sepc256k1-tr)
|
||||
fn is_taproot_compat() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// aggregate tweak z (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn aggregate_tweak_z(
|
||||
/// Finalize an aggregated group signature. This is used by frost-sepc256k1-tr
|
||||
/// to ensure the signature is valid under BIP340; for all other ciphersuites
|
||||
/// this simply returns a [`Signature`] wrapping `R` and `z`.
|
||||
fn aggregate_sig_finalize(
|
||||
z: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
challenge: &Challenge<Self>,
|
||||
verifying_key: &Element<Self>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
panic!("Not implemented");
|
||||
R: Element<Self>,
|
||||
_verifying_key: &VerifyingKey<Self>,
|
||||
_msg: &[u8],
|
||||
) -> Signature<Self> {
|
||||
Signature { R, z }
|
||||
}
|
||||
|
||||
/// tweaked z for SigningKey sign (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn tweaked_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: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
verifying_key: &Element<Self>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
panic!("Not implemented");
|
||||
challenge: &Challenge<Self>,
|
||||
_verifying_key: &VerifyingKey<Self>,
|
||||
) -> Signature<Self> {
|
||||
let z = k + (challenge.0 * secret);
|
||||
Signature { R, z }
|
||||
}
|
||||
|
||||
/// signature_share compatible with taproot (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn compute_taproot_compat_signature_share(
|
||||
signer_nonces: &crate::round1::SigningNonces<Self>,
|
||||
binding_factor: crate::BindingFactor<Self>,
|
||||
group_commitment: crate::GroupCommitment<Self>,
|
||||
/// 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: &crate::keys::KeyPackage<Self>,
|
||||
key_package: &KeyPackage<Self>,
|
||||
challenge: Challenge<Self>,
|
||||
) -> crate::round2::SignatureShare<Self> {
|
||||
panic!("Not implemented");
|
||||
) -> round2::SignatureShare<Self> {
|
||||
round2::compute_signature_share(
|
||||
signer_nonces,
|
||||
binding_factor,
|
||||
lambda_i,
|
||||
key_package,
|
||||
challenge,
|
||||
)
|
||||
}
|
||||
|
||||
/// calculate tweaked public key (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn tweaked_public_key(
|
||||
public_key: &<Self::Group as Group>::Element,
|
||||
/// 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.
|
||||
/// For all other ciphersuites, this simply returns `verifying_key.to_element()`
|
||||
fn effective_pubkey_element(
|
||||
verifying_key: &VerifyingKey<Self>,
|
||||
) -> <Self::Group as Group>::Element {
|
||||
panic!("Not implemented");
|
||||
verifying_key.to_element()
|
||||
}
|
||||
|
||||
/// calculate taproot compatible R (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn taproot_compat_R(
|
||||
public_key: &<Self::Group as Group>::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.
|
||||
/// For all other ciphersuites, this simply returns the input `R`.
|
||||
fn effective_nonce_element(
|
||||
R: <Self::Group as Group>::Element,
|
||||
) -> <Self::Group as Group>::Element {
|
||||
panic!("Not implemented");
|
||||
R
|
||||
}
|
||||
|
||||
/// tweaked secret (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn tweaked_secret_key(
|
||||
/// 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.
|
||||
/// For all other ciphersuites, this simply returns `secret` unchanged.
|
||||
fn effective_secret_key(
|
||||
secret: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
public: &Element<Self>,
|
||||
_public: &VerifyingKey<Self>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
panic!("Not implemented");
|
||||
secret
|
||||
}
|
||||
|
||||
/// calculate taproot compatible nonce (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn taproot_compat_nonce(
|
||||
/// 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.
|
||||
/// For all other ciphersuites, this simply returns the input `nonce`.
|
||||
fn effective_nonce_secret(
|
||||
nonce: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
R: &Element<Self>,
|
||||
_R: &Element<Self>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
panic!("Not implemented");
|
||||
nonce
|
||||
}
|
||||
|
||||
/// calculate taproot compatible commitment share (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn taproot_compat_commitment_share(
|
||||
group_commitment_share: &<Self::Group as Group>::Element,
|
||||
group_commitment: &<Self::Group as Group>::Element,
|
||||
/// 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. For all other ciphersuites, this simply returns
|
||||
/// `group_commitment_share.to_element()`
|
||||
fn effective_commitment_share(
|
||||
group_commitment_share: round1::GroupCommitmentShare<Self>,
|
||||
_group_commitment: &GroupCommitment<Self>,
|
||||
) -> <Self::Group as Group>::Element {
|
||||
panic!("Not implemented");
|
||||
group_commitment_share.to_element()
|
||||
}
|
||||
|
||||
/// calculate taproot compatible verifying share (used in frost-sepc256k1-tr)
|
||||
#[allow(unused)]
|
||||
fn taproot_compat_verifying_share(
|
||||
verifying_share: &<Self::Group as Group>::Element,
|
||||
verifying_key: &<Self::Group as Group>::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. For all other ciphersuites, this simply returns
|
||||
/// `verifying_share.to_element()`
|
||||
fn effective_verifying_share(
|
||||
verifying_share: &VerifyingShare<Self>,
|
||||
_verifying_key: &VerifyingKey<Self>,
|
||||
) -> <Self::Group as Group>::Element {
|
||||
panic!("Not implemented");
|
||||
verifying_share.to_element()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,12 +68,9 @@ where
|
|||
// h * ( z * B - c * A - R) == 0
|
||||
//
|
||||
// where h is the cofactor
|
||||
let mut R = signature.R;
|
||||
let mut vk = self.element;
|
||||
if <C>::is_taproot_compat() {
|
||||
R = <C>::taproot_compat_R(&signature.R);
|
||||
vk = <C>::tweaked_public_key(&self.element);
|
||||
}
|
||||
let R = C::effective_nonce_element(signature.R);
|
||||
let vk = C::effective_pubkey_element(&self);
|
||||
|
||||
let zB = C::Group::generator() * signature.z;
|
||||
let cA = vk * challenge.0;
|
||||
let check = (zB - cA - R) * C::Group::cofactor();
|
||||
|
|
|
@ -13,7 +13,7 @@ use k256::{
|
|||
bigint::U256,
|
||||
group::prime::PrimeCurveAffine,
|
||||
hash2curve::{hash_to_field, ExpandMsgXmd},
|
||||
point::{AffineCoordinates, DecompactPoint},
|
||||
point::AffineCoordinates,
|
||||
sec1::{FromEncodedPoint, ToEncodedPoint},
|
||||
Field as FFField, PrimeField, ScalarPrimitive,
|
||||
},
|
||||
|
@ -29,11 +29,15 @@ mod tests;
|
|||
|
||||
// Re-exports in our public API
|
||||
pub use frost_core::{
|
||||
serde, Challenge, Ciphersuite, Element, Field, FieldError, Group, GroupError,
|
||||
serde, Challenge, Ciphersuite, Element, Field, FieldError, Group, GroupCommitment, GroupError,
|
||||
};
|
||||
|
||||
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>;
|
||||
|
||||
|
@ -218,40 +222,16 @@ fn tweak(
|
|||
|
||||
/// Create a BIP341 compliant tweaked public key
|
||||
fn tweaked_public_key(
|
||||
public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
|
||||
public_key: &VerifyingKey,
|
||||
merkle_root: &[u8],
|
||||
) -> <<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element {
|
||||
let mut pk = *public_key;
|
||||
if public_key.to_affine().y_is_odd().into() {
|
||||
let mut pk = public_key.to_element();
|
||||
if pk.to_affine().y_is_odd().into() {
|
||||
pk = -pk;
|
||||
}
|
||||
ProjectivePoint::GENERATOR * tweak(&pk, merkle_root) + pk
|
||||
}
|
||||
|
||||
/// Creates a real BIP341 tweaked public key by assuming an even y-coordinate.
|
||||
fn real_tweaked_pubkey(
|
||||
public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
|
||||
merkle_root: &[u8],
|
||||
) -> <<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element {
|
||||
let tweaked_pubkey = tweaked_public_key(public_key, merkle_root);
|
||||
AffinePoint::decompact(&tweaked_pubkey.to_affine().x())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Create a BIP341 compliant tweaked secret key
|
||||
fn tweaked_secret_key(
|
||||
secret: <<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
|
||||
public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
|
||||
merkle_root: &[u8],
|
||||
) -> <<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
|
||||
if public_key.to_affine().y_is_odd().into() {
|
||||
-secret + tweak(public_key, merkle_root)
|
||||
} else {
|
||||
secret + tweak(public_key, merkle_root)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ciphersuite for Secp256K1Sha256 {
|
||||
const ID: &'static str = CONTEXT_STRING;
|
||||
|
||||
|
@ -317,54 +297,58 @@ 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> {
|
||||
let mut preimage = vec![];
|
||||
let tweaked_public_key = tweaked_public_key(&verifying_key.to_element(), &[]);
|
||||
let tweaked_pk = tweaked_public_key(&verifying_key, &UNSPENDABLE_MERKLE_ROOT);
|
||||
preimage.extend_from_slice(&R.to_affine().x());
|
||||
preimage.extend_from_slice(&tweaked_public_key.to_affine().x());
|
||||
preimage.extend_from_slice(&tweaked_pk.to_affine().x());
|
||||
preimage.extend_from_slice(msg);
|
||||
Challenge::from_scalar(S::H2(&preimage[..]))
|
||||
}
|
||||
|
||||
/// determine code is taproot compatible
|
||||
fn is_taproot_compat() -> bool {
|
||||
true
|
||||
}
|
||||
/// 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,
|
||||
msg: &[u8],
|
||||
) -> Signature {
|
||||
let challenge = Self::challenge(&R, verifying_key, msg);
|
||||
|
||||
/// aggregate tweak z
|
||||
fn aggregate_tweak_z(
|
||||
z: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
challenge: &Challenge<S>,
|
||||
verifying_key: &Element<S>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
let t = tweak(verifying_key, &[]);
|
||||
let t = tweak(verifying_key.element(), &UNSPENDABLE_MERKLE_ROOT);
|
||||
let tc = t * challenge.clone().to_scalar();
|
||||
let tweaked_pubkey = tweaked_public_key(verifying_key, &[]);
|
||||
if tweaked_pubkey.to_affine().y_is_odd().into() {
|
||||
z - tc
|
||||
let tweaked_pubkey = tweaked_public_key(verifying_key, &UNSPENDABLE_MERKLE_ROOT);
|
||||
let z_tweaked = if tweaked_pubkey.to_affine().y_is_odd().into() {
|
||||
z_raw - tc
|
||||
} else {
|
||||
z + tc
|
||||
}
|
||||
z_raw + tc
|
||||
};
|
||||
Signature::new(R, z_tweaked)
|
||||
}
|
||||
|
||||
/// tweaked z for SigningKey sign
|
||||
fn tweaked_z(
|
||||
/// 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: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
verifying_key: &Element<S>,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
let tweaked_pubkey = tweaked_public_key(verifying_key, &[]);
|
||||
if tweaked_pubkey.to_affine().y_is_odd().into() {
|
||||
k - (challenge * secret)
|
||||
challenge: &Challenge<S>,
|
||||
verifying_key: &VerifyingKey,
|
||||
) -> Signature {
|
||||
let tweaked_pubkey = tweaked_public_key(verifying_key, &UNSPENDABLE_MERKLE_ROOT);
|
||||
let c = challenge.clone().to_scalar();
|
||||
let z = if tweaked_pubkey.to_affine().y_is_odd().into() {
|
||||
k - (c * secret)
|
||||
} else {
|
||||
k + (challenge * secret)
|
||||
}
|
||||
k + (c * secret)
|
||||
};
|
||||
|
||||
Signature::new(R, z)
|
||||
}
|
||||
|
||||
/// signature_share compatible with taproot
|
||||
fn compute_taproot_compat_signature_share(
|
||||
/// Compute a signature share, negating if required by BIP340.
|
||||
fn compute_signature_share(
|
||||
signer_nonces: &round1::SigningNonces,
|
||||
binding_factor: frost::BindingFactor<S>,
|
||||
group_commitment: frost_core::GroupCommitment<S>,
|
||||
group_commitment: GroupCommitment<S>,
|
||||
lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
|
||||
key_package: &frost::keys::KeyPackage<S>,
|
||||
challenge: Challenge<S>,
|
||||
|
@ -377,7 +361,7 @@ 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.element(), &[])
|
||||
let tweaked_pubkey_is_odd: bool = tweaked_public_key(public_key, &UNSPENDABLE_MERKLE_ROOT)
|
||||
.to_affine()
|
||||
.y_is_odd()
|
||||
.into();
|
||||
|
@ -388,28 +372,42 @@ impl Ciphersuite for Secp256K1Sha256 {
|
|||
frost::round2::compute_signature_share(&sn, binding_factor, lambda_i, &kp, challenge)
|
||||
}
|
||||
|
||||
/// calculate tweaked public key
|
||||
fn tweaked_public_key(
|
||||
public_key: &<Self::Group as Group>::Element,
|
||||
/// 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);
|
||||
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 {
|
||||
real_tweaked_pubkey(public_key, &[])
|
||||
if Self::Group::y_is_odd(&R) {
|
||||
-R
|
||||
} else {
|
||||
R
|
||||
}
|
||||
}
|
||||
|
||||
/// calculate taproot compatible R
|
||||
fn taproot_compat_R(R: &<Self::Group as Group>::Element) -> <Self::Group as Group>::Element {
|
||||
AffinePoint::decompact(&R.to_affine().x()).unwrap().into()
|
||||
}
|
||||
|
||||
/// tweaked secret
|
||||
fn tweaked_secret_key(
|
||||
/// 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: &Element<Self>,
|
||||
public_key: &VerifyingKey,
|
||||
) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
tweaked_secret_key(secret, public, &[])
|
||||
if Self::Group::y_is_odd(public_key.element()) {
|
||||
-secret + tweak(public_key.element(), &UNSPENDABLE_MERKLE_ROOT)
|
||||
} else {
|
||||
secret + tweak(public_key.element(), &UNSPENDABLE_MERKLE_ROOT)
|
||||
}
|
||||
}
|
||||
|
||||
/// calculate taproot compatible nonce
|
||||
fn taproot_compat_nonce(
|
||||
/// 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 {
|
||||
|
@ -420,33 +418,43 @@ impl Ciphersuite for Secp256K1Sha256 {
|
|||
}
|
||||
}
|
||||
|
||||
/// calculate taproot compatible commitment share
|
||||
fn taproot_compat_commitment_share(
|
||||
group_commitment_share: &Element<Self>,
|
||||
group_commitment: &Element<Self>,
|
||||
/// 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.to_affine().y_is_odd().into() {
|
||||
-group_commitment_share
|
||||
if group_commitment
|
||||
.clone()
|
||||
.to_element()
|
||||
.to_affine()
|
||||
.y_is_odd()
|
||||
.into()
|
||||
{
|
||||
-group_commitment_share.to_element()
|
||||
} else {
|
||||
*group_commitment_share
|
||||
group_commitment_share.to_element()
|
||||
}
|
||||
}
|
||||
|
||||
/// calculate taproot compatible verifying share
|
||||
fn taproot_compat_verifying_share(
|
||||
verifying_share: &<Self::Group as Group>::Element,
|
||||
verifying_key: &<Self::Group as Group>::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,
|
||||
) -> <Self::Group as Group>::Element {
|
||||
let mut vs = *verifying_share;
|
||||
let pubkey_is_odd: bool = verifying_key.to_affine().y_is_odd().into();
|
||||
let tweaked_pubkey_is_odd: bool = tweaked_public_key(verifying_key, &[])
|
||||
.to_affine()
|
||||
.y_is_odd()
|
||||
.into();
|
||||
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)
|
||||
.to_affine()
|
||||
.y_is_odd()
|
||||
.into();
|
||||
|
||||
let vs = verifying_share.to_element();
|
||||
if pubkey_is_odd != tweaked_pubkey_is_odd {
|
||||
vs = -vs;
|
||||
-vs
|
||||
} else {
|
||||
vs
|
||||
}
|
||||
vs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue