Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frost-secp256k1-tr crate (BIP340/BIP341) #584

Closed
wants to merge 22 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b380fd5
add frost-secp256k1-tr crate (BIP340/BIP341)
zebra-lucky Nov 20, 2023
ab6b0d0
run cargo fmt on frost-secp256k1-tr
zebra-lucky Dec 25, 2023
8204166
fix use of tweaked public key
mimoo Dec 27, 2023
a307130
additional fixes for use of tweaked pubkey
zebra-lucky Jan 10, 2024
6d8be7c
give more consistent names to taproot functions
zebra-lucky Dec 26, 2023
20da59a
add DKG vector test for frost-secp256k1-tr
zebra-lucky Jan 11, 2024
00cdfe5
cargo fmt
conradoplg Feb 5, 2024
bdc8fb4
fix gencode-related issues
conradoplg Feb 6, 2024
a66b9a2
clippy fixes
conradoplg Feb 6, 2024
142556f
Refactor Ciphersuite taproot methods for universal applicability (#2)
conduition Feb 21, 2024
0ed163f
fix docstrings in frost-core/src/traits.rs
zebra-lucky Feb 22, 2024
c63a3ca
update frost-secp256-tr code to changes from 1.0.0
zebra-lucky Feb 24, 2024
e5b3f5d
encapsulate BIP341 tapscript commitment in new SigningTarget type
conduition Mar 2, 2024
155dfa6
add effective_key method to VerifyingKey
conduition Mar 2, 2024
1268f5c
Fix typo for VerifyingKey.effective_key
zebra-lucky Mar 5, 2024
1c085ba
remove debugging assert_eq from tests/vectors.rs
zebra-lucky Mar 6, 2024
c1b8663
fix reference to SigningTarget.message instead of cloned signing target
conduition Mar 16, 2024
8f52646
remove unneeded Into invocation
conduition Mar 16, 2024
5d2d683
fix reference to internal function
conduition Mar 16, 2024
20c2c98
add integration tests to cover taproot-tweaked signing
conduition Mar 16, 2024
15688ab
ensure taproot signatures always use even nonce points
conduition Apr 24, 2024
d580241
serialize taproot signatures as 64 bytes with x-only nonce
conduition Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
run cargo fmt on frost-secp256k1-tr
  • Loading branch information
zebra-lucky committed Feb 24, 2024
commit ab6b0d09d4e5067263864024f6ddd92f2f508da3
50 changes: 17 additions & 33 deletions frost-secp256k1-tr/src/lib.rs
Original file line number Diff line number Diff line change
@@ -10,13 +10,12 @@ use std::collections::BTreeMap;
use frost_rerandomized::RandomizedCiphersuite;
use k256::{
elliptic_curve::{
bigint::{U256},
bigint::U256,
group::prime::PrimeCurveAffine,
hash2curve::{hash_to_field, ExpandMsgXmd},
sec1::{FromEncodedPoint, ToEncodedPoint},
Field as FFField, PrimeField,
ScalarPrimitive,
point::{AffineCoordinates, DecompactPoint},
sec1::{FromEncodedPoint, ToEncodedPoint},
Field as FFField, PrimeField, ScalarPrimitive,
},
AffinePoint, ProjectivePoint, Scalar,
};
@@ -29,8 +28,9 @@ use frost_core as frost;
mod tests;

// Re-exports in our public API
pub use frost_core::{serde, Ciphersuite, Field, FieldError, Group, GroupError,
Element, Challenge};
pub use frost_core::{
serde, Challenge, Ciphersuite, Element, Field, FieldError, Group, GroupError,
};

pub use rand_core;

@@ -190,8 +190,7 @@ pub struct Secp256K1Sha256;

/// Digest the hasher to a Scalar
pub fn hasher_to_scalar(hasher: Sha256) -> Scalar {
let sp = ScalarPrimitive::new(U256::from_be_slice(&hasher.finalize()))
.unwrap();
let sp = ScalarPrimitive::new(U256::from_be_slice(&hasher.finalize())).unwrap();
Scalar::from(&sp)
}

@@ -209,7 +208,7 @@ pub fn tagged_hash(tag: &str) -> Sha256 {
/// Create a BIP341 compliant taproot tweak
pub fn tweak(
public_key: &<<Secp256K1Sha256 as Ciphersuite>::Group as Group>::Element,
merkle_root: &[u8]
merkle_root: &[u8],
) -> Scalar {
let mut hasher = tagged_hash("TapTweak");
hasher.update(public_key.to_affine().x());
@@ -305,8 +304,7 @@ 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, msg: &[u8]) -> Challenge<S> {
let mut preimage = vec![];
let tweaked_public_key = tweaked_public_key(&verifying_key.to_element(), &[]);
Copy link
Contributor

@0xBEEFCAF3 0xBEEFCAF3 Feb 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the merkel root be empty here?
i.e does this code assume that we only spend from the default key path? Not a tapscript path?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly my understanding is wrong, but in BIP341, in
code example for taproot_sign_key I've seen next:

    if script_tree is None:
        h = bytes()  # empty bytes for merkle root

In the case when script path is not used (used only key path),
seems empty bytes for merkle root is correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this code only works with empty commitment, and it works fine for us (https://github.com/sigma0-xyz/zkbitcoin). I think it's a good first step, but it'd be nice to expose that feature later

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I'll look to code in zkbitcoin in a few days.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case when script path is not used (used only key path),

Ah yes you're correct. In the case of a script path spend (non-frost spend) the spender can tweak the verifying key outside the context of this library.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I'll look to code in zkbitcoin in a few days.

Only looking in the current day...

preimage.extend_from_slice(&R.to_affine().x());
@@ -325,8 +323,7 @@ impl Ciphersuite for Secp256K1Sha256 {
z: <<Self::Group as Group>::Field as Field>::Scalar,
challenge: &Challenge<S>,
verifying_key: &Element<S>,
) -> <<Self::Group as Group>::Field as Field>::Scalar
{
) -> <<Self::Group as Group>::Field as Field>::Scalar {
let t = tweak(&verifying_key, &[]);
z + t * challenge.clone().to_scalar()
}
@@ -339,8 +336,7 @@ impl Ciphersuite for Secp256K1Sha256 {
lambda_i: <<Self::Group as Group>::Field as Field>::Scalar,
key_package: &frost::keys::KeyPackage<S>,
challenge: Challenge<S>,
) -> round2::SignatureShare
{
) -> round2::SignatureShare {
let mut sn = signer_nonces.clone();
if group_commitment.y_is_odd() {
sn.negate_nonces();
@@ -351,13 +347,7 @@ impl Ciphersuite for Secp256K1Sha256 {
kp.negate_signing_share();
}

frost::round2::compute_signature_share(
&sn,
binding_factor,
lambda_i,
&kp,
challenge,
)
frost::round2::compute_signature_share(&sn, binding_factor, lambda_i, &kp, challenge)
}

/// calculate tweaked public key
@@ -368,27 +358,23 @@ impl Ciphersuite for Secp256K1Sha256 {
}

/// calculate tweaked R
fn tweaked_R(
R: &<Self::Group as Group>::Element,
) -> <Self::Group as Group>::Element {
fn tweaked_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(
secret: <<Self::Group as Group>::Field as Field>::Scalar,
public: &Element<Self>,
) -> <<Self::Group as Group>::Field as Field>::Scalar
{
) -> <<Self::Group as Group>::Field as Field>::Scalar {
tweaked_secret_key(secret, &public, &[])
}

/// tweaked nonce
fn tweaked_nonce(
nonce: <<Self::Group as Group>::Field as Field>::Scalar,
R: &Element<Self>,
) -> <<Self::Group as Group>::Field as Field>::Scalar
{
) -> <<Self::Group as Group>::Field as Field>::Scalar {
if R.to_affine().y_is_odd().into() {
-nonce
} else {
@@ -399,8 +385,7 @@ impl Ciphersuite for Secp256K1Sha256 {
fn tweaked_group_commitment_share(
group_commitment_share: &Element<Self>,
group_commitment: &Element<Self>,
) -> Element<Self>
{
) -> Element<Self> {
if group_commitment.to_affine().y_is_odd().into() {
-group_commitment_share
} else {
@@ -411,8 +396,7 @@ impl Ciphersuite for Secp256K1Sha256 {
fn tweaked_verifying_share(
verifying_share: &<Self::Group as Group>::Element,
verifying_key: &<Self::Group as Group>::Element,
) -> <Self::Group as Group>::Element
{
) -> <Self::Group as Group>::Element {
let mut vs = verifying_share.clone();
if verifying_key.to_affine().y_is_odd().into() {
vs = -vs;