Skip to main content

threshold_pairing/
lib.rs

1//! A pairing-based threshold cryptosystem for collaborative decryption and signatures.
2//!
3//! This crate provides cryptographic primitives for threshold signatures and threshold
4//! encryption based on BLS12-381 elliptic curve pairings. It enables `t+1` of `n` participants
5//! to collaboratively sign messages or decrypt ciphertexts, where `t` is the threshold.
6//!
7//! # Features
8//!
9//! - **Threshold Signatures**: Any `t+1` participants can produce a valid signature
10//! - **Threshold Encryption**: Messages can only be decrypted with `t+1` decryption shares
11//! - **Distributed Key Generation**: Tools for generating keys without a trusted dealer
12//! - **RFC 9380 Compliant**: Uses standard hash-to-curve algorithms
13//!
14//! # Quick Start
15//!
16//! ## Simple Signatures
17//!
18//! ```
19//! use threshold_pairing::SecretKey;
20//!
21//! let sk = SecretKey::random();
22//! let pk = sk.public_key();
23//!
24//! let message = b"Hello, world!";
25//! let signature = sk.sign(message);
26//!
27//! assert!(pk.verify(&signature, message));
28//! ```
29//!
30//! ## Threshold Signatures
31//!
32//! ```
33//! use std::collections::BTreeMap;
34//! use threshold_pairing::SecretKeySet;
35//!
36//! // Create a key set with threshold 2 (requires 3 shares to sign)
37//! let mut rng = rand::thread_rng();
38//! let sk_set = SecretKeySet::random(2, &mut rng);
39//! let pk_set = sk_set.public_keys();
40//!
41//! let message = b"Threshold signed message";
42//!
43//! // Collect 3 signature shares
44//! let shares: BTreeMap<_, _> = (0..3usize)
45//!     .map(|i| (i, sk_set.secret_key_share(i).expect("valid index").sign(message)))
46//!     .collect();
47//!
48//! // Combine shares into a full signature
49//! let signature = pk_set.combine_signatures(&shares).expect("not enough shares");
50//! assert!(pk_set.public_key().verify(&signature, message));
51//! ```
52//!
53//! # Modules
54//!
55//! - [`poly`]: Polynomial and commitment types for distributed key generation
56//! - [`error`]: Error types for cryptographic operations
57//! - [`serde_impl`]: Serialization helpers, including [`serde_impl::SerdeSecret`] for secret keys
58//!
59//! # Feature Flags
60//!
61//! - `serde` (enabled by default): Enables `Serialize` and `Deserialize` impls for all public types
62//! - `bincode`: Enables bincode serialization support (requires `serde`)
63//! - `serialization`: Convenience feature that enables both `serde` and `bincode`
64//! - `expose-secret`: Enables `reveal()` methods on secret types for debugging (dev/debug only, never use in production)
65//!
66//! # Security
67//!
68//! This crate follows cryptographic best practices:
69//! - Secret keys are zeroized on drop
70//! - Constant-time comparison for secret key equality
71//! - RFC 9380 hash-to-curve for BLS signatures
72//! - Domain separation between signature and encryption schemes
73//!
74//! ## Caller responsibility for returned secret material
75//!
76//! Several methods return values that may contain sensitive material — for example,
77//! [`SecretKey::decrypt`] returns a `Vec<u8>` plaintext, and [`SecretKeySet::secret_key_share`]
78//! returns a [`SecretKeyShare`]. This crate zeroizes its own internal state on drop, but it
79//! **cannot control what the caller does with returned values**.
80//!
81//! If you need the returned material to be erased from memory after use, wrap it in
82//! [`zeroize::Zeroizing`] yourself:
83//!
84//! ```
85//! use threshold_pairing::SecretKey;
86//! use zeroize::Zeroizing;
87//!
88//! let sk = SecretKey::random();
89//! let pk = sk.public_key();
90//! let ciphertext = pk.encrypt(b"secret");
91//!
92//! // Wrap the plaintext so it is zeroized when dropped.
93//! let plaintext = Zeroizing::new(sk.decrypt(&ciphertext).unwrap());
94//! // `plaintext` is zeroed when it goes out of scope.
95//! ```
96
97// Clippy warns that it's dangerous to derive `PartialEq` and explicitly implement `Hash`, but the
98// `pairing::bls12_381` types don't implement `Hash`, so we can't derive it.
99#![allow(clippy::derived_hash_with_manual_eq)]
100#![warn(missing_docs)]
101
102use core::ops::{AddAssign, Mul, MulAssign, SubAssign};
103
104pub use bls12_381;
105use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve};
106use bls12_381::{G2Projective, Scalar};
107pub use ff;
108use group::prime::PrimeCurve;
109use rand::rngs::OsRng;
110
111mod cmp_pairing;
112mod into_fr;
113
114pub mod error;
115pub mod poly;
116
117#[cfg(feature = "serde")]
118pub mod serde_impl;
119
120use std::borrow::Borrow;
121use std::cmp::Ordering;
122use std::convert::TryFrom;
123use std::fmt;
124use std::hash::{Hash, Hasher};
125use std::vec::Vec;
126
127use ff::Field;
128use group::{Curve, Group};
129use hex_fmt::HexFmt;
130use rand::distributions::{Distribution, Standard};
131use rand::Rng;
132use rand_chacha::rand_core::{RngCore, SeedableRng};
133use rand_chacha::ChaChaRng;
134#[cfg(feature = "serde")]
135use serde::{Deserialize, Serialize};
136use sha2::Sha256;
137use subtle::ConstantTimeEq;
138use zeroize::{Zeroize, Zeroizing};
139
140use crate::cmp_pairing::cmp_projective;
141use crate::error::{Error, FromBytesError, FromBytesResult, Result};
142use crate::poly::{Commitment, Poly};
143
144pub use crate::into_fr::{IntoScalar, TryIntoScalarSafe};
145
146mod util;
147use util::sha3_256;
148
149pub use bls12_381::{pairing, G1Affine, G1Projective, G2Affine};
150
151/// The size of a key's representation in bytes.
152pub const PK_SIZE: usize = 48;
153
154/// The size of a signature's representation in bytes.
155pub const SIG_SIZE: usize = 96;
156
157/// A public key.
158///
159/// Public keys are used to verify signatures and encrypt messages.
160///
161/// # Example
162///
163/// ```
164/// use threshold_pairing::SecretKey;
165///
166/// let sk = SecretKey::random();
167/// let pk = sk.public_key();
168///
169/// // Verify a signature
170/// let sig = sk.sign(b"message");
171/// assert!(pk.verify(&sig, b"message"));
172///
173/// // Encrypt a message
174/// let ciphertext = pk.encrypt(b"secret message");
175/// let decrypted = sk.decrypt(&ciphertext).unwrap();
176/// assert_eq!(b"secret message".to_vec(), decrypted);
177/// ```
178#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
179#[derive(Copy, Clone, PartialEq, Eq)]
180pub struct PublicKey(
181    #[cfg_attr(feature = "serde", serde(with = "serde_impl::projective"))] G1Projective,
182);
183
184impl Hash for PublicKey {
185    fn hash<H: Hasher>(&self, state: &mut H) {
186        self.0.to_affine().to_compressed().as_ref().hash(state);
187    }
188}
189
190impl fmt::Debug for PublicKey {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        let uncomp = self.0.to_affine().to_uncompressed();
193        write!(f, "PublicKey({:0.10})", HexFmt(uncomp))
194    }
195}
196
197impl PartialOrd for PublicKey {
198    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
199        Some(self.cmp(other))
200    }
201}
202
203impl Ord for PublicKey {
204    fn cmp(&self, other: &Self) -> Ordering {
205        cmp_projective(&self.0, &other.0)
206    }
207}
208
209impl PublicKey {
210    /// Returns `true` if the signature matches the element of `G2`.
211    #[must_use = "verification result must be checked"]
212    pub fn verify_g2<H: Into<G2Affine>>(&self, sig: &Signature, hash: H) -> bool {
213        pairing(&self.0.into(), &hash.into()) == pairing(&G1Affine::generator(), &sig.0.into())
214    }
215
216    /// Returns `true` if the signature matches the message.
217    ///
218    /// This is equivalent to [`verify_g2`](Self::verify_g2)`(sig, `[`hash_g2`]`(msg))`.
219    #[must_use = "verification result must be checked"]
220    pub fn verify<M: AsRef<[u8]>>(&self, sig: &Signature, msg: M) -> bool {
221        self.verify_g2(sig, hash_g2(msg))
222    }
223
224    /// Encrypts the message using the OS random number generator.
225    ///
226    /// Uses the `OsRng` by default. To pass in a custom random number generator, use
227    /// `encrypt_with_rng()`.
228    pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext {
229        self.encrypt_with_rng(&mut OsRng, msg)
230    }
231
232    /// Encrypts the message.
233    pub fn encrypt_with_rng<R: RngCore, M: AsRef<[u8]>>(&self, rng: &mut R, msg: M) -> Ciphertext {
234        let mut r: Scalar = Scalar::random(rng);
235        let u = G1Affine::generator().mul(r);
236        let v: Vec<u8> = {
237            let g = self.0.to_affine().mul(r);
238            xor_with_hash(g, msg.as_ref())
239        };
240        let w = hash_g1_g2(u, &v).to_affine().mul(r);
241        r.zeroize();
242        Ciphertext(u, v, w)
243    }
244
245    /// Returns the key with the given representation, if valid.
246    ///
247    /// The `from_compressed` method performs full validation including subgroup
248    /// checks to prevent small subgroup attacks.
249    ///
250    /// # Errors
251    ///
252    /// Returns [`FromBytesError::Invalid`] if:
253    /// - The bytes do not represent a valid compressed G1 point
254    /// - The point is not on the curve
255    /// - The point is not in the correct subgroup
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// use threshold_pairing::{PublicKey, SecretKey, PK_SIZE};
261    ///
262    /// let sk = SecretKey::random();
263    /// let pk = sk.public_key();
264    ///
265    /// // Round-trip through bytes
266    /// let bytes: [u8; PK_SIZE] = pk.to_bytes();
267    /// let pk2 = PublicKey::from_bytes(&bytes).expect("valid bytes");
268    /// assert_eq!(pk, pk2);
269    /// ```
270    pub fn from_bytes<B: Borrow<[u8; PK_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
271        let affine: Option<G1Affine> = G1Affine::from_compressed(bytes.borrow()).into();
272        let affine = affine.ok_or(FromBytesError::Invalid)?;
273        Ok(PublicKey(affine.into()))
274    }
275
276    /// Returns a byte string representation of the public key.
277    pub fn to_bytes(&self) -> [u8; PK_SIZE] {
278        let mut bytes = [0u8; PK_SIZE];
279        bytes.copy_from_slice(self.0.to_affine().to_compressed().as_ref());
280        bytes
281    }
282}
283
284impl TryFrom<&[u8; PK_SIZE]> for PublicKey {
285    type Error = FromBytesError;
286
287    fn try_from(bytes: &[u8; PK_SIZE]) -> FromBytesResult<Self> {
288        Self::from_bytes(bytes)
289    }
290}
291
292impl TryFrom<[u8; PK_SIZE]> for PublicKey {
293    type Error = FromBytesError;
294
295    fn try_from(bytes: [u8; PK_SIZE]) -> FromBytesResult<Self> {
296        Self::from_bytes(bytes)
297    }
298}
299
300impl fmt::LowerHex for PublicKey {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        for byte in self.to_bytes() {
303            write!(f, "{:02x}", byte)?;
304        }
305        Ok(())
306    }
307}
308
309impl fmt::UpperHex for PublicKey {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        for byte in self.to_bytes() {
312            write!(f, "{:02X}", byte)?;
313        }
314        Ok(())
315    }
316}
317
318impl fmt::Display for PublicKey {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        write!(f, "{:x}", self)
321    }
322}
323
324/// A public key share.
325#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
326#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
327pub struct PublicKeyShare(PublicKey);
328
329impl fmt::Debug for PublicKeyShare {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        let uncomp = (self.0).0.to_affine().to_uncompressed();
332        write!(f, "PublicKeyShare({:0.10})", HexFmt(uncomp))
333    }
334}
335
336impl PublicKeyShare {
337    /// Returns `true` if the signature matches the element of `G2`.
338    #[must_use = "verification result must be checked"]
339    pub fn verify_g2<H: Into<G2Affine>>(&self, sig: &SignatureShare, hash: H) -> bool {
340        self.0.verify_g2(&sig.0, hash)
341    }
342
343    /// Returns `true` if the signature matches the message.
344    ///
345    /// This is equivalent to [`verify_g2`](Self::verify_g2)`(sig, `[`hash_g2`]`(msg))`.
346    #[must_use = "verification result must be checked"]
347    pub fn verify<M: AsRef<[u8]>>(&self, sig: &SignatureShare, msg: M) -> bool {
348        self.verify_g2(sig, hash_g2(msg))
349    }
350
351    /// Returns `true` if the decryption share matches the ciphertext.
352    #[must_use = "verification result must be checked"]
353    pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool {
354        let Ciphertext(ref u, ref v, ref w) = *ct;
355        let hash = hash_g1_g2(*u, v);
356        pairing(&share.0.into(), &hash.into()) == pairing(&(self.0).0.into(), &w.into())
357    }
358
359    /// Returns the key share with the given representation, if valid.
360    pub fn from_bytes<B: Borrow<[u8; PK_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
361        Ok(PublicKeyShare(PublicKey::from_bytes(bytes)?))
362    }
363
364    /// Returns a byte string representation of the public key share.
365    pub fn to_bytes(&self) -> [u8; PK_SIZE] {
366        self.0.to_bytes()
367    }
368}
369
370impl TryFrom<&[u8; PK_SIZE]> for PublicKeyShare {
371    type Error = FromBytesError;
372
373    fn try_from(bytes: &[u8; PK_SIZE]) -> FromBytesResult<Self> {
374        Self::from_bytes(bytes)
375    }
376}
377
378impl TryFrom<[u8; PK_SIZE]> for PublicKeyShare {
379    type Error = FromBytesError;
380
381    fn try_from(bytes: [u8; PK_SIZE]) -> FromBytesResult<Self> {
382        Self::from_bytes(bytes)
383    }
384}
385
386impl fmt::LowerHex for PublicKeyShare {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        fmt::LowerHex::fmt(&self.0, f)
389    }
390}
391
392impl fmt::UpperHex for PublicKeyShare {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        fmt::UpperHex::fmt(&self.0, f)
395    }
396}
397
398impl fmt::Display for PublicKeyShare {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        fmt::Display::fmt(&self.0, f)
401    }
402}
403
404/// A BLS signature.
405///
406/// Signatures are created by signing a message with a [`SecretKey`] and can be verified
407/// using the corresponding [`PublicKey`].
408///
409/// # Example
410///
411/// ```
412/// use threshold_pairing::{SecretKey, Signature, SIG_SIZE};
413///
414/// let sk = SecretKey::random();
415/// let pk = sk.public_key();
416///
417/// let sig = sk.sign(b"message");
418/// assert!(pk.verify(&sig, b"message"));
419///
420/// // Serialize and deserialize
421/// let bytes: [u8; SIG_SIZE] = sig.to_bytes();
422/// let sig2 = Signature::from_bytes(&bytes).unwrap();
423/// assert_eq!(sig, sig2);
424/// ```
425// Note: Random signatures can be generated for testing.
426#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
427#[derive(Clone, PartialEq, Eq)]
428pub struct Signature(
429    #[cfg_attr(feature = "serde", serde(with = "serde_impl::projective"))] G2Projective,
430);
431
432impl PartialOrd for Signature {
433    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
434        Some(self.cmp(other))
435    }
436}
437
438impl Ord for Signature {
439    fn cmp(&self, other: &Self) -> Ordering {
440        cmp_projective(&self.0, &other.0)
441    }
442}
443
444impl Distribution<Signature> for Standard {
445    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Signature {
446        Signature(G2Projective::random(rng))
447    }
448}
449
450impl fmt::Debug for Signature {
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        let uncomp = self.0.to_affine().to_uncompressed();
453        write!(f, "Signature({:0.10})", HexFmt(uncomp))
454    }
455}
456
457impl Hash for Signature {
458    fn hash<H: Hasher>(&self, state: &mut H) {
459        self.0.to_affine().to_compressed().as_ref().hash(state);
460    }
461}
462
463impl Signature {
464    /// Returns `true` if the signature contains an odd number of ones.
465    pub fn parity(&self) -> bool {
466        let uncomp = self.0.to_affine().to_uncompressed();
467        let xor_bytes: u8 = uncomp.as_ref().iter().fold(0, |result, byte| result ^ byte);
468        xor_bytes.count_ones() % 2 != 0
469    }
470
471    /// Returns the signature with the given representation, if valid.
472    ///
473    /// The `from_compressed` method performs full validation including subgroup
474    /// checks to prevent small subgroup attacks.
475    ///
476    /// # Errors
477    ///
478    /// Returns [`FromBytesError::Invalid`] if:
479    /// - The bytes do not represent a valid compressed G2 point
480    /// - The point is not on the curve
481    /// - The point is not in the correct subgroup
482    ///
483    /// # Example
484    ///
485    /// ```
486    /// use threshold_pairing::{SecretKey, Signature, SIG_SIZE};
487    ///
488    /// let sk = SecretKey::random();
489    /// let sig = sk.sign(b"message");
490    ///
491    /// // Round-trip through bytes
492    /// let bytes: [u8; SIG_SIZE] = sig.to_bytes();
493    /// let sig2 = Signature::from_bytes(&bytes).expect("valid bytes");
494    /// assert_eq!(sig, sig2);
495    /// ```
496    pub fn from_bytes<B: Borrow<[u8; SIG_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
497        let affine: Option<G2Affine> = G2Affine::from_compressed(bytes.borrow()).into();
498        let affine = affine.ok_or(FromBytesError::Invalid)?;
499        Ok(Signature(affine.into()))
500    }
501
502    /// Returns a byte string representation of the signature.
503    pub fn to_bytes(&self) -> [u8; SIG_SIZE] {
504        let mut bytes = [0u8; SIG_SIZE];
505        bytes.copy_from_slice(self.0.to_affine().to_compressed().as_ref());
506        bytes
507    }
508}
509
510impl TryFrom<&[u8; SIG_SIZE]> for Signature {
511    type Error = FromBytesError;
512
513    fn try_from(bytes: &[u8; SIG_SIZE]) -> FromBytesResult<Self> {
514        Self::from_bytes(bytes)
515    }
516}
517
518impl TryFrom<[u8; SIG_SIZE]> for Signature {
519    type Error = FromBytesError;
520
521    fn try_from(bytes: [u8; SIG_SIZE]) -> FromBytesResult<Self> {
522        Self::from_bytes(bytes)
523    }
524}
525
526impl fmt::LowerHex for Signature {
527    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528        for byte in self.to_bytes() {
529            write!(f, "{:02x}", byte)?;
530        }
531        Ok(())
532    }
533}
534
535impl fmt::UpperHex for Signature {
536    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537        for byte in self.to_bytes() {
538            write!(f, "{:02X}", byte)?;
539        }
540        Ok(())
541    }
542}
543
544impl fmt::Display for Signature {
545    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546        write!(f, "{:x}", self)
547    }
548}
549
550/// A signature share.
551// Note: Random signature shares can be generated for testing.
552#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
553#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
554pub struct SignatureShare(Signature);
555
556impl SignatureShare {
557    /// Returns a reference to the inner signature.
558    pub fn inner(&self) -> &Signature {
559        &self.0
560    }
561}
562
563impl Distribution<SignatureShare> for Standard {
564    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SignatureShare {
565        SignatureShare(rng.gen())
566    }
567}
568
569impl fmt::Debug for SignatureShare {
570    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571        let uncomp = (self.0).0.to_affine().to_uncompressed();
572        write!(f, "SignatureShare({:0.10})", HexFmt(uncomp))
573    }
574}
575
576impl SignatureShare {
577    /// Returns the signature share with the given representation, if valid.
578    pub fn from_bytes<B: Borrow<[u8; SIG_SIZE]>>(bytes: B) -> FromBytesResult<Self> {
579        Ok(SignatureShare(Signature::from_bytes(bytes)?))
580    }
581
582    /// Returns a byte string representation of the signature share.
583    pub fn to_bytes(&self) -> [u8; SIG_SIZE] {
584        self.0.to_bytes()
585    }
586}
587
588impl TryFrom<&[u8; SIG_SIZE]> for SignatureShare {
589    type Error = FromBytesError;
590
591    fn try_from(bytes: &[u8; SIG_SIZE]) -> FromBytesResult<Self> {
592        Self::from_bytes(bytes)
593    }
594}
595
596impl TryFrom<[u8; SIG_SIZE]> for SignatureShare {
597    type Error = FromBytesError;
598
599    fn try_from(bytes: [u8; SIG_SIZE]) -> FromBytesResult<Self> {
600        Self::from_bytes(bytes)
601    }
602}
603
604impl fmt::LowerHex for SignatureShare {
605    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606        fmt::LowerHex::fmt(&self.0, f)
607    }
608}
609
610impl fmt::UpperHex for SignatureShare {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        fmt::UpperHex::fmt(&self.0, f)
613    }
614}
615
616impl fmt::Display for SignatureShare {
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618        fmt::Display::fmt(&self.0, f)
619    }
620}
621
622/// A secret key; wraps a single prime field element.
623///
624/// # Example
625///
626/// ```
627/// use threshold_pairing::SecretKey;
628///
629/// // Generate a new random secret key
630/// let sk = SecretKey::random();
631///
632/// // Sign a message
633/// let signature = sk.sign(b"Hello, world!");
634///
635/// // Get the corresponding public key
636/// let pk = sk.public_key();
637/// assert!(pk.verify(&signature, b"Hello, world!"));
638/// ```
639///
640/// # Serde integration
641///
642/// `SecretKey` implements `Deserialize` but not `Serialize` to avoid accidental
643/// serialization in insecure contexts. To enable both use the [`serde_impl::SerdeSecret`]
644/// wrapper which implements both `Deserialize` and `Serialize`.
645///
646/// # Security
647///
648/// - The secret key is zeroized on drop to prevent secret leakage.
649/// - Equality comparison is performed in constant time to prevent timing attacks.
650#[derive(Clone)]
651pub struct SecretKey(Scalar);
652
653impl PartialEq for SecretKey {
654    fn eq(&self, other: &Self) -> bool {
655        // Use constant-time comparison to prevent timing attacks
656        self.0.to_bytes().ct_eq(&other.0.to_bytes()).into()
657    }
658}
659
660impl Eq for SecretKey {}
661
662impl Zeroize for SecretKey {
663    fn zeroize(&mut self) {
664        self.0.zeroize();
665    }
666}
667
668impl Drop for SecretKey {
669    fn drop(&mut self) {
670        self.zeroize();
671    }
672}
673
674impl Distribution<SecretKey> for Standard {
675    /// Creates a new random instance of `SecretKey`. If you do not need to specify your own RNG,
676    /// you should use the [`SecretKey::random()`](struct.SecretKey.html#method.random) constructor,
677    /// which uses [`rand::thread_rng()`](https://docs.rs/rand/0.8.5/rand/fn.thread_rng.html)
678    /// internally as its RNG.
679    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SecretKey {
680        SecretKey(Scalar::random(rng))
681    }
682}
683
684/// A debug statement where the secret prime field element is redacted.
685impl fmt::Debug for SecretKey {
686    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687        f.debug_tuple("SecretKey").field(&DebugDots).finish()
688    }
689}
690
691impl SecretKey {
692    /// Creates a new `SecretKey` from a mutable reference to a field element. This constructor
693    /// takes a reference to avoid unnecessary stack copying/moving of secrets (i.e. the field
694    /// element). The field element is copied into the SecretKey and the source is zeroized.
695    ///
696    /// # Security
697    /// The source scalar is zeroized after copying to prevent secret leakage.
698    pub fn from_mut(s: &mut Scalar) -> Self {
699        let sk = SecretKey(*s);
700        s.zeroize();
701        sk
702    }
703
704    /// Creates a new random instance of `SecretKey`.
705    ///
706    /// If you want to use/define your own random number generator, you should use
707    /// [`rand::Rng::gen()`] instead. This method uses
708    /// [`rand::thread_rng()`](https://docs.rs/rand/0.8.5/rand/fn.thread_rng.html)
709    /// internally as its RNG.
710    pub fn random() -> Self {
711        rand::random()
712    }
713
714    /// Returns the matching public key.
715    pub fn public_key(&self) -> PublicKey {
716        PublicKey(G1Affine::generator().mul(self.0))
717    }
718
719    /// Signs the given element of `G2`.
720    pub fn sign_g2<H: Into<G2Affine>>(&self, hash: H) -> Signature {
721        Signature(hash.into().mul(self.0))
722    }
723
724    /// Signs the given message.
725    ///
726    /// This is equivalent to `sign_g2(hash_g2(msg))`.
727    pub fn sign<M: AsRef<[u8]>>(&self, msg: M) -> Signature {
728        self.sign_g2(hash_g2(msg))
729    }
730
731    /// Returns the decrypted plaintext, or an error if the ciphertext is invalid.
732    ///
733    /// This method first verifies the ciphertext using [`Ciphertext::verify`] to prevent
734    /// chosen-ciphertext attacks. If verification fails, [`Error::InvalidCiphertext`] is
735    /// returned. The error gives no information about the decryption key, preventing
736    /// oracle attacks.
737    ///
738    /// # Errors
739    ///
740    /// Returns [`Error::InvalidCiphertext`] if the ciphertext fails the pairing-based
741    /// integrity check. This may indicate tampering, corruption, or a chosen-ciphertext
742    /// attack attempt.
743    ///
744    /// # Example
745    ///
746    /// ```
747    /// use threshold_pairing::SecretKey;
748    ///
749    /// let sk = SecretKey::random();
750    /// let pk = sk.public_key();
751    ///
752    /// let message = b"secret message";
753    /// let ciphertext = pk.encrypt(message);
754    ///
755    /// let decrypted = sk.decrypt(&ciphertext).expect("valid ciphertext");
756    /// assert_eq!(message.to_vec(), decrypted);
757    /// ```
758    pub fn decrypt(&self, ct: &Ciphertext) -> Result<Vec<u8>> {
759        if !ct.verify() {
760            return Err(Error::InvalidCiphertext);
761        }
762        let Ciphertext(ref u, ref v, _) = *ct;
763        let g = u.to_affine().mul(self.0);
764        Ok(xor_with_hash(g, v))
765    }
766
767    /// Generates a non-redacted debug string. This method differs from
768    /// the `Debug` implementation in that it *does* leak the secret prime
769    /// field element.
770    ///
771    /// # Security
772    ///
773    /// This method is intended for debugging purposes only. The output
774    /// contains sensitive key material and should never be logged in
775    /// production or exposed to untrusted parties.
776    ///
777    /// Requires the `expose-secret` feature flag to be enabled. This forces
778    /// an explicit opt-in and prevents accidental inclusion in production builds.
779    #[cfg(feature = "expose-secret")]
780    pub fn reveal(&self) -> String {
781        format!("SecretKey({:?})", self.0)
782    }
783}
784
785/// A secret key share.
786///
787/// # Serde integration
788/// `SecretKeyShare` implements `Deserialize` but not `Serialize` to avoid accidental
789/// serialization in insecure contexts. To enable both use the `::serde_impl::SerdeSecret`
790/// wrapper which implements both `Deserialize` and `Serialize`.
791#[derive(Clone, PartialEq, Eq)]
792pub struct SecretKeyShare(SecretKey);
793
794/// Can be used to create a new random instance of `SecretKeyShare`. This is only useful for testing
795/// purposes as such a key has not been derived from a `SecretKeySet`.
796impl Distribution<SecretKeyShare> for Standard {
797    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SecretKeyShare {
798        SecretKeyShare(rng.gen())
799    }
800}
801
802impl fmt::Debug for SecretKeyShare {
803    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804        f.debug_tuple("SecretKeyShare").field(&DebugDots).finish()
805    }
806}
807
808impl SecretKeyShare {
809    /// Creates a new `SecretKeyShare` from a mutable reference to a field element.
810    ///
811    /// # Security
812    /// The source scalar is zeroized after copying to prevent secret leakage.
813    pub fn from_mut(s: &mut Scalar) -> Self {
814        SecretKeyShare(SecretKey::from_mut(s))
815    }
816
817    /// Returns the matching public key share.
818    pub fn public_key_share(&self) -> PublicKeyShare {
819        PublicKeyShare(self.0.public_key())
820    }
821
822    /// Signs the given element of `G2`.
823    pub fn sign_g2<H: Into<G2Affine>>(&self, hash: H) -> SignatureShare {
824        SignatureShare(self.0.sign_g2(hash))
825    }
826
827    /// Signs the given message.
828    pub fn sign<M: AsRef<[u8]>>(&self, msg: M) -> SignatureShare {
829        SignatureShare(self.0.sign(msg))
830    }
831
832    /// Returns a decryption share, or `None`, if the ciphertext isn't valid.
833    pub fn decrypt_share(&self, ct: &Ciphertext) -> Option<DecryptionShare> {
834        if !ct.verify() {
835            return None;
836        }
837        Some(self.decrypt_share_no_verify(ct))
838    }
839
840    /// Returns a decryption share, without validating the ciphertext.
841    ///
842    /// # Security
843    ///
844    /// This method skips ciphertext integrity verification and is therefore **not** safe
845    /// to call on ciphertexts received from untrusted parties. Producing a decryption share
846    /// for an unverified ciphertext breaks the CCA2 security of the threshold encryption
847    /// scheme: an attacker can submit crafted ciphertexts and exploit the resulting shares
848    /// to extract plaintext information.
849    ///
850    /// Use [`decrypt_share`](Self::decrypt_share) for all network-facing code.
851    /// This method is intentionally restricted to crate-internal use.
852    pub(crate) fn decrypt_share_no_verify(&self, ct: &Ciphertext) -> DecryptionShare {
853        DecryptionShare(ct.0.to_affine().mul((self.0).0))
854    }
855
856    /// Generates a non-redacted debug string. This method differs from
857    /// the `Debug` implementation in that it *does* leak the secret prime
858    /// field element.
859    ///
860    /// # Security
861    ///
862    /// This method is intended for debugging purposes only. The output
863    /// contains sensitive key material and should never be logged in
864    /// production or exposed to untrusted parties.
865    ///
866    /// Requires the `expose-secret` feature flag to be enabled. This forces
867    /// an explicit opt-in and prevents accidental inclusion in production builds.
868    #[cfg(feature = "expose-secret")]
869    pub fn reveal(&self) -> String {
870        format!("SecretKeyShare({:?})", (self.0).0)
871    }
872}
873
874/// An encrypted message.
875///
876/// Ciphertexts are created by encrypting a message with a [`PublicKey`] and can be
877/// decrypted using the corresponding [`SecretKey`].
878///
879/// # Security
880///
881/// Always call [`Ciphertext::verify`] before processing a ciphertext from an untrusted
882/// source to prevent chosen-ciphertext attacks. The [`SecretKey::decrypt`] method
883/// performs this check automatically.
884///
885/// # Known Limitations
886///
887/// **Plaintext length is not hidden.** The ciphertext `v` component is an XOR-encrypted
888/// byte string of exactly the same length as the plaintext. An observer can therefore
889/// determine the exact length of any encrypted message from the ciphertext size alone.
890/// If message length is sensitive in your protocol, pad all messages to a uniform length
891/// before encrypting.
892///
893/// # Example
894///
895/// ```
896/// use threshold_pairing::SecretKey;
897///
898/// let sk = SecretKey::random();
899/// let pk = sk.public_key();
900///
901/// let message = b"secret message";
902/// let ciphertext = pk.encrypt(message);
903///
904/// // Verify the ciphertext is valid
905/// assert!(ciphertext.verify());
906///
907/// let decrypted = sk.decrypt(&ciphertext).unwrap();
908/// assert_eq!(message.to_vec(), decrypted);
909/// ```
910#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
911#[derive(Debug, Clone, PartialEq, Eq)]
912pub struct Ciphertext(
913    #[cfg_attr(feature = "serde", serde(with = "serde_impl::projective"))] G1Projective,
914    Vec<u8>,
915    #[cfg_attr(feature = "serde", serde(with = "serde_impl::projective"))] G2Projective,
916);
917
918impl Hash for Ciphertext {
919    fn hash<H: Hasher>(&self, state: &mut H) {
920        let Ciphertext(ref u, ref v, ref w) = *self;
921        u.to_affine().to_compressed().as_ref().hash(state);
922        v.hash(state);
923        w.to_affine().to_compressed().as_ref().hash(state);
924    }
925}
926
927impl PartialOrd for Ciphertext {
928    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
929        Some(self.cmp(other))
930    }
931}
932
933impl Ord for Ciphertext {
934    fn cmp(&self, other: &Self) -> Ordering {
935        let Ciphertext(ref u0, ref v0, ref w0) = self;
936        let Ciphertext(ref u1, ref v1, ref w1) = other;
937        cmp_projective(u0, u1)
938            .then(v0.cmp(v1))
939            .then(cmp_projective(w0, w1))
940    }
941}
942
943impl Ciphertext {
944    /// Returns `true` if this is a valid ciphertext. This check is necessary to prevent
945    /// chosen-ciphertext attacks. Implementation treats ciphertexts with either `u` or
946    /// `v` as the identity point as invalid.
947    #[must_use = "verification result must be checked"]
948    pub fn verify(&self) -> bool {
949        let Ciphertext(ref u, ref v, ref w) = *self;
950        if bool::from(u.is_identity()) || bool::from(w.is_identity()) {
951            return false;
952        }
953        let hash = hash_g1_g2(*u, v);
954        pairing(&G1Affine::generator(), &w.into()) == pairing(&u.into(), &hash.into())
955    }
956}
957
958/// A decryption share. A threshold of decryption shares can be used to decrypt a message.
959///
960/// Decryption shares are created by calling [`SecretKeyShare::decrypt_share`] on a
961/// [`Ciphertext`]. Once `threshold + 1` shares are collected, they can be combined
962/// using [`PublicKeySet::decrypt`] to recover the original plaintext.
963///
964/// # Example
965///
966/// ```
967/// use std::collections::BTreeMap;
968/// use threshold_pairing::SecretKeySet;
969///
970/// let mut rng = rand::thread_rng();
971/// let sk_set = SecretKeySet::random(2, &mut rng);
972/// let pk_set = sk_set.public_keys();
973///
974/// let message = b"secret";
975/// let ciphertext = pk_set.public_key().encrypt(message);
976///
977/// // Each participant creates a decryption share
978/// let shares: BTreeMap<_, _> = (0..3usize)
979///     .map(|i| {
980///         let share = sk_set.secret_key_share(i).unwrap()
981///             .decrypt_share(&ciphertext).unwrap();
982///         (i, share)
983///     })
984///     .collect();
985///
986/// // Combine shares to decrypt
987/// let decrypted = pk_set.decrypt(&shares, &ciphertext).unwrap();
988/// assert_eq!(message.to_vec(), decrypted);
989/// ```
990#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
991#[derive(Clone, PartialEq, Eq)]
992pub struct DecryptionShare(
993    #[cfg_attr(feature = "serde", serde(with = "serde_impl::projective"))] G1Projective,
994);
995
996impl PartialOrd for DecryptionShare {
997    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
998        Some(self.cmp(other))
999    }
1000}
1001
1002impl Ord for DecryptionShare {
1003    fn cmp(&self, other: &Self) -> Ordering {
1004        cmp_projective(&self.0, &other.0)
1005    }
1006}
1007
1008impl Distribution<DecryptionShare> for Standard {
1009    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> DecryptionShare {
1010        DecryptionShare(G1Projective::random(rng))
1011    }
1012}
1013
1014impl Hash for DecryptionShare {
1015    fn hash<H: Hasher>(&self, state: &mut H) {
1016        self.0.to_affine().to_compressed().as_ref().hash(state);
1017    }
1018}
1019
1020impl fmt::Debug for DecryptionShare {
1021    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1022        f.debug_tuple("DecryptionShare").field(&DebugDots).finish()
1023    }
1024}
1025
1026/// A public key and an associated set of public key shares.
1027#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1028#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
1029pub struct PublicKeySet {
1030    /// The coefficients of a polynomial whose value at `0` is the "master key", and value at
1031    /// `i + 1` is key share number `i`.
1032    commit: Commitment,
1033}
1034
1035impl Hash for PublicKeySet {
1036    fn hash<H: Hasher>(&self, state: &mut H) {
1037        self.commit.hash(state);
1038    }
1039}
1040
1041impl From<Commitment> for PublicKeySet {
1042    fn from(commit: Commitment) -> PublicKeySet {
1043        PublicKeySet { commit }
1044    }
1045}
1046
1047impl PublicKeySet {
1048    /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full
1049    /// signature.
1050    pub fn threshold(&self) -> usize {
1051        self.commit.degree()
1052    }
1053
1054    /// Returns the public key.
1055    pub fn public_key(&self) -> PublicKey {
1056        PublicKey(self.commit.coeff[0])
1057    }
1058
1059    /// Returns the `i`-th public key share.
1060    ///
1061    /// # Errors
1062    ///
1063    /// Returns [`Error::IndexMapsToZero`] if `i` maps to the zero scalar after the internal
1064    /// `index + 1` offset (e.g. `-1i32`). This prevents a caller from accidentally
1065    /// retrieving the master public key through an out-of-range signed index.
1066    pub fn public_key_share<T: TryIntoScalarSafe>(&self, i: T) -> Result<PublicKeyShare> {
1067        let value = self.commit.evaluate(into_scalar_plus_1(i)?);
1068        Ok(PublicKeyShare(PublicKey(value)))
1069    }
1070
1071    /// Combines the shares into a signature that can be verified with the main public key.
1072    ///
1073    /// The validity of the shares is not checked: If one of them is invalid, the resulting
1074    /// signature also is. Only returns an error if there is a duplicate index or too few shares.
1075    ///
1076    /// Validity of signature shares should be checked beforehand, or validity of the result
1077    /// afterwards.
1078    ///
1079    /// # Errors
1080    ///
1081    /// - [`Error::NotEnoughShares`]: Fewer than `threshold + 1` shares were provided
1082    /// - [`Error::DuplicateEntry`]: Two shares have the same index
1083    ///
1084    /// # Example
1085    ///
1086    /// ```
1087    /// # extern crate rand;
1088    /// #
1089    /// # use std::collections::BTreeMap;
1090    /// # use threshold_pairing::SecretKeySet;
1091    /// #
1092    /// let sk_set = SecretKeySet::random(3, &mut rand::thread_rng());
1093    /// let sk_shares: Vec<_> = (0..6usize)
1094    ///     .map(|i| sk_set.secret_key_share(i).expect("valid index"))
1095    ///     .collect();
1096    /// let pk_set = sk_set.public_keys();
1097    /// let msg = "Happy birthday! If this is signed, at least four people remembered!";
1098    ///
1099    /// // Create four signature shares for the message.
1100    /// let sig_shares: BTreeMap<_, _> = (0..4).map(|i| (i, sk_shares[i].sign(msg))).collect();
1101    ///
1102    /// // Validate the signature shares.
1103    /// for (i, sig_share) in &sig_shares {
1104    ///     assert!(pk_set.public_key_share(*i).expect("valid index").verify(sig_share, msg));
1105    /// }
1106    ///
1107    /// // Combine them to produce the main signature.
1108    /// let sig = pk_set.combine_signatures(&sig_shares).expect("not enough shares");
1109    ///
1110    /// // Validate the main signature. If the shares were valid, this can't fail.
1111    /// assert!(pk_set.public_key().verify(&sig, msg));
1112    /// ```
1113    ///
1114    /// # Security
1115    ///
1116    /// Signature share validity is **not** checked by this method. A single invalid or
1117    /// maliciously crafted share silently produces an invalid combined signature. In
1118    /// adversarial settings, callers **must** verify each share against its corresponding
1119    /// [`PublicKeyShare`] before combining:
1120    ///
1121    /// ```
1122    /// # use std::collections::BTreeMap;
1123    /// # use threshold_pairing::SecretKeySet;
1124    /// # let sk_set = SecretKeySet::random(2, &mut rand::thread_rng());
1125    /// # let pk_set = sk_set.public_keys();
1126    /// # let msg = b"msg";
1127    /// # let sig_shares: BTreeMap<usize, _> = (0..3usize)
1128    /// #     .map(|i| (i, sk_set.secret_key_share(i).unwrap().sign(msg)))
1129    /// #     .collect();
1130    /// for (i, sig_share) in &sig_shares {
1131    ///     assert!(pk_set.public_key_share(*i).expect("valid index").verify(sig_share, msg));
1132    /// }
1133    /// ```
1134    ///
1135    /// Validity of the result can also be checked afterwards by verifying the combined
1136    /// signature against [`PublicKeySet::public_key`].
1137    pub fn combine_signatures<'a, T, I>(&self, shares: I) -> Result<Signature>
1138    where
1139        I: IntoIterator<Item = (T, &'a SignatureShare)>,
1140        T: TryIntoScalarSafe,
1141    {
1142        let samples = shares.into_iter().map(|(i, share)| (i, &(share.0).0));
1143        Ok(Signature(interpolate(self.commit.degree(), samples)?))
1144    }
1145
1146    /// Combines the shares to decrypt the ciphertext.
1147    ///
1148    /// The ciphertext is verified before interpolation. Decryption share validity is
1149    /// **not** checked; use [`decrypt_with_verification`](Self::decrypt_with_verification)
1150    /// if you need per-share verification in adversarial settings.
1151    ///
1152    /// # Errors
1153    ///
1154    /// - [`Error::InvalidCiphertext`]: The ciphertext failed integrity verification
1155    /// - [`Error::NotEnoughShares`]: Fewer than `threshold + 1` shares were provided
1156    /// - [`Error::DuplicateEntry`]: Two shares have the same index
1157    ///
1158    /// # Example
1159    ///
1160    /// ```
1161    /// use std::collections::BTreeMap;
1162    /// use threshold_pairing::SecretKeySet;
1163    ///
1164    /// let mut rng = rand::thread_rng();
1165    /// let sk_set = SecretKeySet::random(2, &mut rng);
1166    /// let pk_set = sk_set.public_keys();
1167    ///
1168    /// let message = b"secret message";
1169    /// let ciphertext = pk_set.public_key().encrypt(message);
1170    ///
1171    /// // Collect 3 decryption shares
1172    /// let shares: BTreeMap<_, _> = (0..3usize)
1173    ///     .map(|i| {
1174    ///         let share = sk_set.secret_key_share(i).unwrap()
1175    ///             .decrypt_share(&ciphertext).unwrap();
1176    ///         (i, share)
1177    ///     })
1178    ///     .collect();
1179    ///
1180    /// let decrypted = pk_set.decrypt(&shares, &ciphertext).expect("valid shares");
1181    /// assert_eq!(message.to_vec(), decrypted);
1182    /// ```
1183    pub fn decrypt<'a, T, I>(&self, shares: I, ct: &Ciphertext) -> Result<Vec<u8>>
1184    where
1185        I: IntoIterator<Item = (T, &'a DecryptionShare)>,
1186        T: TryIntoScalarSafe,
1187    {
1188        if !ct.verify() {
1189            return Err(Error::InvalidCiphertext);
1190        }
1191        let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
1192        let g = interpolate(self.commit.degree(), samples)?;
1193        Ok(xor_with_hash(g, &ct.1))
1194    }
1195
1196    /// Combines the shares to decrypt the ciphertext, verifying each decryption share
1197    /// against its corresponding public key share before interpolation.
1198    ///
1199    /// This is the recommended method in adversarial settings where individual participants
1200    /// may submit invalid or malicious decryption shares.
1201    ///
1202    /// # Errors
1203    ///
1204    /// - [`Error::InvalidCiphertext`]: The ciphertext failed integrity verification
1205    /// - [`Error::NotEnoughShares`]: Fewer than `threshold + 1` shares were provided
1206    /// - [`Error::DuplicateEntry`]: Two shares have the same index
1207    /// - [`Error::InvalidShare`]: A decryption share failed verification against its
1208    ///   public key share
1209    ///
1210    /// # Example
1211    ///
1212    /// ```
1213    /// use std::collections::BTreeMap;
1214    /// use threshold_pairing::SecretKeySet;
1215    ///
1216    /// let mut rng = rand::thread_rng();
1217    /// let sk_set = SecretKeySet::random(2, &mut rng);
1218    /// let pk_set = sk_set.public_keys();
1219    ///
1220    /// let message = b"secret message";
1221    /// let ciphertext = pk_set.public_key().encrypt(message);
1222    ///
1223    /// // Collect 3 decryption shares together with their public key shares
1224    /// let shares: BTreeMap<_, _> = (0..3usize)
1225    ///     .map(|i| {
1226    ///         let sk_share = sk_set.secret_key_share(i).unwrap();
1227    ///         let pk_share = pk_set.public_key_share(i).unwrap();
1228    ///         let dec_share = sk_share.decrypt_share(&ciphertext).unwrap();
1229    ///         (i, (dec_share, pk_share))
1230    ///     })
1231    ///     .collect();
1232    ///
1233    /// let decrypted = pk_set
1234    ///     .decrypt_with_verification(
1235    ///         shares.iter().map(|(i, (d, p))| (*i, d, p)),
1236    ///         &ciphertext,
1237    ///     )
1238    ///     .expect("valid shares");
1239    /// assert_eq!(message.to_vec(), decrypted);
1240    /// ```
1241    pub fn decrypt_with_verification<'a, T, I>(&self, shares: I, ct: &Ciphertext) -> Result<Vec<u8>>
1242    where
1243        I: IntoIterator<Item = (T, &'a DecryptionShare, &'a PublicKeyShare)>,
1244        T: TryIntoScalarSafe,
1245    {
1246        if !ct.verify() {
1247            return Err(Error::InvalidCiphertext);
1248        }
1249        let verified: Vec<_> = shares
1250            .into_iter()
1251            .map(|(i, dec_share, pk_share)| {
1252                if !pk_share.verify_decryption_share(dec_share, ct) {
1253                    return Err(Error::InvalidShare);
1254                }
1255                Ok((i, dec_share))
1256            })
1257            .collect::<Result<_>>()?;
1258        let samples = verified.into_iter().map(|(i, share)| (i, &share.0));
1259        let g = interpolate(self.commit.degree(), samples)?;
1260        Ok(xor_with_hash(g, &ct.1))
1261    }
1262}
1263
1264/// A secret key and an associated set of secret key shares.
1265#[derive(Clone, PartialEq, Eq)]
1266pub struct SecretKeySet {
1267    /// The coefficients of a polynomial whose value at `0` is the "master key", and value at
1268    /// `i + 1` is key share number `i`.
1269    poly: Poly,
1270}
1271
1272impl From<Poly> for SecretKeySet {
1273    fn from(poly: Poly) -> SecretKeySet {
1274        SecretKeySet { poly }
1275    }
1276}
1277
1278impl SecretKeySet {
1279    /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
1280    /// sign and decrypt. This constructor is identical to [`SecretKeySet::try_random`] in every
1281    /// way except that this constructor panics if the other returns an error.
1282    ///
1283    /// # Panics
1284    ///
1285    /// Panics if the `threshold` is too large for the coefficients to fit into a `Vec`.
1286    /// For fallible construction, use [`SecretKeySet::try_random`] instead.
1287    ///
1288    /// # Example
1289    ///
1290    /// ```
1291    /// use threshold_pairing::SecretKeySet;
1292    ///
1293    /// let mut rng = rand::thread_rng();
1294    /// let sk_set = SecretKeySet::random(2, &mut rng);
1295    ///
1296    /// // Get shares for participants
1297    /// let share_0 = sk_set.secret_key_share(0usize).expect("valid index");
1298    /// let share_1 = sk_set.secret_key_share(1usize).expect("valid index");
1299    /// ```
1300    pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self {
1301        SecretKeySet::try_random(threshold, rng)
1302            .unwrap_or_else(|e| panic!("Failed to create random `SecretKeySet`: {}", e))
1303    }
1304
1305    /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
1306    /// sign and decrypt. This constructor is identical to [`SecretKeySet::random`] in every
1307    /// way except that this constructor returns an `Err` where `random` would panic.
1308    ///
1309    /// # Errors
1310    ///
1311    /// Returns [`Error::DegreeTooHigh`] if the `threshold` is too large for the
1312    /// coefficients to fit into a `Vec`.
1313    pub fn try_random<R: Rng>(threshold: usize, rng: &mut R) -> Result<Self> {
1314        Poly::try_random(threshold, rng).map(SecretKeySet::from)
1315    }
1316
1317    /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full
1318    /// signature.
1319    pub fn threshold(&self) -> usize {
1320        self.poly.degree()
1321    }
1322
1323    /// Returns the `i`-th secret key share.
1324    ///
1325    /// # Errors
1326    ///
1327    /// Returns [`Error::IndexMapsToZero`] if `i` maps to the zero scalar after the internal
1328    /// `index + 1` offset (e.g. `-1i32`). This prevents a caller from accidentally
1329    /// retrieving the master secret key through an out-of-range signed index.
1330    pub fn secret_key_share<T: TryIntoScalarSafe>(&self, i: T) -> Result<SecretKeyShare> {
1331        let mut fr = self.poly.evaluate(into_scalar_plus_1(i)?);
1332        Ok(SecretKeyShare::from_mut(&mut fr))
1333    }
1334
1335    /// Returns the corresponding public key set. That information can be shared publicly.
1336    pub fn public_keys(&self) -> PublicKeySet {
1337        PublicKeySet {
1338            commit: self.poly.commitment(),
1339        }
1340    }
1341
1342    /// Returns the secret master key.
1343    #[cfg(test)]
1344    fn secret_key(&self) -> SecretKey {
1345        let mut fr = self.poly.evaluate(0);
1346        SecretKey::from_mut(&mut fr)
1347    }
1348}
1349
1350/// Domain separation tag for BLS signatures following the IETF BLS draft specification.
1351/// This ensures signatures cannot be replayed across different protocols.
1352pub const BLS_SIG_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
1353
1354/// Returns a hash of the given message in `G2` using the standard hash-to-curve
1355/// algorithm (RFC 9380 / draft-irtf-cfrg-hash-to-curve).
1356///
1357/// This implementation uses the recommended domain separation tag and provides
1358/// proper security properties required for BLS signatures.
1359///
1360/// This function is useful when you need to pre-hash a message for use with
1361/// [`PublicKey::verify_g2`] or [`SecretKey::sign_g2`], or when implementing
1362/// custom signature schemes.
1363///
1364/// # Example
1365///
1366/// ```
1367/// use threshold_pairing::{hash_g2, SecretKey};
1368///
1369/// let sk = SecretKey::random();
1370/// let pk = sk.public_key();
1371///
1372/// let message = b"Hello, world!";
1373/// let hash = hash_g2(message);
1374///
1375/// // Sign using the pre-computed hash
1376/// let sig = sk.sign_g2(hash);
1377///
1378/// // Verify using the same hash
1379/// assert!(pk.verify_g2(&sig, hash));
1380/// ```
1381pub fn hash_g2<M: AsRef<[u8]>>(msg: M) -> G2Projective {
1382    <G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve(msg, BLS_SIG_DST)
1383}
1384
1385/// Domain separation tag for threshold encryption hash function.
1386/// This ensures separation from the signature scheme.
1387const THRESHOLD_ENC_DST: &[u8] = b"THRESHOLD_ENC_BLS12381G2_XMD:SHA-256_SSWU_RO_";
1388
1389/// Returns a hash of the group element and message, in the second group.
1390///
1391/// Uses proper domain separation to ensure cryptographic independence from
1392/// the signature scheme.
1393fn hash_g1_g2<M: AsRef<[u8]>>(g1: G1Projective, msg: M) -> G2Projective {
1394    // Concatenate the compressed G1 point with the message for hashing
1395    let mut data = g1.to_affine().to_compressed().as_ref().to_vec();
1396    data.extend_from_slice(msg.as_ref());
1397    <G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve(&data, THRESHOLD_ENC_DST)
1398}
1399
1400/// Returns the bitwise xor of `bytes` with a sequence of pseudorandom bytes determined by `g1`.
1401fn xor_with_hash(g1: G1Projective, bytes: &[u8]) -> Vec<u8> {
1402    let digest = sha3_256(g1.to_affine().to_compressed().as_ref());
1403    let rng = ChaChaRng::from_seed(digest);
1404    let xor = |(a, b): (u8, &u8)| a ^ b;
1405    rng.sample_iter(&Standard).zip(bytes).map(xor).collect()
1406}
1407
1408/// Given a list of `t + 1` samples `(i - 1, f(i) * g)` for a polynomial `f` of degree `t`, and a
1409/// group generator `g`, returns `f(0) * g`.
1410fn interpolate<C, B, T, I>(t: usize, items: I) -> Result<C>
1411where
1412    C: PrimeCurve<Scalar = Scalar>,
1413    I: IntoIterator<Item = (T, B)>,
1414    T: TryIntoScalarSafe,
1415    B: Borrow<C>,
1416{
1417    let samples: Vec<_> = items
1418        .into_iter()
1419        .take(t + 1)
1420        .map(|(i, sample)| -> Result<_> { Ok((into_scalar_plus_1(i)?, sample)) })
1421        .collect::<Result<_>>()?;
1422    if samples.len() <= t {
1423        return Err(Error::NotEnoughShares);
1424    }
1425
1426    if t == 0 {
1427        return Ok(*samples[0].1.borrow());
1428    }
1429
1430    // Compute the products `x_prod[i]` of all but the `i`-th entry.
1431    let mut x_prod: Vec<Zeroizing<C::Scalar>> = Vec::with_capacity(t);
1432    let mut tmp = Zeroizing::new(C::Scalar::one());
1433    x_prod.push(Zeroizing::new(*tmp));
1434    for (x, _) in samples.iter().take(t) {
1435        tmp.mul_assign(x);
1436        x_prod.push(Zeroizing::new(*tmp));
1437    }
1438    *tmp = C::Scalar::one();
1439    for (i, (x, _)) in samples[1..].iter().enumerate().rev() {
1440        tmp.mul_assign(x);
1441        x_prod[i].mul_assign(&*tmp);
1442    }
1443
1444    let mut result = C::identity();
1445    for (mut l0, (x, sample)) in x_prod.into_iter().zip(&samples) {
1446        // Compute the value at 0 of the Lagrange polynomial that is `0` at the other data
1447        // points but `1` at `x`.
1448        let mut denom = Zeroizing::new(C::Scalar::one());
1449        for (x0, _) in samples.iter().filter(|(x0, _)| x0 != x) {
1450            let mut diff = Zeroizing::new(*x0);
1451            diff.sub_assign(x);
1452            denom.mul_assign(&*diff);
1453        }
1454        l0.mul_assign(&Option::from(denom.invert()).ok_or(Error::DuplicateEntry)?);
1455        result.add_assign(&sample.borrow().to_affine().mul(*l0));
1456    }
1457    Ok(result)
1458}
1459
1460/// Converts a share index to the scalar used as the polynomial evaluation point (`index + 1`),
1461/// rejecting any index that would map to zero (which would expose the master secret).
1462///
1463/// # Errors
1464///
1465/// Returns [`Error::IndexMapsToZero`] when `x + 1 ≡ 0 (mod p)` (e.g. `x = -1i32`).
1466fn into_scalar_plus_1<I: TryIntoScalarSafe>(x: I) -> Result<Scalar> {
1467    let s = x.try_into_scalar_safe()?;
1468    let mut result = Scalar::one();
1469    result.add_assign(&s);
1470    Ok(result)
1471}
1472
1473/// Type that implements `Debug` printing three dots. This can be used to hide the contents of a
1474/// field in a `Debug` implementation.
1475struct DebugDots;
1476
1477impl fmt::Debug for DebugDots {
1478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479        write!(f, "...")
1480    }
1481}
1482
1483#[cfg(test)]
1484mod tests {
1485    use super::*;
1486
1487    use std::collections::BTreeMap;
1488
1489    use rand::{self, distributions::Standard, random, Rng};
1490
1491    #[test]
1492    fn test_simple_sig() {
1493        let sk0 = SecretKey::random();
1494        let sk1 = SecretKey::random();
1495        let pk0 = sk0.public_key();
1496        let msg0 = b"Real news";
1497        let msg1 = b"Fake news";
1498        assert!(pk0.verify(&sk0.sign(msg0), msg0));
1499        assert!(!pk0.verify(&sk1.sign(msg0), msg0)); // Wrong key.
1500        assert!(!pk0.verify(&sk0.sign(msg1), msg0)); // Wrong message.
1501    }
1502
1503    #[test]
1504    fn test_threshold_sig() {
1505        let mut rng = rand::thread_rng();
1506        let sk_set = SecretKeySet::random(3, &mut rng);
1507        let pk_set = sk_set.public_keys();
1508        let pk_master = pk_set.public_key();
1509
1510        // Make sure the keys are different, and the first coefficient is the main key.
1511        assert_ne!(pk_master, pk_set.public_key_share(0usize).unwrap().0);
1512        assert_ne!(pk_master, pk_set.public_key_share(1usize).unwrap().0);
1513        assert_ne!(pk_master, pk_set.public_key_share(2usize).unwrap().0);
1514
1515        // Make sure we don't hand out the main secret key to anyone.
1516        let sk_master = sk_set.secret_key();
1517        let sk_share_0 = sk_set.secret_key_share(0usize).unwrap().0;
1518        let sk_share_1 = sk_set.secret_key_share(1usize).unwrap().0;
1519        let sk_share_2 = sk_set.secret_key_share(2usize).unwrap().0;
1520        assert_ne!(sk_master, sk_share_0);
1521        assert_ne!(sk_master, sk_share_1);
1522        assert_ne!(sk_master, sk_share_2);
1523
1524        let msg = "Totally real news";
1525
1526        // The threshold is 3, so 4 signature shares will suffice to recreate the share.
1527        let sigs: BTreeMap<_, _> = [5usize, 8, 7, 10]
1528            .iter()
1529            .map(|&i| {
1530                let sig = sk_set.secret_key_share(i).unwrap().sign(msg);
1531                (i, sig)
1532            })
1533            .collect();
1534
1535        // Each of the shares is a valid signature matching its public key share.
1536        for (i, sig) in &sigs {
1537            assert!(pk_set.public_key_share(*i).unwrap().verify(sig, msg));
1538        }
1539
1540        // Combined, they produce a signature matching the main public key.
1541        let sig = pk_set.combine_signatures(&sigs).expect("signatures match");
1542        assert!(pk_set.public_key().verify(&sig, msg));
1543
1544        // A different set of signatories produces the same signature.
1545        let sigs2: BTreeMap<_, _> = [42usize, 43, 44, 45]
1546            .iter()
1547            .map(|&i| {
1548                let sig = sk_set.secret_key_share(i).unwrap().sign(msg);
1549                (i, sig)
1550            })
1551            .collect();
1552        let sig2 = pk_set.combine_signatures(&sigs2).expect("signatures match");
1553        assert_eq!(sig, sig2);
1554    }
1555
1556    #[test]
1557    fn test_simple_enc() {
1558        let sk_bob: SecretKey = random();
1559        let sk_eve: SecretKey = random();
1560        let pk_bob = sk_bob.public_key();
1561        let msg = b"Muffins in the canteen today! Don't tell Eve!";
1562        let ciphertext = pk_bob.encrypt(&msg[..]);
1563        assert!(ciphertext.verify());
1564
1565        // Bob can decrypt the message.
1566        let decrypted = sk_bob.decrypt(&ciphertext).expect("invalid ciphertext");
1567        assert_eq!(msg[..], decrypted[..]);
1568
1569        // Eve decrypts the ciphertext with her key — the ciphertext is valid (verification
1570        // is key-independent), but she gets garbage plaintext because she has the wrong key.
1571        let decrypted_eve = sk_eve
1572            .decrypt(&ciphertext)
1573            .expect("ciphertext verifies fine");
1574        assert_ne!(msg[..], decrypted_eve[..]);
1575
1576        // Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate.
1577        let Ciphertext(u, v, w) = ciphertext;
1578        let fake_ciphertext = Ciphertext(u, vec![0; v.len()], w);
1579        assert!(!fake_ciphertext.verify());
1580        assert!(sk_bob.decrypt(&fake_ciphertext).is_err());
1581    }
1582
1583    #[test]
1584    fn test_random_extreme_thresholds() {
1585        let mut rng = rand::thread_rng();
1586        let sks = SecretKeySet::random(0, &mut rng);
1587        assert_eq!(0, sks.threshold());
1588        assert!(SecretKeySet::try_random(usize::MAX, &mut rng).is_err());
1589    }
1590
1591    #[test]
1592    fn test_threshold_enc() {
1593        let mut rng = rand::thread_rng();
1594        let sk_set = SecretKeySet::random(3, &mut rng);
1595        let pk_set = sk_set.public_keys();
1596        let msg = b"Totally real news";
1597        let ciphertext = pk_set.public_key().encrypt(&msg[..]);
1598
1599        // The threshold is 3, so 4 signature shares will suffice to decrypt.
1600        let shares: BTreeMap<_, _> = [5usize, 8, 7, 10]
1601            .iter()
1602            .map(|&i| {
1603                let dec_share = sk_set
1604                    .secret_key_share(i)
1605                    .unwrap()
1606                    .decrypt_share(&ciphertext)
1607                    .expect("ciphertext is invalid");
1608                (i, dec_share)
1609            })
1610            .collect();
1611
1612        // Each of the shares is valid matching its public key share.
1613        for (i, share) in &shares {
1614            assert!(pk_set
1615                .public_key_share(*i)
1616                .unwrap()
1617                .verify_decryption_share(share, &ciphertext));
1618        }
1619
1620        // Combined, they can decrypt the message.
1621        let decrypted = pk_set
1622            .decrypt(&shares, &ciphertext)
1623            .expect("decryption shares match");
1624        assert_eq!(msg[..], decrypted[..]);
1625    }
1626
1627    /// Some basic sanity checks for the `hash_g2` function.
1628    #[test]
1629    fn test_hash_g2() {
1630        let rng = rand::thread_rng();
1631        let msg: Vec<u8> = rng.sample_iter(&Standard).take(1000).collect();
1632        let msg_end0: Vec<u8> = msg.iter().chain(b"end0").cloned().collect();
1633        let msg_end1: Vec<u8> = msg.iter().chain(b"end1").cloned().collect();
1634
1635        assert_eq!(hash_g2(&msg), hash_g2(&msg));
1636        assert_ne!(hash_g2(&msg), hash_g2(&msg_end0));
1637        assert_ne!(hash_g2(&msg_end0), hash_g2(msg_end1));
1638    }
1639
1640    /// Some basic sanity checks for the `hash_g1_g2` function.
1641    #[test]
1642    fn test_hash_g1_g2() {
1643        let mut rng = rand::thread_rng();
1644        let msg: Vec<u8> = (&mut rng).sample_iter(&Standard).take(1000).collect();
1645        let msg_end0: Vec<u8> = msg.iter().chain(b"end0").cloned().collect();
1646        let msg_end1: Vec<u8> = msg.iter().chain(b"end1").cloned().collect();
1647        let g0 = G1Projective::random(&mut rng);
1648        let g1 = G1Projective::random(&mut rng);
1649
1650        assert_eq!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg));
1651        assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg_end0));
1652        assert_ne!(hash_g1_g2(g0, &msg_end0), hash_g1_g2(g0, msg_end1));
1653        assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g1, &msg));
1654    }
1655
1656    /// Some basic sanity checks for the `hash_bytes` function.
1657    #[test]
1658    fn test_xor_with_hash() {
1659        let mut rng = rand::thread_rng();
1660        let g0 = G1Projective::random(&mut rng);
1661        let g1 = G1Projective::random(&mut rng);
1662        let xwh = xor_with_hash;
1663        assert_eq!(xwh(g0, &[0; 5]), xwh(g0, &[0; 5]));
1664        assert_ne!(xwh(g0, &[0; 5]), xwh(g1, &[0; 5]));
1665        assert_eq!(5, xwh(g0, &[0; 5]).len());
1666        assert_eq!(6, xwh(g0, &[0; 6]).len());
1667        assert_eq!(20, xwh(g0, &[0; 20]).len());
1668    }
1669
1670    #[test]
1671    fn test_from_to_bytes() {
1672        let sk: SecretKey = random();
1673        let sig = sk.sign("Please sign here: ______");
1674        let pk = sk.public_key();
1675        let pk2 = PublicKey::from_bytes(pk.to_bytes()).expect("invalid pk representation");
1676        assert_eq!(pk, pk2);
1677        let sig2 = Signature::from_bytes(sig.to_bytes()).expect("invalid sig representation");
1678        assert_eq!(sig, sig2);
1679    }
1680
1681    #[test]
1682    #[cfg(feature = "bincode")]
1683    fn test_serde() {
1684        let sk = SecretKey::random();
1685        let sig = sk.sign("Please sign here: ______");
1686        let pk = sk.public_key();
1687        let ser_pk = bincode::serde::encode_to_vec(pk, bincode::config::legacy())
1688            .expect("serialize public key");
1689        let (deser_pk, _) = bincode::serde::decode_from_slice(&ser_pk, bincode::config::legacy())
1690            .expect("deserialize public key");
1691        assert_eq!(ser_pk.len(), PK_SIZE);
1692        assert_eq!(pk, deser_pk);
1693        let ser_sig = bincode::serde::encode_to_vec(&sig, bincode::config::legacy())
1694            .expect("serialize signature");
1695        let (deser_sig, _) = bincode::serde::decode_from_slice(&ser_sig, bincode::config::legacy())
1696            .expect("deserialize signature");
1697        assert_eq!(ser_sig.len(), SIG_SIZE);
1698        assert_eq!(sig, deser_sig);
1699    }
1700
1701    #[test]
1702    fn test_zeroize() {
1703        let zero_sk = SecretKey::from_mut(&mut Scalar::zero());
1704
1705        let mut sk = SecretKey::random();
1706        assert_ne!(zero_sk, sk);
1707
1708        sk.zeroize();
1709        assert_eq!(zero_sk, sk);
1710    }
1711
1712    #[test]
1713    fn test_rng_seed() {
1714        let sk1 = SecretKey::random();
1715        let sk2 = SecretKey::random();
1716
1717        assert_ne!(sk1, sk2);
1718        let mut seed = [0u8; 32];
1719        rand::thread_rng().fill_bytes(&mut seed);
1720
1721        let mut rng = ChaChaRng::from_seed(seed);
1722        let sk3: SecretKey = rng.sample(Standard);
1723
1724        let mut rng = ChaChaRng::from_seed(seed);
1725        let sk4: SecretKey = rng.sample(Standard);
1726        assert_eq!(sk3, sk4);
1727    }
1728
1729    /// Compile-time check that a type is `Send + Sync`.
1730    fn assert_send_sync<T: Send + Sync>() {}
1731
1732    /// Verify all public types are `Send + Sync`.
1733    #[test]
1734    fn types_are_send_and_sync() {
1735        assert_send_sync::<PublicKey>();
1736        assert_send_sync::<PublicKeyShare>();
1737        assert_send_sync::<Signature>();
1738        assert_send_sync::<SignatureShare>();
1739        assert_send_sync::<SecretKey>();
1740        assert_send_sync::<SecretKeyShare>();
1741        assert_send_sync::<Ciphertext>();
1742        assert_send_sync::<DecryptionShare>();
1743        assert_send_sync::<PublicKeySet>();
1744        assert_send_sync::<SecretKeySet>();
1745    }
1746}