Skip to content

Commit b94881d

Browse files
authored
[❄] Add ShareImage (#226)
- Remove public key from VerificationShare which was only used to do a sanity check. Instead I tried to improve documentation.
1 parent 95b326a commit b94881d

File tree

3 files changed

+83
-44
lines changed

3 files changed

+83
-44
lines changed

schnorr_fun/src/frost/session.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,21 @@ impl CoordinatorSignSession {
5555
///
5656
/// The `verification_share` is usually derived from either [`SharedKey::verification_share`] or
5757
/// [`PairedSecretShare::verification_share`].
58+
///
59+
/// **Important**: The verification share must come from the same `SharedKey` that this session
60+
/// was created for. If the key has been tweaked or modified since the session was created,
61+
/// the verification will fail. Each verification share is tied to a specific polynomial and
62+
/// cannot be reused across different keys or tweaked versions of the same key.
5863
pub fn verify_signature_share(
5964
&self,
60-
verification_share: VerificationShare<impl PointType>,
65+
verification_share: VerificationShare,
6166
signature_share: SignatureShare,
6267
) -> Result<(), SignatureShareInvalid> {
63-
let X = verification_share.share_image;
64-
let index = verification_share.index;
65-
66-
// We need to know the verification share was generated against the session's key for
67-
// further validity to have any meaning.
68-
if verification_share.public_key != self.public_key() {
69-
return Err(SignatureShareInvalid { index });
70-
}
68+
let X = verification_share.0.image;
69+
let index = verification_share.0.index;
7170

7271
let s = signature_share;
73-
let lambda =
74-
poly::eval_basis_poly_at_0(verification_share.index, self.nonces.keys().cloned());
72+
let lambda = poly::eval_basis_poly_at_0(index, self.nonces.keys().cloned());
7573
let c = &self.challenge;
7674
let b = &self.binding_coeff;
7775
let [R1, R2] = self

schnorr_fun/src/frost/share.rs

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::ShareIndex;
12
use secp256kfun::{poly, prelude::*};
23
/// A *[Shamir secret share]*.
34
///
@@ -87,8 +88,11 @@ impl SecretShare {
8788
}
8889

8990
/// Get the image of the secret share.
90-
pub fn share_image(&self) -> Point<NonNormal, Public, Zero> {
91-
g!(self.share * G)
91+
pub fn share_image(&self) -> ShareImage {
92+
ShareImage {
93+
index: self.index,
94+
image: g!(self.share * G).normalize(),
95+
}
9296
}
9397
}
9498

@@ -265,12 +269,12 @@ impl PairedSecretShare<Normal> {
265269

266270
impl PairedSecretShare<EvenY> {
267271
/// Get the verification for the inner secret share.
268-
pub fn verification_share(&self) -> VerificationShare<NonNormal> {
269-
VerificationShare {
272+
pub fn verification_share(&self) -> VerificationShare {
273+
VerificationShare(ShareImage {
270274
index: self.index(),
271-
share_image: self.secret_share.share_image(),
272-
public_key: self.public_key,
273-
}
275+
// we don't use SecretShare::share_image because it normalizes which is unecessary here
276+
image: g!(self.secret_share.share * G),
277+
})
274278
}
275279
}
276280

@@ -286,14 +290,7 @@ impl PairedSecretShare<EvenY> {
286290
///
287291
/// [`share_image`]: SecretShare::share_image
288292
#[derive(Clone, Copy, Debug, PartialEq)]
289-
pub struct VerificationShare<T: PointType> {
290-
/// The index of the share in the secret sharing
291-
pub index: ShareIndex,
292-
/// The image of the secret share
293-
pub share_image: Point<T, Public, Zero>,
294-
/// The public key that this is a share of
295-
pub public_key: Point<EvenY>,
296-
}
293+
pub struct VerificationShare(pub(crate) ShareImage<NonNormal>);
297294

298295
#[cfg(feature = "share_backup")]
299296
mod share_backup {
@@ -501,7 +498,45 @@ mod share_backup {
501498
#[cfg(feature = "share_backup")]
502499
pub use share_backup::BackupDecodeError;
503500

504-
use super::ShareIndex;
501+
/// The public image of a secret share, consisting of an index and the corresponding point.
502+
///
503+
/// A `ShareImage` represents the public information about a share: the index at
504+
/// which the polynomial was evaluated and the image of the secret share. This
505+
/// can be shared publicly and used to reconstruct the shared public key and the polynomial
506+
/// from a threshold number of share images using [`SharedKey::from_share_images`].
507+
///
508+
/// [`SharedKey::from_share_images`]: crate::frost::SharedKey::from_share_images
509+
#[derive(Clone, Copy, Debug)]
510+
#[cfg_attr(
511+
feature = "bincode",
512+
derive(bincode::Encode, bincode::Decode),
513+
bincode(
514+
encode_bounds = "Point<T, Public, Zero>: bincode::Encode",
515+
decode_bounds = "Point<T, Public, Zero>: bincode::Decode<__Context>",
516+
borrow_decode_bounds = "Point<T, Public, Zero>: bincode::BorrowDecode<'__de, __Context>"
517+
)
518+
)]
519+
#[cfg_attr(
520+
feature = "serde",
521+
derive(crate::fun::serde::Deserialize, crate::fun::serde::Serialize),
522+
serde(crate = "crate::fun::serde"),
523+
serde(bound(
524+
serialize = "Point<T, Public, Zero>: crate::fun::serde::Serialize",
525+
deserialize = "Point<T, Public, Zero>: crate::fun::serde::Deserialize<'de>"
526+
))
527+
)]
528+
pub struct ShareImage<T = Normal> {
529+
/// The index where the polynomial was evaluated
530+
pub index: ShareIndex,
531+
/// The image of the secret share (G * share_scalar)
532+
pub image: Point<T, Public, Zero>,
533+
}
534+
535+
impl<T: PointType> PartialEq for ShareImage<T> {
536+
fn eq(&self, other: &Self) -> bool {
537+
self.index == other.index && self.image == other.image
538+
}
539+
}
505540

506541
#[cfg(test)]
507542
mod test {

schnorr_fun/src/frost/shared_key.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{PairedSecretShare, SecretShare, ShareIndex, VerificationShare};
1+
use super::{PairedSecretShare, SecretShare, ShareImage, ShareIndex, VerificationShare};
22
use alloc::vec::Vec;
33
use core::{marker::PhantomData, ops::Deref};
44
use secp256kfun::{poly, prelude::*};
@@ -179,8 +179,11 @@ impl<T: PointType, Z: ZeroChoice> SharedKey<T, Z> {
179179
/// contains a share image).
180180
///
181181
/// [`verification_share`]: Self::verification_share
182-
pub fn share_image(&self, index: ShareIndex) -> Point<NonNormal, Public, Zero> {
183-
poly::point::eval(&self.point_polynomial, index)
182+
pub fn share_image(&self, index: ShareIndex) -> ShareImage {
183+
ShareImage {
184+
index,
185+
image: poly::point::eval(&self.point_polynomial, index).normalize(),
186+
}
184187
}
185188

186189
/// Checks if the polynomial coefficients contain a fingerprint by verifying that
@@ -268,7 +271,7 @@ impl SharedKey<Normal, Zero> {
268271
///
269272
/// If all the share images are correct and you have at least a threshold of them then you'll
270273
/// get the original shared key. If you put in a wrong share you won't get the right answer and
271-
/// there will be no error.
274+
/// there will be **no error**.
272275
///
273276
/// Note that a "share image" is not a concept that we really use in the core of this library
274277
/// but you can get one from a share with [`SecretShare::share_image`].
@@ -277,26 +280,29 @@ impl SharedKey<Normal, Zero> {
277280
///
278281
/// ⚠ You can't just take any points you want and pass them in here and hope it's secure.
279282
/// They need to be from a securely generated key.
280-
pub fn from_share_images(
281-
shares: &[(ShareIndex, Point<impl PointType, Public, impl ZeroChoice>)],
282-
) -> Self {
283-
let poly = poly::point::interpolate(shares);
283+
pub fn from_share_images(share_images: impl IntoIterator<Item = ShareImage>) -> Self {
284+
let shares: Vec<(ShareIndex, Point<Normal, Public, Zero>)> = share_images
285+
.into_iter()
286+
.map(|img| (img.index, img.image))
287+
.collect();
288+
let poly = poly::point::interpolate(&shares);
284289
let poly = poly::point::normalize(poly);
285290
SharedKey::from_inner(poly.collect())
286291
}
287292
}
288293

289294
impl SharedKey<EvenY> {
290-
/// The verification shares of each party in the key.
295+
/// Creates a verification share for a party at the given index.
291296
///
292-
/// The verification share is the image of their secret share.
293-
pub fn verification_share(&self, index: ShareIndex) -> VerificationShare<NonNormal> {
294-
let share_image = poly::point::eval(&self.point_polynomial, index);
295-
VerificationShare {
296-
index,
297-
share_image,
298-
public_key: self.public_key(),
299-
}
297+
/// The verification share is the image of the party's secret share evaluated from this key's polynomial.
298+
///
299+
/// **Important**: The returned `VerificationShare` can only be used to verify signatures
300+
/// against the specific `SharedKey` that created it. You must ensure you're verifying
301+
/// against the correct shared key. If the polynomial has been tweaked or modified,
302+
/// you'll need to get a new verification share from the modified key.
303+
pub fn verification_share(&self, index: ShareIndex) -> VerificationShare {
304+
let image = poly::point::eval(&self.point_polynomial, index);
305+
VerificationShare(ShareImage { index, image })
300306
}
301307
}
302308

0 commit comments

Comments
 (0)