1use core::iter;
7
8use elliptic_curve::rand_core::{CryptoRng, RngCore};
9use merlin::Transcript;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::{
14 Ciphertext, Keypair, PublicKey, SecretKey, VerifiableDecryption,
15 alloc::Vec,
16 group::Group,
17 proofs::{LogEqualityProof, ProofOfPossession},
18 sharing::{Error, Params, PublicKeySet},
19};
20
21#[derive(Debug, Clone)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "serde", serde(bound = ""))]
27pub struct Dealer<G: Group> {
28 params: Params,
29 polynomial: Vec<Keypair<G>>,
30 proof_of_possession: ProofOfPossession<G>,
31}
32
33impl<G: Group> Dealer<G> {
34 pub fn new<R: CryptoRng + RngCore>(params: Params, rng: &mut R) -> Self {
36 let polynomial: Vec<_> = (0..params.threshold)
37 .map(|_| Keypair::<G>::generate(rng))
38 .collect();
39
40 let mut transcript = Transcript::new(b"elgamal_share_poly");
41 transcript.append_u64(b"n", params.shares as u64);
42 transcript.append_u64(b"t", params.threshold as u64);
43
44 let proof_of_possession = ProofOfPossession::new(&polynomial, &mut transcript, rng);
45
46 Self {
47 params,
48 polynomial,
49 proof_of_possession,
50 }
51 }
52
53 pub fn public_info(&self) -> (Vec<G::Element>, &ProofOfPossession<G>) {
56 let public_polynomial = self
57 .polynomial
58 .iter()
59 .map(|pair| pair.public().as_element())
60 .collect();
61 (public_polynomial, &self.proof_of_possession)
62 }
63
64 pub fn secret_share_for_participant(&self, index: usize) -> SecretKey<G> {
70 assert!(
71 index < self.params.shares,
72 "participant index {index} out of bounds, expected a value in 0..{}",
73 self.params.shares
74 );
75
76 let power = G::Scalar::from(index as u64 + 1);
77 let mut poly_value = SecretKey::new(G::Scalar::from(0));
78 for keypair in self.polynomial.iter().rev() {
79 poly_value = poly_value * &power + keypair.secret().clone();
80 }
81 poly_value
82 }
83}
84
85#[derive(Debug, Clone)]
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
90#[cfg_attr(feature = "serde", serde(bound = ""))]
91pub struct ActiveParticipant<G: Group> {
92 key_set: PublicKeySet<G>,
93 index: usize,
94 secret_share: SecretKey<G>,
95}
96
97impl<G: Group> ActiveParticipant<G> {
98 pub fn new(
109 key_set: PublicKeySet<G>,
110 index: usize,
111 secret_share: SecretKey<G>,
112 ) -> Result<Self, Error> {
113 let expected_element = key_set.participant_keys()[index].as_element();
114 if G::mul_generator(secret_share.expose_scalar()) == expected_element {
115 Ok(Self {
116 key_set,
117 index,
118 secret_share,
119 })
120 } else {
121 Err(Error::InvalidSecret)
122 }
123 }
124
125 pub fn key_set(&self) -> &PublicKeySet<G> {
128 &self.key_set
129 }
130
131 pub fn index(&self) -> usize {
133 self.index
134 }
135
136 pub fn secret_share(&self) -> &SecretKey<G> {
139 &self.secret_share
140 }
141
142 pub fn public_key_share(&self) -> &PublicKey<G> {
144 &self.key_set.participant_keys()[self.index]
145 }
146
147 pub fn proof_of_possession<R: CryptoRng + RngCore>(&self, rng: &mut R) -> ProofOfPossession<G> {
150 let mut transcript = Transcript::new(b"elgamal_participant_pop");
151 self.key_set.commit(&mut transcript);
152 transcript.append_u64(b"i", self.index as u64);
153 ProofOfPossession::from_keys(
154 iter::once(&self.secret_share),
155 iter::once(self.public_key_share()),
156 &mut transcript,
157 rng,
158 )
159 }
160
161 pub fn decrypt_share<R>(
164 &self,
165 ciphertext: Ciphertext<G>,
166 rng: &mut R,
167 ) -> (VerifiableDecryption<G>, LogEqualityProof<G>)
168 where
169 R: CryptoRng + RngCore,
170 {
171 let dh_element = ciphertext.random_element * self.secret_share.expose_scalar();
172 let our_public_key = self.public_key_share().as_element();
173 let mut transcript = Transcript::new(b"elgamal_decryption_share");
174 self.key_set.commit(&mut transcript);
175 transcript.append_u64(b"i", self.index as u64);
176
177 let proof = LogEqualityProof::new(
178 &PublicKey::from_element(ciphertext.random_element),
179 &self.secret_share,
180 (our_public_key, dh_element),
181 &mut transcript,
182 rng,
183 );
184 (VerifiableDecryption::from_element(dh_element), proof)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::{curve25519::scalar::Scalar as Scalar25519, group::Ristretto};
192
193 #[test]
194 fn shared_2_of_3_key() {
195 let mut rng = rand::rng();
196 let params = Params::new(3, 2);
197
198 let dealer = Dealer::<Ristretto>::new(params, &mut rng);
199 let (public_poly, public_poly_proof) = dealer.public_info();
200 let key_set = PublicKeySet::new(params, public_poly, public_poly_proof).unwrap();
201
202 let alice_share = dealer.secret_share_for_participant(0);
203 let alice = ActiveParticipant::new(key_set.clone(), 0, alice_share).unwrap();
204 let bob_share = dealer.secret_share_for_participant(1);
205 let bob = ActiveParticipant::new(key_set.clone(), 1, bob_share).unwrap();
206 let carol_share = dealer.secret_share_for_participant(2);
207 let carol = ActiveParticipant::new(key_set.clone(), 2, carol_share).unwrap();
208
209 key_set
210 .verify_participant(0, &alice.proof_of_possession(&mut rng))
211 .unwrap();
212 key_set
213 .verify_participant(1, &bob.proof_of_possession(&mut rng))
214 .unwrap();
215 key_set
216 .verify_participant(2, &carol.proof_of_possession(&mut rng))
217 .unwrap();
218 assert!(
219 key_set
220 .verify_participant(1, &alice.proof_of_possession(&mut rng))
221 .is_err()
222 );
223
224 let ciphertext = key_set.shared_key().encrypt(15_u64, &mut rng);
225 let (alice_share, proof) = alice.decrypt_share(ciphertext, &mut rng);
226 key_set
227 .verify_share(alice_share.into(), ciphertext, 0, &proof)
228 .unwrap();
229
230 let (bob_share, proof) = bob.decrypt_share(ciphertext, &mut rng);
231 key_set
232 .verify_share(bob_share.into(), ciphertext, 1, &proof)
233 .unwrap();
234
235 let composite_dh_element =
239 *alice_share.as_element() * Scalar25519::from(2_u64) - *bob_share.as_element();
240 let message = Ristretto::mul_generator(&Scalar25519::from(15_u64));
241 assert_eq!(composite_dh_element, ciphertext.blinded_element - message);
242 }
243}