1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5use core::{
6 ops::Deref,
7 fmt::{self, Debug},
8};
9use std_shims::{sync::Arc, vec, vec::Vec, collections::HashMap, io};
10
11use zeroize::{Zeroize, Zeroizing};
12
13use ciphersuite::{
14 group::{
15 ff::{Field, PrimeField},
16 GroupEncoding,
17 },
18 Ciphersuite,
19};
20
21#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Zeroize)]
23#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
24pub struct Participant(u16);
25impl Participant {
26 pub const fn new(i: u16) -> Option<Participant> {
28 if i == 0 {
29 None
30 } else {
31 Some(Participant(i))
32 }
33 }
34
35 #[allow(clippy::wrong_self_convention)]
37 pub const fn to_bytes(&self) -> [u8; 2] {
38 self.0.to_le_bytes()
39 }
40}
41
42impl From<Participant> for u16 {
43 fn from(participant: Participant) -> u16 {
44 participant.0
45 }
46}
47
48impl fmt::Display for Participant {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 write!(f, "{}", self.0)
51 }
52}
53
54#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
56pub enum DkgError {
57 #[error("a parameter was 0 (threshold {t}, participants {n})")]
59 ZeroParameter {
60 t: u16,
62 n: u16,
64 },
65
66 #[error("invalid threshold (max {n}, got {t})")]
68 InvalidThreshold {
69 t: u16,
71 n: u16,
73 },
74
75 #[error("invalid participant (1 <= participant <= {n}, yet participant is {participant})")]
77 InvalidParticipant {
78 n: u16,
80 participant: Participant,
82 },
83
84 #[error("incorrect amount of verification shares (n = {n} yet {shares} provided)")]
86 IncorrectAmountOfVerificationShares {
87 n: u16,
89 shares: usize,
91 },
92
93 #[error("inapplicable method of interpolation ({0})")]
95 InapplicableInterpolation(&'static str),
96
97 #[error("incorrect amount of participants. {t} <= amount <= {n}, yet amount is {amount}")]
99 IncorrectAmountOfParticipants {
100 t: u16,
102 n: u16,
104 amount: usize,
106 },
107
108 #[error("a participant ({0}) was duplicated")]
110 DuplicatedParticipant(Participant),
111
112 #[error("not participating in declared signing set")]
114 NotParticipating,
115}
116
117#[cfg(feature = "borsh")]
119impl borsh::BorshDeserialize for Participant {
120 fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
121 Participant::new(u16::deserialize_reader(reader)?)
122 .ok_or_else(|| io::Error::other("invalid participant"))
123 }
124}
125
126#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
128#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
129pub struct ThresholdParams {
130 t: u16,
132 n: u16,
134 i: Participant,
136}
137
138struct AllParticipantIndexes {
140 i: u16,
141 n: u16,
142}
143impl Iterator for AllParticipantIndexes {
144 type Item = Participant;
145 fn next(&mut self) -> Option<Participant> {
146 if self.i > self.n {
147 None?;
148 }
149 let res = Participant::new(self.i).unwrap();
150
151 if self.i == u16::MAX {
153 self.n = 0;
154 } else {
155 self.i += 1;
156 }
157
158 Some(res)
159 }
160}
161
162impl ThresholdParams {
163 pub const fn new(t: u16, n: u16, i: Participant) -> Result<ThresholdParams, DkgError> {
165 if (t == 0) || (n == 0) {
166 return Err(DkgError::ZeroParameter { t, n });
167 }
168
169 if t > n {
170 return Err(DkgError::InvalidThreshold { t, n });
171 }
172 if i.0 > n {
173 return Err(DkgError::InvalidParticipant { n, participant: i });
174 }
175
176 Ok(ThresholdParams { t, n, i })
177 }
178
179 pub const fn t(&self) -> u16 {
181 self.t
182 }
183 pub const fn n(&self) -> u16 {
185 self.n
186 }
187 pub const fn i(&self) -> Participant {
189 self.i
190 }
191
192 pub fn all_participant_indexes(&self) -> impl Iterator<Item = Participant> {
194 AllParticipantIndexes { i: 1, n: self.n }
195 }
196}
197
198#[cfg(feature = "borsh")]
199impl borsh::BorshDeserialize for ThresholdParams {
200 fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
201 let t = u16::deserialize_reader(reader)?;
202 let n = u16::deserialize_reader(reader)?;
203 let i = Participant::deserialize_reader(reader)?;
204 ThresholdParams::new(t, n, i).map_err(|e| io::Error::other(format!("{e:?}")))
205 }
206}
207
208#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
210pub enum Interpolation<F: Zeroize + PrimeField> {
211 Constant(Vec<F>),
218 Lagrange,
220}
221
222impl<F: Zeroize + PrimeField> Interpolation<F> {
223 fn interpolation_factor(&self, i: Participant, included: &[Participant]) -> F {
225 match self {
226 Interpolation::Constant(c) => c[usize::from(u16::from(i) - 1)],
227 Interpolation::Lagrange => {
228 let i_f = F::from(u64::from(u16::from(i)));
229
230 let mut num = F::ONE;
231 let mut denom = F::ONE;
232 for l in included {
233 if i == *l {
234 continue;
235 }
236
237 let share = F::from(u64::from(u16::from(*l)));
238 num *= share;
239 denom *= share - i_f;
240 }
241
242 num * denom.invert().unwrap()
245 }
246 }
247 }
248}
249
250#[derive(Clone, PartialEq, Eq)]
256struct ThresholdCore<C: Ciphersuite> {
257 params: ThresholdParams,
258 group_key: C::G,
259 verification_shares: HashMap<Participant, C::G>,
260 interpolation: Interpolation<C::F>,
261 secret_share: Zeroizing<C::F>,
262}
263
264impl<C: Ciphersuite> fmt::Debug for ThresholdCore<C> {
265 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
266 fmt
267 .debug_struct("ThresholdCore")
268 .field("params", &self.params)
269 .field("group_key", &self.group_key)
270 .field("verification_shares", &self.verification_shares)
271 .field("interpolation", &self.interpolation)
272 .finish_non_exhaustive()
273 }
274}
275
276impl<C: Ciphersuite> Zeroize for ThresholdCore<C> {
277 fn zeroize(&mut self) {
278 self.params.zeroize();
279 self.group_key.zeroize();
280 for share in self.verification_shares.values_mut() {
281 share.zeroize();
282 }
283 self.interpolation.zeroize();
284 self.secret_share.zeroize();
285 }
286}
287
288#[derive(Clone, Debug, Zeroize)]
290pub struct ThresholdKeys<C: Ciphersuite> {
291 #[zeroize(skip)]
293 core: Arc<Zeroizing<ThresholdCore<C>>>,
294
295 scalar: C::F,
297 offset: C::F,
299}
300
301#[derive(Clone)]
303pub struct ThresholdView<C: Ciphersuite> {
304 interpolation: Interpolation<C::F>,
305 scalar: C::F,
306 offset: C::F,
307 group_key: C::G,
308 included: Vec<Participant>,
309 secret_share: Zeroizing<C::F>,
310 original_verification_shares: HashMap<Participant, C::G>,
311 verification_shares: HashMap<Participant, C::G>,
312}
313
314impl<C: Ciphersuite> fmt::Debug for ThresholdView<C> {
315 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
316 fmt
317 .debug_struct("ThresholdView")
318 .field("interpolation", &self.interpolation)
319 .field("scalar", &self.scalar)
320 .field("offset", &self.offset)
321 .field("group_key", &self.group_key)
322 .field("included", &self.included)
323 .field("original_verification_shares", &self.original_verification_shares)
324 .field("verification_shares", &self.verification_shares)
325 .finish_non_exhaustive()
326 }
327}
328
329impl<C: Ciphersuite> Zeroize for ThresholdView<C> {
330 fn zeroize(&mut self) {
331 self.scalar.zeroize();
332 self.offset.zeroize();
333 self.group_key.zeroize();
334 self.included.zeroize();
335 self.secret_share.zeroize();
336 for share in self.original_verification_shares.values_mut() {
337 share.zeroize();
338 }
339 for share in self.verification_shares.values_mut() {
340 share.zeroize();
341 }
342 }
343}
344
345impl<C: Ciphersuite> ThresholdKeys<C> {
346 pub fn new(
348 params: ThresholdParams,
349 interpolation: Interpolation<C::F>,
350 secret_share: Zeroizing<C::F>,
351 verification_shares: HashMap<Participant, C::G>,
352 ) -> Result<ThresholdKeys<C>, DkgError> {
353 if verification_shares.len() != usize::from(params.n()) {
354 Err(DkgError::IncorrectAmountOfVerificationShares {
355 n: params.n(),
356 shares: verification_shares.len(),
357 })?;
358 }
359 for participant in verification_shares.keys().copied() {
360 if u16::from(participant) > params.n() {
361 Err(DkgError::InvalidParticipant { n: params.n(), participant })?;
362 }
363 }
364
365 match &interpolation {
366 Interpolation::Constant(_) => {
367 if params.t() != params.n() {
368 Err(DkgError::InapplicableInterpolation("constant interpolation for keys where t != n"))?;
369 }
370 }
371 Interpolation::Lagrange => {}
372 }
373
374 let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
375 let group_key =
376 t.iter().map(|i| verification_shares[i] * interpolation.interpolation_factor(*i, &t)).sum();
377
378 Ok(ThresholdKeys {
379 core: Arc::new(Zeroizing::new(ThresholdCore {
380 params,
381 interpolation,
382 secret_share,
383 group_key,
384 verification_shares,
385 })),
386 scalar: C::F::ONE,
387 offset: C::F::ZERO,
388 })
389 }
390
391 #[must_use]
398 pub fn scale(mut self, scalar: C::F) -> Option<ThresholdKeys<C>> {
399 if bool::from(scalar.is_zero()) {
400 None?;
401 }
402 self.scalar *= scalar;
403 self.offset *= scalar;
404 Some(self)
405 }
406
407 #[must_use]
412 pub fn offset(mut self, offset: C::F) -> ThresholdKeys<C> {
413 self.offset += offset;
414 self
415 }
416
417 pub fn current_scalar(&self) -> C::F {
419 self.scalar
420 }
421
422 pub fn current_offset(&self) -> C::F {
424 self.offset
425 }
426
427 pub fn params(&self) -> ThresholdParams {
429 self.core.params
430 }
431
432 pub fn original_group_key(&self) -> C::G {
434 self.core.group_key
435 }
436
437 pub fn interpolation(&self) -> &Interpolation<C::F> {
439 &self.core.interpolation
440 }
441
442 pub fn group_key(&self) -> C::G {
444 (self.core.group_key * self.scalar) + (C::generator() * self.offset)
445 }
446
447 pub fn original_secret_share(&self) -> &Zeroizing<C::F> {
449 &self.core.secret_share
450 }
451
452 pub fn original_verification_share(&self, l: Participant) -> C::G {
456 self.core.verification_shares[&l]
457 }
458
459 pub fn view(&self, mut included: Vec<Participant>) -> Result<ThresholdView<C>, DkgError> {
462 if (included.len() < self.params().t.into()) ||
463 (usize::from(self.params().n()) < included.len())
464 {
465 Err(DkgError::IncorrectAmountOfParticipants {
466 t: self.params().t,
467 n: self.params().n,
468 amount: included.len(),
469 })?;
470 }
471 included.sort();
472 {
473 let mut found = included[0] == self.params().i();
474 for i in 1 .. included.len() {
475 if included[i - 1] == included[i] {
476 Err(DkgError::DuplicatedParticipant(included[i]))?;
477 }
478 found |= included[i] == self.params().i();
479 }
480 if !found {
481 Err(DkgError::NotParticipating)?;
482 }
483 }
484 {
485 let last = *included.last().unwrap();
486 if u16::from(last) > self.params().n() {
487 Err(DkgError::InvalidParticipant { n: self.params().n(), participant: last })?;
488 }
489 }
490
491 let secret_share_scaled = Zeroizing::new(self.scalar * self.original_secret_share().deref());
493 let mut secret_share = Zeroizing::new(
494 self.core.interpolation.interpolation_factor(self.params().i(), &included) *
495 secret_share_scaled.deref(),
496 );
497
498 let mut verification_shares = HashMap::with_capacity(included.len());
499 for i in &included {
500 let verification_share = self.core.verification_shares[i];
501 let verification_share = verification_share *
502 self.scalar *
503 self.core.interpolation.interpolation_factor(*i, &included);
504 verification_shares.insert(*i, verification_share);
505 }
506
507 if included[0] == self.params().i() {
517 *secret_share += self.offset;
518 }
519 *verification_shares.get_mut(&included[0]).unwrap() += C::generator() * self.offset;
520
521 Ok(ThresholdView {
522 interpolation: self.core.interpolation.clone(),
523 scalar: self.scalar,
524 offset: self.offset,
525 group_key: self.group_key(),
526 secret_share,
527 original_verification_shares: self.core.verification_shares.clone(),
528 verification_shares,
529 included,
530 })
531 }
532
533 pub fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
537 writer.write_all(&u32::try_from(C::ID.len()).unwrap().to_le_bytes())?;
538 writer.write_all(C::ID)?;
539 writer.write_all(&self.core.params.t.to_le_bytes())?;
540 writer.write_all(&self.core.params.n.to_le_bytes())?;
541 writer.write_all(&self.core.params.i.to_bytes())?;
542 match &self.core.interpolation {
543 Interpolation::Constant(c) => {
544 writer.write_all(&[0])?;
545 for c in c {
546 writer.write_all(c.to_repr().as_ref())?;
547 }
548 }
549 Interpolation::Lagrange => writer.write_all(&[1])?,
550 };
551 let mut share_bytes = self.core.secret_share.to_repr();
552 writer.write_all(share_bytes.as_ref())?;
553 share_bytes.as_mut().zeroize();
554 for l in 1 ..= self.core.params.n {
555 writer.write_all(
556 self.core.verification_shares[&Participant::new(l).unwrap()].to_bytes().as_ref(),
557 )?;
558 }
559 Ok(())
560 }
561
562 pub fn serialize(&self) -> Zeroizing<Vec<u8>> {
566 let mut serialized = Zeroizing::new(vec![]);
567 self.write::<Vec<u8>>(serialized.as_mut()).unwrap();
568 serialized
569 }
570
571 pub fn read<R: io::Read>(reader: &mut R) -> io::Result<ThresholdKeys<C>> {
573 {
574 let different = || io::Error::other("deserializing ThresholdKeys for another curve");
575
576 let mut id_len = [0; 4];
577 reader.read_exact(&mut id_len)?;
578 if u32::try_from(C::ID.len()).unwrap().to_le_bytes() != id_len {
579 Err(different())?;
580 }
581
582 let mut id = vec![0; C::ID.len()];
583 reader.read_exact(&mut id)?;
584 if id != C::ID {
585 Err(different())?;
586 }
587 }
588
589 let (t, n, i) = {
590 let mut read_u16 = || -> io::Result<u16> {
591 let mut value = [0; 2];
592 reader.read_exact(&mut value)?;
593 Ok(u16::from_le_bytes(value))
594 };
595 (
596 read_u16()?,
597 read_u16()?,
598 Participant::new(read_u16()?).ok_or(io::Error::other("invalid participant index"))?,
599 )
600 };
601
602 let mut interpolation = [0];
603 reader.read_exact(&mut interpolation)?;
604 let interpolation = match interpolation[0] {
605 0 => Interpolation::Constant({
606 let mut res = Vec::with_capacity(usize::from(n));
607 for _ in 0 .. n {
608 res.push(C::read_F(reader)?);
609 }
610 res
611 }),
612 1 => Interpolation::Lagrange,
613 _ => Err(io::Error::other("invalid interpolation method"))?,
614 };
615
616 let secret_share = Zeroizing::new(C::read_F(reader)?);
617
618 let mut verification_shares = HashMap::new();
619 for l in (1 ..= n).map(Participant) {
620 verification_shares.insert(l, <C as Ciphersuite>::read_G(reader)?);
621 }
622
623 ThresholdKeys::new(
624 ThresholdParams::new(t, n, i).map_err(io::Error::other)?,
625 interpolation,
626 secret_share,
627 verification_shares,
628 )
629 .map_err(io::Error::other)
630 }
631}
632
633impl<C: Ciphersuite> ThresholdView<C> {
634 pub fn scalar(&self) -> C::F {
636 self.scalar
637 }
638
639 pub fn offset(&self) -> C::F {
641 self.offset
642 }
643
644 pub fn group_key(&self) -> C::G {
646 self.group_key
647 }
648
649 pub fn included(&self) -> &[Participant] {
651 &self.included
652 }
653
654 pub fn interpolation_factor(&self, participant: Participant) -> Option<C::F> {
656 if !self.included.contains(&participant) {
657 None?
658 }
659 Some(self.interpolation.interpolation_factor(participant, &self.included))
660 }
661
662 pub fn secret_share(&self) -> &Zeroizing<C::F> {
664 &self.secret_share
665 }
666
667 pub fn original_verification_share(&self, l: Participant) -> C::G {
671 self.original_verification_shares[&l]
672 }
673
674 pub fn verification_share(&self, l: Participant) -> C::G {
679 self.verification_shares[&l]
680 }
681}