Skip to content

Commit

Permalink
support serializing SigningNonces (#595)
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg authored Jan 16, 2024
1 parent 1168154 commit 9921b12
Show file tree
Hide file tree
Showing 24 changed files with 265 additions and 34 deletions.
82 changes: 68 additions & 14 deletions frost-core/src/round1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ use crate::{
};

#[cfg(feature = "serde")]
use crate::serialization::ElementSerialization;
use crate::serialization::{ElementSerialization, ScalarSerialization};

use super::{keys::SigningShare, Identifier};

/// A scalar that is a signing nonce.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))]
#[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))]
pub struct Nonce<C: Ciphersuite>(pub(super) Scalar<C>);

impl<C> Nonce<C>
Expand Down Expand Up @@ -107,6 +111,28 @@ where
}
}

#[cfg(feature = "serde")]
impl<C> TryFrom<ScalarSerialization<C>> for Nonce<C>
where
C: Ciphersuite,
{
type Error = Error<C>;

fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> {
Self::deserialize(value.0)
}
}

#[cfg(feature = "serde")]
impl<C> From<Nonce<C>> for ScalarSerialization<C>
where
C: Ciphersuite,
{
fn from(value: Nonce<C>) -> Self {
Self(value.serialize())
}
}

/// A group element that is a commitment to a signing nonce share.
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -206,8 +232,14 @@ where
/// 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.
#[derive(Clone, Zeroize)]
#[derive(Clone, Zeroize, PartialEq, Eq, Getters)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SigningNonces<C: Ciphersuite> {
/// Serialization header
#[getter(skip)]
pub(crate) header: Header<C>,
/// The hiding [`Nonce`].
pub(crate) hiding: Nonce<C>,
/// The binding [`Nonce`].
Expand Down Expand Up @@ -238,30 +270,52 @@ where
Self::from_nonces(hiding, binding)
}

/// Generates a new [`SigningNonces`] from a pair of [`Nonce`]. This is
/// useful internally since [`SigningNonces`] precompute the respective
/// commitments.
#[cfg_attr(test, visibility::make(pub))]
pub(crate) fn from_nonces(hiding: Nonce<C>, binding: Nonce<C>) -> Self {
/// Generates a new [`SigningNonces`] from a pair of [`Nonce`].
///
/// # Security
///
/// SigningNonces MUST NOT be repeated in different FROST signings.
/// Thus, if you're using this method (because e.g. you're writing it
/// to disk between rounds), be careful so that does not happen.
pub fn from_nonces(hiding: Nonce<C>, binding: Nonce<C>) -> Self {
let hiding_commitment = (&hiding).into();
let binding_commitment = (&binding).into();
let commitments = SigningCommitments::new(hiding_commitment, binding_commitment);

Self {
header: Header::default(),
hiding,
binding,
commitments,
}
}
}

impl<C> Debug for SigningNonces<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SigningNonces")
.field("hiding", &"<redacted>")
.field("binding", &"<redacted>")
.finish()
}
}

/// Gets the hiding [`Nonce`]
pub fn hiding(&self) -> &Nonce<C> {
&self.hiding
#[cfg(feature = "serialization")]
impl<C> SigningNonces<C>
where
C: Ciphersuite,
{
/// Serialize the struct into a Vec.
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}

/// Gets the binding [`Nonce`]
pub fn binding(&self) -> &Nonce<C> {
&self.binding
/// Deserialize the struct from a slice of bytes.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}

Expand Down
14 changes: 12 additions & 2 deletions frost-ed25519/tests/helpers/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
use std::collections::BTreeMap;

use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_core::{round1::Nonce, Ciphersuite, Element, Group, Scalar};
use frost_ed25519::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round1::{NonceCommitment, SigningCommitments, SigningNonces},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
Expand All @@ -32,6 +32,16 @@ fn scalar1() -> Scalar<C> {
.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).unwrap();
let binding_nonce = Nonce::deserialize(serialized_scalar2).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());
Expand Down
12 changes: 11 additions & 1 deletion frost-ed25519/tests/recreation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use frost_ed25519::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round1::{SigningCommitments, SigningNonces},
round2::SignatureShare,
SigningPackage,
};
Expand All @@ -15,6 +15,16 @@ 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() {
Expand Down
10 changes: 9 additions & 1 deletion frost-ed25519/tests/serialization_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ use frost_ed25519::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: frost-ed25519/tests/serialization_tests.rs
expression: "hex::encode(&bytes)"
---
00b169f0da498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a00b169f0daa57766449a934461866051263c8785663857640e5a32d702f21e085bc31a0283a57766449a934461866051263c8785663857640e5a32d702f21e085bc31a0283
4 changes: 4 additions & 0 deletions frost-ed448/tests/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +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;
14 changes: 12 additions & 2 deletions frost-ed448/tests/helpers/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
use std::collections::BTreeMap;

use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_core::{round1::Nonce, Ciphersuite, Element, Group, Scalar};
use frost_ed448::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round1::{NonceCommitment, SigningCommitments, SigningNonces},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
Expand All @@ -32,6 +32,16 @@ fn scalar1() -> Scalar<C> {
.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).unwrap();
let binding_nonce = Nonce::deserialize(serialized_scalar2).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());
Expand Down
12 changes: 11 additions & 1 deletion frost-ed448/tests/recreation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use frost_ed448::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round1::{SigningCommitments, SigningNonces},
round2::SignatureShare,
SigningPackage,
};
Expand All @@ -15,6 +15,16 @@ 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() {
Expand Down
10 changes: 9 additions & 1 deletion frost-ed448/tests/serialization_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ use frost_ed448::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: frost-ed448/tests/serialization_tests.rs
expression: "hex::encode(&bytes)"
---
005a064cfd4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a004d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00005a064cfd0e04abea2bff79d5b6223b84b0a9323e0ef8572c7d0afd1fd5e4535ae6823e352ee239ad5f5981eb0c23292fb88bc55fe969957103c0c002800e04abea2bff79d5b6223b84b0a9323e0ef8572c7d0afd1fd5e4535ae6823e352ee239ad5f5981eb0c23292fb88bc55fe969957103c0c00280
4 changes: 4 additions & 0 deletions frost-p256/tests/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +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;
14 changes: 12 additions & 2 deletions frost-p256/tests/helpers/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
use std::collections::BTreeMap;

use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_core::{round1::Nonce, Ciphersuite, Element, Group, Scalar};
use frost_p256::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round1::{NonceCommitment, SigningCommitments, SigningNonces},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
Expand All @@ -32,6 +32,16 @@ fn scalar1() -> Scalar<C> {
.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).unwrap();
let binding_nonce = Nonce::deserialize(serialized_scalar2).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());
Expand Down
12 changes: 11 additions & 1 deletion frost-p256/tests/recreation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use frost_p256::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round1::{SigningCommitments, SigningNonces},
round2::SignatureShare,
SigningPackage,
};
Expand All @@ -15,6 +15,16 @@ 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() {
Expand Down
Loading

0 comments on commit 9921b12

Please sign in to comment.