1use std::{fmt, io};
2
3use ark_poly::{EvaluationDomain, GeneralEvaluationDomain};
4use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
5use ark_std::UniformRand;
6use bincode;
7use ferveo_common::serialization;
8use generic_array::{typenum::U48, GenericArray};
9use group_threshold_cryptography as tpke;
10use rand::RngCore;
11use serde::{Deserialize, Serialize};
12use serde_with::serde_as;
13pub use tpke::api::{
14 prepare_combine_simple, share_combine_precomputed, share_combine_simple,
15 Fr, G1Affine, G1Prepared, G2Affine, SecretBox, E,
16};
17
18pub type PublicKey = ferveo_common::PublicKey<E>;
19pub type Keypair = ferveo_common::Keypair<E>;
20pub type Validator = crate::Validator<E>;
21pub type Transcript = PubliclyVerifiableSS<E>;
22pub type ValidatorMessage = (Validator, Transcript);
23
24#[cfg(feature = "bindings-python")]
25use crate::bindings_python;
26#[cfg(feature = "bindings-wasm")]
27use crate::bindings_wasm;
28pub use crate::EthereumAddress;
29use crate::{
30 do_verify_aggregation, Error, PVSSMap, PubliclyVerifiableParams,
31 PubliclyVerifiableSS, Result,
32};
33
34pub type DecryptionSharePrecomputed = tpke::api::DecryptionSharePrecomputed;
35
36pub fn to_bytes<T: CanonicalSerialize>(item: &T) -> Result<Vec<u8>> {
40 let mut writer = Vec::new();
41 item.serialize_compressed(&mut writer)?;
42 Ok(writer)
43}
44
45pub fn from_bytes<T: CanonicalDeserialize>(bytes: &[u8]) -> Result<T> {
46 let mut reader = io::Cursor::new(bytes);
47 let item = T::deserialize_compressed(&mut reader)?;
48 Ok(item)
49}
50
51pub fn encrypt(
52 message: SecretBox<Vec<u8>>,
53 aad: &[u8],
54 pubkey: &DkgPublicKey,
55) -> Result<Ciphertext> {
56 let mut rng = rand::thread_rng();
57 let ciphertext = tpke::api::encrypt(message, aad, &pubkey.0, &mut rng)?;
58 Ok(Ciphertext(ciphertext))
59}
60
61pub fn decrypt_with_shared_secret(
62 ciphertext: &Ciphertext,
63 aad: &[u8],
64 shared_secret: &SharedSecret,
65) -> Result<Vec<u8>> {
66 let dkg_public_params = DkgPublicParameters::default();
67 tpke::api::decrypt_with_shared_secret(
68 &ciphertext.0,
69 aad,
70 &shared_secret.0,
71 &dkg_public_params.g1_inv,
72 )
73 .map_err(Error::from)
74}
75
76#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
77pub struct Ciphertext(tpke::api::Ciphertext);
78
79impl Ciphertext {
80 pub fn header(&self) -> Result<CiphertextHeader> {
81 Ok(CiphertextHeader(self.0.header()?))
82 }
83
84 pub fn payload(&self) -> Vec<u8> {
85 self.0.payload()
86 }
87}
88
89#[serde_as]
90#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
91pub struct CiphertextHeader(tpke::api::CiphertextHeader);
92
93#[derive(
95 PartialEq, Eq, Debug, Serialize, Deserialize, Copy, Clone, PartialOrd,
96)]
97pub enum FerveoVariant {
98 Simple,
100 Precomputed,
102}
103
104impl fmt::Display for FerveoVariant {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(f, "{}", self.as_str())
107 }
108}
109
110impl FerveoVariant {
111 pub fn as_str(&self) -> &'static str {
112 match self {
113 FerveoVariant::Simple => "FerveoVariant::Simple",
114 FerveoVariant::Precomputed => "FerveoVariant::Precomputed",
115 }
116 }
117
118 pub fn from_string(s: &str) -> Result<Self> {
119 match s {
120 "FerveoVariant::Simple" => Ok(FerveoVariant::Simple),
121 "FerveoVariant::Precomputed" => Ok(FerveoVariant::Precomputed),
122 _ => Err(Error::InvalidVariant(s.to_string())),
123 }
124 }
125}
126
127#[cfg(feature = "bindings-python")]
128impl From<bindings_python::FerveoVariant> for FerveoVariant {
129 fn from(variant: bindings_python::FerveoVariant) -> Self {
130 variant.0
131 }
132}
133
134#[cfg(feature = "bindings-wasm")]
135impl From<bindings_wasm::FerveoVariant> for FerveoVariant {
136 fn from(variant: bindings_wasm::FerveoVariant) -> Self {
137 variant.0
138 }
139}
140
141#[serde_as]
142#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
143pub struct DkgPublicKey(
144 #[serde_as(as = "serialization::SerdeAs")] pub(crate) G1Affine,
145);
146
147impl DkgPublicKey {
148 pub fn to_bytes(&self) -> Result<GenericArray<u8, U48>> {
149 let as_bytes = to_bytes(&self.0)?;
150 Ok(GenericArray::<u8, U48>::from_slice(&as_bytes).to_owned())
151 }
152
153 pub fn from_bytes(bytes: &[u8]) -> Result<DkgPublicKey> {
154 let bytes =
155 GenericArray::<u8, U48>::from_exact_iter(bytes.iter().cloned())
156 .ok_or_else(|| {
157 Error::InvalidByteLength(
158 Self::serialized_size(),
159 bytes.len(),
160 )
161 })?;
162 from_bytes(&bytes).map(DkgPublicKey)
163 }
164
165 pub fn serialized_size() -> usize {
166 48
167 }
168
169 pub fn random() -> Self {
172 let mut rng = rand::thread_rng();
173 let g1 = G1Affine::rand(&mut rng);
174 Self(g1)
175 }
176}
177
178pub type UnblindingKey = FieldPoint;
179
180#[serde_as]
181#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
182pub struct FieldPoint(#[serde_as(as = "serialization::SerdeAs")] pub Fr);
183
184impl FieldPoint {
185 pub fn to_bytes(&self) -> Result<Vec<u8>> {
186 to_bytes(&self.0)
187 }
188
189 pub fn from_bytes(bytes: &[u8]) -> Result<FieldPoint> {
190 from_bytes(bytes).map(FieldPoint)
191 }
192}
193
194#[derive(Clone)]
195pub struct Dkg(crate::PubliclyVerifiableDkg<E>);
196
197impl Dkg {
198 pub fn new(
199 tau: u32,
200 shares_num: u32,
201 security_threshold: u32,
202 validators: &[Validator],
203 me: &Validator,
204 ) -> Result<Self> {
205 let dkg_params = crate::DkgParams {
206 tau,
207 security_threshold,
208 shares_num,
209 };
210 let dkg = crate::PubliclyVerifiableDkg::<E>::new(
211 validators,
212 &dkg_params,
213 me,
214 )?;
215 Ok(Self(dkg))
216 }
217
218 pub fn public_key(&self) -> DkgPublicKey {
219 DkgPublicKey(self.0.public_key())
220 }
221
222 pub fn generate_transcript<R: RngCore>(
223 &self,
224 rng: &mut R,
225 ) -> Result<Transcript> {
226 self.0.create_share(rng)
227 }
228
229 pub fn aggregate_transcripts(
230 &mut self,
231 messages: &[ValidatorMessage],
232 ) -> Result<AggregatedTranscript> {
233 for (validator, transcript) in messages {
241 self.0.deal(validator, transcript)?;
242 }
243 Ok(AggregatedTranscript(crate::pvss::aggregate(&self.0.vss)))
244 }
245
246 pub fn public_params(&self) -> DkgPublicParameters {
247 DkgPublicParameters {
248 g1_inv: self.0.pvss_params.g_inv(),
249 }
250 }
251}
252
253fn make_pvss_map(messages: &[ValidatorMessage]) -> PVSSMap<E> {
254 let mut pvss_map: PVSSMap<E> = PVSSMap::new();
255 messages.iter().for_each(|(validator, transcript)| {
256 pvss_map.insert(validator.address.clone(), transcript.clone());
257 });
258 pvss_map
259}
260
261#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
262pub struct AggregatedTranscript(PubliclyVerifiableSS<E, crate::Aggregated>);
263
264impl AggregatedTranscript {
265 pub fn new(messages: &[ValidatorMessage]) -> Self {
266 let pvss_map = make_pvss_map(messages);
267 AggregatedTranscript(crate::pvss::aggregate(&pvss_map))
268 }
269
270 pub fn verify(
271 &self,
272 shares_num: u32,
273 messages: &[ValidatorMessage],
274 ) -> Result<bool> {
275 let pvss_params = PubliclyVerifiableParams::<E>::default();
276 let domain = GeneralEvaluationDomain::<Fr>::new(shares_num as usize)
277 .expect("Unable to construct an evaluation domain");
278
279 let is_valid_optimistic = self.0.verify_optimistic();
280 if !is_valid_optimistic {
281 return Err(Error::InvalidTranscriptAggregate);
282 }
283
284 let pvss_map = make_pvss_map(messages);
285 let validators: Vec<_> = messages
286 .iter()
287 .map(|(validator, _)| validator)
288 .cloned()
289 .collect();
290
291 let is_valid = do_verify_aggregation(
293 &self.0.coeffs,
294 &self.0.shares,
295 &pvss_params,
296 &validators,
297 &domain,
298 &pvss_map,
299 )?;
300 Ok(is_valid)
301 }
302
303 pub fn create_decryption_share_precomputed(
304 &self,
305 dkg: &Dkg,
306 ciphertext_header: &CiphertextHeader,
307 aad: &[u8],
308 validator_keypair: &Keypair,
309 ) -> Result<DecryptionSharePrecomputed> {
310 let domain_points: Vec<_> = dkg
311 .0
312 .domain
313 .elements()
314 .take(dkg.0.dkg_params.shares_num as usize)
315 .collect();
316 self.0.make_decryption_share_simple_precomputed(
317 &ciphertext_header.0,
318 aad,
319 &validator_keypair.decryption_key,
320 dkg.0.me.share_index,
321 &domain_points,
322 &dkg.0.pvss_params.g_inv(),
323 )
324 }
325
326 pub fn create_decryption_share_simple(
327 &self,
328 dkg: &Dkg,
329 ciphertext_header: &CiphertextHeader,
330 aad: &[u8],
331 validator_keypair: &Keypair,
332 ) -> Result<DecryptionShareSimple> {
333 let share = self.0.make_decryption_share_simple(
334 &ciphertext_header.0,
335 aad,
336 &validator_keypair.decryption_key,
337 dkg.0.me.share_index,
338 &dkg.0.pvss_params.g_inv(),
339 )?;
340 Ok(DecryptionShareSimple {
341 share,
342 domain_point: dkg.0.domain.element(dkg.0.me.share_index),
343 })
344 }
345}
346
347#[serde_as]
348#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
349pub struct DecryptionShareSimple {
350 share: tpke::api::DecryptionShareSimple,
351 #[serde_as(as = "serialization::SerdeAs")]
352 domain_point: Fr,
353}
354
355#[serde_as]
356#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
357pub struct DkgPublicParameters {
358 #[serde_as(as = "serialization::SerdeAs")]
359 pub(crate) g1_inv: G1Prepared,
360}
361
362impl Default for DkgPublicParameters {
363 fn default() -> Self {
364 DkgPublicParameters {
365 g1_inv: PubliclyVerifiableParams::<E>::default().g_inv(),
366 }
367 }
368}
369
370impl DkgPublicParameters {
371 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
372 bincode::deserialize(bytes).map_err(|e| e.into())
373 }
374
375 pub fn to_bytes(&self) -> Result<Vec<u8>> {
376 bincode::serialize(self).map_err(|e| e.into())
377 }
378}
379
380pub fn combine_shares_simple(shares: &[DecryptionShareSimple]) -> SharedSecret {
381 let domain_points: Vec<_> = shares.iter().map(|s| s.domain_point).collect();
383 let lagrange_coefficients = prepare_combine_simple::<E>(&domain_points);
384
385 let shares: Vec<_> = shares.iter().cloned().map(|s| s.share).collect();
386 let shared_secret =
387 share_combine_simple(&shares, &lagrange_coefficients[..]);
388 SharedSecret(shared_secret)
389}
390
391#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
392pub struct SharedSecret(pub tpke::api::SharedSecret<E>);
393
394#[cfg(test)]
395mod test_ferveo_api {
396 use itertools::izip;
397 use rand::{prelude::StdRng, SeedableRng};
398 use tpke::SecretBox;
399
400 use crate::{api::*, dkg::test_common::*};
401
402 type TestInputs = (Vec<ValidatorMessage>, Vec<Validator>, Vec<Keypair>);
403
404 fn make_test_inputs(
405 rng: &mut StdRng,
406 tau: u32,
407 security_threshold: u32,
408 shares_num: u32,
409 ) -> TestInputs {
410 let validator_keypairs = gen_keypairs(shares_num);
411 let validators = validator_keypairs
412 .iter()
413 .enumerate()
414 .map(|(i, keypair)| Validator {
415 address: gen_address(i),
416 public_key: keypair.public_key(),
417 })
418 .collect::<Vec<_>>();
419
420 let messages: Vec<_> = validators
423 .iter()
424 .map(|sender| {
425 let dkg = Dkg::new(
426 tau,
427 shares_num,
428 security_threshold,
429 &validators,
430 sender,
431 )
432 .unwrap();
433 (sender.clone(), dkg.generate_transcript(rng).unwrap())
434 })
435 .collect();
436 (messages, validators, validator_keypairs)
437 }
438
439 #[test]
440 fn test_dkg_pk_serialization() {
441 let dkg_pk = DkgPublicKey::random();
442 let serialized = dkg_pk.to_bytes().unwrap();
443 let deserialized = DkgPublicKey::from_bytes(&serialized).unwrap();
444 assert_eq!(dkg_pk, deserialized);
445 }
446
447 #[test]
448 fn test_server_api_tdec_precomputed() {
449 let rng = &mut StdRng::seed_from_u64(0);
450
451 for shares_num in [4, 7] {
453 let tau = 1;
454 let security_threshold = shares_num;
458
459 let (messages, validators, validator_keypairs) =
460 make_test_inputs(rng, tau, security_threshold, shares_num);
461
462 let me = validators[0].clone();
465 let mut dkg =
466 Dkg::new(tau, shares_num, security_threshold, &validators, &me)
467 .unwrap();
468
469 let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
470 assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
471
472 let dkg_public_key = dkg.public_key();
474
475 let msg = "my-msg".as_bytes().to_vec();
477 let aad: &[u8] = "my-aad".as_bytes();
478 let ciphertext =
479 encrypt(SecretBox::new(msg.clone()), aad, &dkg_public_key)
480 .unwrap();
481
482 let decryption_shares: Vec<_> =
484 izip!(&validators, &validator_keypairs)
485 .map(|(validator, validator_keypair)| {
486 let mut dkg = Dkg::new(
488 tau,
489 shares_num,
490 security_threshold,
491 &validators,
492 validator,
493 )
494 .unwrap();
495 let aggregate =
496 dkg.aggregate_transcripts(&messages).unwrap();
497 assert!(pvss_aggregated
498 .verify(shares_num, &messages)
499 .unwrap());
500
501 aggregate
503 .create_decryption_share_precomputed(
504 &dkg,
505 &ciphertext.header().unwrap(),
506 aad,
507 validator_keypair,
508 )
509 .unwrap()
510 })
511 .collect();
512
513 let shared_secret = share_combine_precomputed(&decryption_shares);
517 let plaintext = decrypt_with_shared_secret(
518 &ciphertext,
519 aad,
520 &SharedSecret(shared_secret),
521 )
522 .unwrap();
523 assert_eq!(plaintext, msg);
524
525 let decryption_shares =
528 decryption_shares[..shares_num as usize - 1].to_vec();
529
530 let shared_secret = share_combine_precomputed(&decryption_shares);
531 let result = decrypt_with_shared_secret(
532 &ciphertext,
533 aad,
534 &SharedSecret(shared_secret),
535 );
536 assert!(result.is_err());
537 }
538 }
539
540 #[test]
541 fn test_server_api_tdec_simple() {
542 let rng = &mut StdRng::seed_from_u64(0);
543
544 for shares_num in [4, 7] {
546 let tau = 1;
547 let security_threshold = shares_num / 2 + 1;
548
549 let (messages, validators, validator_keypairs) =
550 make_test_inputs(rng, tau, security_threshold, shares_num);
551
552 let mut dkg = Dkg::new(
555 tau,
556 shares_num,
557 security_threshold,
558 &validators,
559 &validators[0],
560 )
561 .unwrap();
562
563 let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
564 assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
565
566 let public_key = dkg.public_key();
568
569 let msg = "my-msg".as_bytes().to_vec();
571 let aad: &[u8] = "my-aad".as_bytes();
572 let ciphertext =
573 encrypt(SecretBox::new(msg.clone()), aad, &public_key).unwrap();
574
575 let decryption_shares: Vec<_> =
577 izip!(&validators, &validator_keypairs)
578 .map(|(validator, validator_keypair)| {
579 let mut dkg = Dkg::new(
581 tau,
582 shares_num,
583 security_threshold,
584 &validators,
585 validator,
586 )
587 .unwrap();
588 let aggregate =
589 dkg.aggregate_transcripts(&messages).unwrap();
590 assert!(aggregate
591 .verify(shares_num, &messages)
592 .unwrap());
593 aggregate
594 .create_decryption_share_simple(
595 &dkg,
596 &ciphertext.header().unwrap(),
597 aad,
598 validator_keypair,
599 )
600 .unwrap()
601 })
602 .collect();
603
604 let decryption_shares =
609 decryption_shares[..security_threshold as usize].to_vec();
610
611 let shared_secret = combine_shares_simple(&decryption_shares);
612 let plaintext =
613 decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
614 .unwrap();
615 assert_eq!(plaintext, msg);
616
617 let decryption_shares =
620 decryption_shares[..security_threshold as usize - 1].to_vec();
621
622 let shared_secret = combine_shares_simple(&decryption_shares);
623 let result =
624 decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
625 assert!(result.is_err());
626 }
627 }
628
629 #[test]
630 fn server_side_local_verification() {
631 let rng = &mut StdRng::seed_from_u64(0);
632
633 let tau = 1;
634 let security_threshold = 3;
635 let shares_num = 4;
636
637 let (messages, validators, _) =
638 make_test_inputs(rng, tau, security_threshold, shares_num);
639
640 let me = validators[0].clone();
643 let mut dkg =
644 Dkg::new(tau, shares_num, security_threshold, &validators, &me)
645 .unwrap();
646
647 let local_aggregate = dkg.aggregate_transcripts(&messages).unwrap();
648 assert!(local_aggregate
649 .verify(dkg.0.dkg_params.shares_num, &messages)
650 .is_ok());
651 }
652
653 #[test]
654 fn client_side_local_verification() {
655 let rng = &mut StdRng::seed_from_u64(0);
656
657 let tau = 1;
658 let security_threshold = 3;
659 let shares_num = 4;
660
661 let (messages, _, _) =
662 make_test_inputs(rng, tau, security_threshold, shares_num);
663
664 let messages = &messages[..security_threshold as usize];
666
667 let aggregated_transcript = AggregatedTranscript::new(messages);
669
670 let result = aggregated_transcript.verify(shares_num, messages);
675 assert!(result.is_ok());
676 assert!(result.unwrap());
677
678 let not_enough_messages = &messages[..2];
682 assert!(not_enough_messages.len() < security_threshold as usize);
683 let insufficient_aggregate =
684 AggregatedTranscript::new(not_enough_messages);
685 let result = insufficient_aggregate.verify(shares_num, messages);
686 assert!(result.is_err());
687
688 let (bad_messages, _, _) =
691 make_test_inputs(rng, tau, security_threshold, shares_num);
692 let mixed_messages = [&messages[..2], &bad_messages[..1]].concat();
693 let bad_aggregate = AggregatedTranscript::new(&mixed_messages);
694 let result = bad_aggregate.verify(shares_num, messages);
695 assert!(result.is_err());
696 }
697}