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}