Skip to content

Commit

Permalink
[❄] Add binonce::SecretNonce
Browse files Browse the repository at this point in the history
Sometimes you don't want the keypair. When you just want to sign you
shouldn't be forced to recompute it.
  • Loading branch information
LLFourn committed Jan 14, 2025
1 parent c825a36 commit 1bd0892
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 48 deletions.
106 changes: 72 additions & 34 deletions schnorr_fun/src/binonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct Nonce<Z = NonZero>(pub [Point<Normal, Public, Z>; 2]);
impl<Z: ZeroChoice> Nonce<Z> {
/// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
///
/// If either pair of 33 bytes is `[0u8;32]` that point is interpreted as `Zero`.
/// If either pair of 33 bytes is `[0u8;33]` that point is interpreted as `Zero`.
pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
let R1 = Point::from_slice(&bytes[..33])?;
let R2 = Point::from_slice(&bytes[33..])?;
Expand All @@ -35,7 +35,7 @@ impl<Z: ZeroChoice> Nonce<Z> {

/// Serializes a public nonce as as 66 bytes (two 33-byte serialized points).
///
/// If either point is `Zero` it will be serialized as `[0u8;32]`.
/// If either point is `Zero` it will be serialized as `[0u8;33]`.
pub fn to_bytes(&self) -> [u8; 66] {
let mut bytes = [0u8; 66];
bytes[..33].copy_from_slice(self.0[0].to_bytes().as_ref());
Expand Down Expand Up @@ -72,7 +72,7 @@ impl Nonce<Zero> {
}

secp256kfun::impl_fromstr_deserialize! {
name => "public nonce pair",
name => "public binonce",
fn from_bytes<Z: ZeroChoice>(bytes: [u8;66]) -> Option<Nonce<Z>> {
Nonce::from_bytes(bytes)
}
Expand All @@ -86,51 +86,64 @@ secp256kfun::impl_display_serialize! {

/// A pair of secret nonces along with the public portion.
///
/// A nonce key pair can be created manually with [`from_secrets`]
/// A nonce key pair can be created manually with [`from_secret`]
///
/// [`from_secrets`]: Self::from_secrets
#[derive(Debug, Clone, PartialEq)]
pub struct NonceKeyPair {
/// The public nonce
pub public: Nonce<NonZero>,
/// The secret nonce
pub secret: [Scalar; 2],
pub secret: SecretNonce,
}

impl NonceKeyPair {
/// Load nonces from two secret scalars
pub fn from_secrets(secret: [Scalar; 2]) -> Self {
let [ref r1, ref r2] = secret;
let R1 = g!(r1 * G).normalize();
let R2 = g!(r2 * G).normalize();
NonceKeyPair {
public: Nonce([R1, R2]),
secret,
}
}
/// Deserializes a nonce key pair from 64-bytes (two 32-byte serialized scalars).
/// A pair of secret nonces.
///
/// ⚠ An attacker getting this allows them to extract your secret share from a signature share.
#[derive(Debug, Clone, PartialEq)]
pub struct SecretNonce(pub [Scalar; 2]);

impl SecretNonce {
/// Deserializes a secret binonce from 64-bytes (two 32-byte serialized scalars).
pub fn from_bytes(bytes: [u8; 64]) -> Option<Self> {
let r1 = Scalar::from_slice(&bytes[..32])?.non_zero()?;
let r2 = Scalar::from_slice(&bytes[32..])?.non_zero()?;
let R1 = g!(r1 * G).normalize();
let R2 = g!(r2 * G).normalize();
let pub_nonce = Nonce([R1, R2]);
Some(NonceKeyPair {
public: pub_nonce,
secret: [r1, r2],
})
Some(Self([r1, r2]))
}

/// Serializes a nonce key pair to 64-bytes (two 32-bytes serialized scalars).
/// Serializes a secret binonce to 64-bytes (two 32-bytes serialized scalars).
pub fn to_bytes(&self) -> [u8; 64] {
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(self.secret[0].to_bytes().as_ref());
bytes[32..].copy_from_slice(self.secret[1].to_bytes().as_ref());
bytes[..32].copy_from_slice(self.0[0].to_bytes().as_ref());
bytes[32..].copy_from_slice(self.0[1].to_bytes().as_ref());
bytes
}

/// Generate a nonce secret binonce from an rng
pub fn random(rng: &mut impl RngCore) -> Self {
Self([Scalar::random(rng), Scalar::random(rng)])
}

/// Convert a secret nonce into a key pair by computing the public nonce
pub fn into_keypair(self) -> NonceKeyPair {
NonceKeyPair::from_secret(self)
}
}

impl NonceKeyPair {
/// Load nonces from two secret scalars
pub fn from_secret(secret: SecretNonce) -> Self {
let [ref r1, ref r2] = secret.0;
let R1 = g!(r1 * G).normalize();
let R2 = g!(r2 * G).normalize();
NonceKeyPair {
public: Nonce([R1, R2]),
secret,
}
}

/// Get the secret portion of the nonce key pair (don't share this!)
pub fn secret(&self) -> &[Scalar; 2] {
pub fn secret(&self) -> &SecretNonce {
&self.secret
}

Expand All @@ -139,21 +152,46 @@ impl NonceKeyPair {
self.public
}

/// Generate a nonce keypair from an rng
/// Generate a random secret nonce and conver to a keypair
pub fn random(rng: &mut impl RngCore) -> Self {
Self::from_secrets([Scalar::random(rng), Scalar::random(rng)])
Self::from_secret(SecretNonce::random(rng))
}
}

impl AsRef<SecretNonce> for NonceKeyPair {
fn as_ref(&self) -> &SecretNonce {
self.secret()
}
}

impl AsRef<SecretNonce> for SecretNonce {
fn as_ref(&self) -> &SecretNonce {
self
}
}

secp256kfun::impl_fromstr_deserialize! {
name => "secret binonce",
fn from_bytes(bytes: [u8;64]) -> Option<SecretNonce> {
SecretNonce::from_bytes(bytes)
}
}

secp256kfun::impl_display_serialize! {
fn to_bytes(value: &SecretNonce) -> [u8;64] {
value.to_bytes()
}
}

secp256kfun::impl_fromstr_deserialize! {
name => "secret nonce pair",
name => "secret binonce",
fn from_bytes(bytes: [u8;64]) -> Option<NonceKeyPair> {
NonceKeyPair::from_bytes(bytes)
Some(NonceKeyPair::from_secret(SecretNonce::from_bytes(bytes)?))
}
}

secp256kfun::impl_display_serialize! {
fn to_bytes(nkp: &NonceKeyPair) -> [u8;64] {
nkp.to_bytes()
fn to_bytes(value: &NonceKeyPair) -> [u8;64] {
value.secret.to_bytes()
}
}
12 changes: 8 additions & 4 deletions schnorr_fun/src/frost/session.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use crate::{binonce, frost::PartyIndex, Signature};
use crate::{
binonce::{self, SecretNonce},
frost::PartyIndex,
Signature,
};
use alloc::collections::{BTreeMap, BTreeSet};
use secp256kfun::{poly, prelude::*};

use super::{NonceKeyPair, PairedSecretShare, SharedKey, SignatureShare, VerificationShare};
use super::{PairedSecretShare, SharedKey, SignatureShare, VerificationShare};
/// A FROST signing session used to *verify* signatures.
///
/// Created using [`coordinator_sign_session`].
Expand Down Expand Up @@ -214,7 +218,7 @@ impl PartySignSession {
pub fn sign(
&self,
secret_share: &PairedSecretShare<EvenY>,
secret_nonce: NonceKeyPair,
secret_nonce: impl AsRef<SecretNonce>,
) -> SignatureShare {
if self.public_key != secret_share.public_key() {
panic!("the share's shared key is not the same as the shared key of the session");
Expand All @@ -224,7 +228,7 @@ impl PartySignSession {
}
let secret_share = secret_share.secret_share();
let lambda = poly::eval_basis_poly_at_0(secret_share.index, self.parties.iter().cloned());
let [mut r1, mut r2] = secret_nonce.secret;
let [mut r1, mut r2] = secret_nonce.as_ref().0;
r1.conditional_negate(self.binonce_needs_negation);
r2.conditional_negate(self.binonce_needs_negation);

Expand Down
10 changes: 7 additions & 3 deletions schnorr_fun/src/musig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@
//!
//! [the excellent paper]: https://eprint.iacr.org/2020/1261.pdf
//! [secp256k1-zkp]: https://github.com/ElementsProject/secp256k1-zkp/pull/131
use crate::{adaptor::EncryptedSignature, binonce, Message, Schnorr, Signature};
use crate::{
adaptor::EncryptedSignature,
binonce::{self, SecretNonce},
Message, Schnorr, Signature,
};
use alloc::vec::Vec;
use secp256kfun::{
hash::{Hash32, HashAdd, Tag},
Expand Down Expand Up @@ -556,7 +560,7 @@ impl<H: Hash32, NG> MuSig<H, NG> {
session: &SignSession<T>,
my_index: usize,
keypair: &KeyPair,
local_secret_nonce: binonce::NonceKeyPair,
local_secret_nonce: impl AsRef<SecretNonce>,
) -> Scalar<Public, Zero> {
assert_eq!(
keypair.public_key(),
Expand All @@ -569,7 +573,7 @@ impl<H: Hash32, NG> MuSig<H, NG> {
let mut a = agg_key.coefs[my_index];

a.conditional_negate(agg_key.needs_negation);
let [mut r1, mut r2] = local_secret_nonce.secret;
let [mut r1, mut r2] = local_secret_nonce.as_ref().0;
r1.conditional_negate(session.nonce_needs_negation);
r2.conditional_negate(session.nonce_needs_negation);
s!(c * a * x_i + r1 + b * r2).public()
Expand Down
5 changes: 2 additions & 3 deletions schnorr_fun/tests/musig_sign_verify.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![cfg(feature = "serde")]
use schnorr_fun::{
binonce,
binonce::NonceKeyPair,
fun::{marker::*, serde, Point, Scalar},
musig, Message,
};
Expand All @@ -26,15 +25,15 @@ impl<T> Maybe<T> {

#[derive(Clone, Debug)]
struct SecNonce {
nonce: NonceKeyPair,
nonce: binonce::SecretNonce,
pk: Point,
}

impl SecNonce {
pub fn from_bytes(bytes: [u8; 97]) -> Option<Self> {
let mut nonce = [0u8; 64];
nonce.copy_from_slice(&bytes[..64]);
let nonce = binonce::NonceKeyPair::from_bytes(nonce)?;
let nonce = binonce::SecretNonce::from_bytes(nonce)?;
Some(SecNonce {
nonce,
pk: Point::from_slice(&bytes[64..])?,
Expand Down
7 changes: 3 additions & 4 deletions schnorr_fun/tests/musig_tweak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
use std::{rc::Rc, sync::Arc};

use schnorr_fun::{
binonce,
binonce::NonceKeyPair,
binonce::{self, NonceKeyPair},
fun::{marker::*, serde, Point, Scalar},
musig, Message,
};
Expand All @@ -28,15 +27,15 @@ impl<T> Maybe<T> {
}

struct SecNonce {
nonce: NonceKeyPair,
nonce: binonce::SecretNonce,
_pk: Point,
}

impl SecNonce {
pub fn from_bytes(bytes: [u8; 97]) -> Option<Self> {
let mut nonce = [0u8; 64];
nonce.copy_from_slice(&bytes[..64]);
let nonce = binonce::NonceKeyPair::from_bytes(nonce)?;
let nonce = binonce::SecretNonce::from_bytes(nonce)?;
Some(SecNonce {
nonce,
_pk: Point::from_slice(&bytes[64..])?,
Expand Down

0 comments on commit 1bd0892

Please sign in to comment.