1use core::{ops::Deref, fmt};
2use std::{io, collections::HashMap};
3
4use thiserror::Error;
5
6use zeroize::{Zeroize, Zeroizing};
7use rand_core::{RngCore, CryptoRng};
8
9use chacha20::{
10 cipher::{crypto_common::KeyIvInit, StreamCipher},
11 Key as Cc20Key, Nonce as Cc20Iv, ChaCha20,
12};
13
14use transcript::{Transcript, RecommendedTranscript};
15
16#[cfg(test)]
17use ciphersuite::group::ff::Field;
18use ciphersuite::{group::GroupEncoding, Ciphersuite};
19use multiexp::BatchVerifier;
20
21use schnorr::SchnorrSignature;
22use dleq::DLEqProof;
23
24use dkg::{Participant, ThresholdParams};
25
26mod sealed {
27 use super::*;
28
29 pub trait ReadWrite: Sized {
30 fn read<R: io::Read>(reader: &mut R, params: ThresholdParams) -> io::Result<Self>;
31 fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()>;
32
33 fn serialize(&self) -> Vec<u8> {
34 let mut buf = vec![];
35 self.write(&mut buf).unwrap();
36 buf
37 }
38 }
39
40 pub trait Message: Clone + PartialEq + Eq + fmt::Debug + Zeroize + ReadWrite {}
41 impl<M: Clone + PartialEq + Eq + fmt::Debug + Zeroize + ReadWrite> Message for M {}
42
43 pub trait Encryptable: Clone + AsRef<[u8]> + AsMut<[u8]> + Zeroize + ReadWrite {}
44 impl<E: Clone + AsRef<[u8]> + AsMut<[u8]> + Zeroize + ReadWrite> Encryptable for E {}
45}
46pub(crate) use sealed::*;
47
48#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
50pub struct EncryptionKeyMessage<C: Ciphersuite, M: Message> {
51 msg: M,
52 enc_key: C::G,
53}
54
55impl<C: Ciphersuite, M: Message> EncryptionKeyMessage<C, M> {
57 pub fn read<R: io::Read>(reader: &mut R, params: ThresholdParams) -> io::Result<Self> {
58 Ok(Self { msg: M::read(reader, params)?, enc_key: C::read_G(reader)? })
59 }
60
61 pub fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
62 self.msg.write(writer)?;
63 writer.write_all(self.enc_key.to_bytes().as_ref())
64 }
65
66 pub fn serialize(&self) -> Vec<u8> {
67 let mut buf = vec![];
68 self.write(&mut buf).unwrap();
69 buf
70 }
71
72 #[cfg(test)]
73 pub(crate) fn enc_key(&self) -> C::G {
74 self.enc_key
75 }
76}
77
78#[derive(Clone, Zeroize)]
81pub struct EncryptedMessage<C: Ciphersuite, E: Encryptable> {
82 key: C::G,
83 pop: SchnorrSignature<C>,
92 msg: Zeroizing<E>,
93}
94
95fn ecdh<C: Ciphersuite>(private: &Zeroizing<C::F>, public: C::G) -> Zeroizing<C::G> {
96 Zeroizing::new(public * private.deref())
97}
98
99fn cipher<C: Ciphersuite>(context: [u8; 32], ecdh: &Zeroizing<C::G>) -> ChaCha20 {
102 let mut transcript = RecommendedTranscript::new(b"DKG Encryption v0.2");
105 transcript.append_message(b"context", context);
106
107 transcript.domain_separate(b"encryption_key");
108
109 let mut ecdh = ecdh.to_bytes();
110 transcript.append_message(b"shared_key", ecdh.as_ref());
111 ecdh.as_mut().zeroize();
112
113 let zeroize = |buf: &mut [u8]| buf.zeroize();
114
115 let mut key = Cc20Key::default();
116 let mut challenge = transcript.challenge(b"key");
117 key.copy_from_slice(&challenge[.. 32]);
118 zeroize(challenge.as_mut());
119
120 let mut iv = Cc20Iv::default();
125 iv.copy_from_slice(b"DKG IV v0.2\0");
127
128 let res = ChaCha20::new(&key, &iv);
131 zeroize(key.as_mut());
132 res
133}
134
135fn encrypt<R: RngCore + CryptoRng, C: Ciphersuite, E: Encryptable>(
136 rng: &mut R,
137 context: [u8; 32],
138 from: Participant,
139 to: C::G,
140 mut msg: Zeroizing<E>,
141) -> EncryptedMessage<C, E> {
142 let key = Zeroizing::new(C::random_nonzero_F(rng));
154 cipher::<C>(context, &ecdh::<C>(&key, to)).apply_keystream(msg.as_mut().as_mut());
155
156 let pub_key = C::generator() * key.deref();
157 let nonce = Zeroizing::new(C::random_nonzero_F(rng));
158 let pub_nonce = C::generator() * nonce.deref();
159 EncryptedMessage {
160 key: pub_key,
161 pop: SchnorrSignature::sign(
162 &key,
163 nonce,
164 pop_challenge::<C>(context, pub_nonce, pub_key, from, msg.deref().as_ref()),
165 ),
166 msg,
167 }
168}
169
170impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
171 pub fn read<R: io::Read>(reader: &mut R, params: ThresholdParams) -> io::Result<Self> {
172 Ok(Self {
173 key: C::read_G(reader)?,
174 pop: SchnorrSignature::<C>::read(reader)?,
175 msg: Zeroizing::new(E::read(reader, params)?),
176 })
177 }
178
179 pub fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
180 writer.write_all(self.key.to_bytes().as_ref())?;
181 self.pop.write(writer)?;
182 self.msg.write(writer)
183 }
184
185 pub fn serialize(&self) -> Vec<u8> {
186 let mut buf = vec![];
187 self.write(&mut buf).unwrap();
188 buf
189 }
190
191 #[cfg(test)]
192 pub(crate) fn invalidate_pop(&mut self) {
193 self.pop.s += C::F::ONE;
194 }
195
196 #[cfg(test)]
197 pub(crate) fn invalidate_msg<R: RngCore + CryptoRng>(
198 &mut self,
199 rng: &mut R,
200 context: [u8; 32],
201 from: Participant,
202 ) {
203 let key = Zeroizing::new(C::random_nonzero_F(rng));
206 let pub_key = C::generator() * key.deref();
207 let nonce = Zeroizing::new(C::random_nonzero_F(rng));
208 let pub_nonce = C::generator() * nonce.deref();
209 self.key = pub_key;
210 self.pop = SchnorrSignature::sign(
211 &key,
212 nonce,
213 pop_challenge::<C>(context, pub_nonce, pub_key, from, self.msg.deref().as_ref()),
214 );
215 }
216
217 #[cfg(test)]
219 pub(crate) fn invalidate_share_serialization<R: RngCore + CryptoRng>(
220 &mut self,
221 rng: &mut R,
222 context: [u8; 32],
223 from: Participant,
224 to: C::G,
225 ) {
226 use ciphersuite::group::ff::PrimeField;
227
228 let mut repr = <C::F as PrimeField>::Repr::default();
229 for b in repr.as_mut() {
230 *b = 255;
231 }
232 assert_eq!(repr.as_ref().len(), self.msg.as_ref().len());
234 assert!(!bool::from(C::F::from_repr(repr).is_some()));
236
237 self.msg.as_mut().as_mut().copy_from_slice(repr.as_ref());
238 *self = encrypt(rng, context, from, to, self.msg.clone());
239 }
240
241 #[cfg(test)]
243 pub(crate) fn invalidate_share_value<R: RngCore + CryptoRng>(
244 &mut self,
245 rng: &mut R,
246 context: [u8; 32],
247 from: Participant,
248 to: C::G,
249 ) {
250 use ciphersuite::group::ff::PrimeField;
251
252 let repr = C::F::ONE.to_repr();
254 self.msg.as_mut().as_mut().copy_from_slice(repr.as_ref());
255 *self = encrypt(rng, context, from, to, self.msg.clone());
256 }
257}
258
259#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
261pub struct EncryptionKeyProof<C: Ciphersuite> {
262 key: Zeroizing<C::G>,
263 dleq: DLEqProof<C::G>,
264}
265
266impl<C: Ciphersuite> EncryptionKeyProof<C> {
267 pub fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
268 Ok(Self { key: Zeroizing::new(C::read_G(reader)?), dleq: DLEqProof::read(reader)? })
269 }
270
271 pub fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
272 writer.write_all(self.key.to_bytes().as_ref())?;
273 self.dleq.write(writer)
274 }
275
276 pub fn serialize(&self) -> Vec<u8> {
277 let mut buf = vec![];
278 self.write(&mut buf).unwrap();
279 buf
280 }
281
282 #[cfg(test)]
283 pub(crate) fn invalidate_key(&mut self) {
284 *self.key += C::generator();
285 }
286
287 #[cfg(test)]
288 pub(crate) fn invalidate_dleq(&mut self) {
289 let mut buf = vec![];
290 self.dleq.write(&mut buf).unwrap();
291 buf[0] = buf[0].wrapping_add(1);
295 self.dleq = DLEqProof::read::<&[u8]>(&mut buf.as_ref()).unwrap();
296 }
297}
298
299fn pop_challenge<C: Ciphersuite>(
303 context: [u8; 32],
304 nonce: C::G,
305 key: C::G,
306 sender: Participant,
307 msg: &[u8],
308) -> C::F {
309 let mut transcript = RecommendedTranscript::new(b"DKG Encryption Key Proof of Possession v0.2");
310 transcript.append_message(b"context", context);
311
312 transcript.domain_separate(b"proof_of_possession");
313
314 transcript.append_message(b"nonce", nonce.to_bytes());
315 transcript.append_message(b"key", key.to_bytes());
316 transcript.append_message(b"sender", sender.to_bytes());
318 transcript.append_message(b"message", msg);
320 C::hash_to_F(b"DKG-encryption-proof_of_possession", &transcript.challenge(b"schnorr"))
324}
325
326fn encryption_key_transcript(context: [u8; 32]) -> RecommendedTranscript {
327 let mut transcript = RecommendedTranscript::new(b"DKG Encryption Key Correctness Proof v0.2");
328 transcript.append_message(b"context", context);
329 transcript
330}
331
332#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
333pub(crate) enum DecryptionError {
334 #[error("accused provided an invalid signature")]
335 InvalidSignature,
336 #[error("accuser provided an invalid decryption key")]
337 InvalidProof,
338}
339
340#[derive(Clone, Debug)]
342pub(crate) struct Decryption<C: Ciphersuite> {
343 context: [u8; 32],
344 enc_keys: HashMap<Participant, C::G>,
345}
346
347impl<C: Ciphersuite> Decryption<C> {
348 pub(crate) fn new(context: [u8; 32]) -> Self {
349 Self { context, enc_keys: HashMap::new() }
350 }
351 pub(crate) fn register<M: Message>(
352 &mut self,
353 participant: Participant,
354 msg: EncryptionKeyMessage<C, M>,
355 ) -> M {
356 assert!(
357 !self.enc_keys.contains_key(&participant),
358 "Re-registering encryption key for a participant"
359 );
360 self.enc_keys.insert(participant, msg.enc_key);
361 msg.msg
362 }
363
364 pub(crate) fn decrypt_with_proof<E: Encryptable>(
367 &self,
368 from: Participant,
369 decryptor: Participant,
370 mut msg: EncryptedMessage<C, E>,
371 proof: Option<EncryptionKeyProof<C>>,
373 ) -> Result<Zeroizing<E>, DecryptionError> {
374 if !msg.pop.verify(
375 msg.key,
376 pop_challenge::<C>(self.context, msg.pop.R, msg.key, from, msg.msg.deref().as_ref()),
377 ) {
378 Err(DecryptionError::InvalidSignature)?;
379 }
380
381 if let Some(proof) = proof {
382 proof
384 .dleq
385 .verify(
386 &mut encryption_key_transcript(self.context),
387 &[C::generator(), msg.key],
388 &[self.enc_keys[&decryptor], *proof.key],
389 )
390 .map_err(|_| DecryptionError::InvalidProof)?;
391
392 cipher::<C>(self.context, &proof.key).apply_keystream(msg.msg.as_mut().as_mut());
393 Ok(msg.msg)
394 } else {
395 Err(DecryptionError::InvalidProof)
396 }
397 }
398}
399
400#[derive(Clone)]
402pub(crate) struct Encryption<C: Ciphersuite> {
403 context: [u8; 32],
404 i: Participant,
405 enc_key: Zeroizing<C::F>,
406 enc_pub_key: C::G,
407 decryption: Decryption<C>,
408}
409
410impl<C: Ciphersuite> fmt::Debug for Encryption<C> {
411 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
412 fmt
413 .debug_struct("Encryption")
414 .field("context", &self.context)
415 .field("i", &self.i)
416 .field("enc_pub_key", &self.enc_pub_key)
417 .field("decryption", &self.decryption)
418 .finish_non_exhaustive()
419 }
420}
421
422impl<C: Ciphersuite> Zeroize for Encryption<C> {
423 fn zeroize(&mut self) {
424 self.enc_key.zeroize();
425 self.enc_pub_key.zeroize();
426 for (_, mut value) in self.decryption.enc_keys.drain() {
427 value.zeroize();
428 }
429 }
430}
431
432impl<C: Ciphersuite> Encryption<C> {
433 pub(crate) fn new<R: RngCore + CryptoRng>(
434 context: [u8; 32],
435 i: Participant,
436 rng: &mut R,
437 ) -> Self {
438 let enc_key = Zeroizing::new(C::random_nonzero_F(rng));
439 Self {
440 context,
441 i,
442 enc_pub_key: C::generator() * enc_key.deref(),
443 enc_key,
444 decryption: Decryption::new(context),
445 }
446 }
447
448 pub(crate) fn registration<M: Message>(&self, msg: M) -> EncryptionKeyMessage<C, M> {
449 EncryptionKeyMessage { msg, enc_key: self.enc_pub_key }
450 }
451
452 pub(crate) fn register<M: Message>(
453 &mut self,
454 participant: Participant,
455 msg: EncryptionKeyMessage<C, M>,
456 ) -> M {
457 self.decryption.register(participant, msg)
458 }
459
460 pub(crate) fn encrypt<R: RngCore + CryptoRng, E: Encryptable>(
461 &self,
462 rng: &mut R,
463 participant: Participant,
464 msg: Zeroizing<E>,
465 ) -> EncryptedMessage<C, E> {
466 encrypt(rng, self.context, self.i, self.decryption.enc_keys[&participant], msg)
467 }
468
469 pub(crate) fn decrypt<R: RngCore + CryptoRng, I: Copy + Zeroize, E: Encryptable>(
470 &self,
471 rng: &mut R,
472 batch: &mut BatchVerifier<I, C::G>,
473 batch_id: I,
476 from: Participant,
477 mut msg: EncryptedMessage<C, E>,
478 ) -> (Zeroizing<E>, EncryptionKeyProof<C>) {
479 msg.pop.batch_verify(
480 rng,
481 batch,
482 batch_id,
483 msg.key,
484 pop_challenge::<C>(self.context, msg.pop.R, msg.key, from, msg.msg.deref().as_ref()),
485 );
486
487 let key = ecdh::<C>(&self.enc_key, msg.key);
488 cipher::<C>(self.context, &key).apply_keystream(msg.msg.as_mut().as_mut());
489 (
490 msg.msg,
491 EncryptionKeyProof {
492 key,
493 dleq: DLEqProof::prove(
494 rng,
495 &mut encryption_key_transcript(self.context),
496 &[C::generator(), msg.key],
497 &self.enc_key,
498 ),
499 },
500 )
501 }
502
503 pub(crate) fn into_decryption(self) -> Decryption<C> {
504 self.decryption
505 }
506}