1#![allow(non_snake_case, clippy::too_many_arguments)]
9#![forbid(missing_docs)]
10#![no_std]
11
12extern crate alloc;
13#[cfg(feature = "std")]
14extern crate std;
15
16pub mod progress;
17pub mod security_level;
18
19mod non_threshold;
21mod threshold;
23
24mod errors;
25mod execution_id;
26mod utils;
27
28use alloc::vec::Vec;
29
30use digest::Digest;
31use generic_ec::Curve;
32use rand_core::{CryptoRng, RngCore};
33use round_based::{Mpc, MsgId, PartyIndex};
34
35#[doc(inline)]
36pub use key_share;
37
38use crate::progress::Tracer;
39use crate::{
40 errors::IoError,
41 key_share::{CoreKeyShare, InvalidCoreShare},
42 security_level::SecurityLevel,
43};
44
45pub use self::execution_id::ExecutionId;
46#[doc(no_inline)]
47pub use self::msg::{non_threshold::Msg as NonThresholdMsg, threshold::Msg as ThresholdMsg};
48
49mod default_choice {
51 pub type Digest = sha2::Sha256;
52 pub type SecurityLevel = crate::security_level::SecurityLevel128;
53}
54
55#[doc = include_str!("../docs/mpc_message.md")]
56pub mod msg {
57 pub mod non_threshold {
59 pub use crate::non_threshold::{Msg, MsgReliabilityCheck, MsgRound1, MsgRound2, MsgRound3};
60 }
61 pub mod threshold {
63 pub use crate::threshold::{
64 Msg, MsgReliabilityCheck, MsgRound1, MsgRound2Broad, MsgRound2Uni, MsgRound3,
65 };
66 }
67}
68
69pub type KeygenBuilder<
74 'a,
75 E,
76 L = crate::default_choice::SecurityLevel,
77 D = crate::default_choice::Digest,
78> = GenericKeygenBuilder<'a, E, NonThreshold, L, D>;
79
80pub type ThresholdKeygenBuilder<
82 'a,
83 E,
84 L = crate::default_choice::SecurityLevel,
85 D = crate::default_choice::Digest,
86> = GenericKeygenBuilder<'a, E, WithThreshold, L, D>;
87
88pub struct GenericKeygenBuilder<'a, E: Curve, M, L: SecurityLevel, D: Digest> {
91 i: u16,
92 n: u16,
93 reliable_broadcast_enforced: bool,
94 optional_t: M,
95 execution_id: ExecutionId<'a>,
96 tracer: Option<&'a mut dyn Tracer>,
97 #[cfg(feature = "hd-wallet")]
98 hd_enabled: bool,
99 _params: core::marker::PhantomData<(E, L, D)>,
100}
101
102pub struct NonThreshold;
104pub struct WithThreshold(u16);
106
107impl<'a, E, L, D> GenericKeygenBuilder<'a, E, NonThreshold, L, D>
108where
109 E: Curve,
110 L: SecurityLevel,
111 D: Digest + Clone + 'static,
112{
113 pub fn new(eid: ExecutionId<'a>, i: u16, n: u16) -> Self {
117 Self {
118 i,
119 n,
120 optional_t: NonThreshold,
121 reliable_broadcast_enforced: true,
122 execution_id: eid,
123 tracer: None,
124 #[cfg(feature = "hd-wallet")]
125 hd_enabled: true,
126 _params: core::marker::PhantomData,
127 }
128 }
129}
130
131impl<'a, E, L, D, M> GenericKeygenBuilder<'a, E, M, L, D>
132where
133 E: Curve,
134 L: SecurityLevel,
135 D: Digest + Clone + 'static,
136{
137 pub fn set_threshold(self, t: u16) -> GenericKeygenBuilder<'a, E, WithThreshold, L, D> {
139 GenericKeygenBuilder {
140 i: self.i,
141 n: self.n,
142 optional_t: WithThreshold(t),
143 reliable_broadcast_enforced: self.reliable_broadcast_enforced,
144 execution_id: self.execution_id,
145 tracer: self.tracer,
146 #[cfg(feature = "hd-wallet")]
147 hd_enabled: self.hd_enabled,
148 _params: core::marker::PhantomData,
149 }
150 }
151 pub fn set_digest<D2>(self) -> GenericKeygenBuilder<'a, E, M, L, D2>
153 where
154 D2: Digest + Clone + 'static,
155 {
156 GenericKeygenBuilder {
157 i: self.i,
158 n: self.n,
159 optional_t: self.optional_t,
160 reliable_broadcast_enforced: self.reliable_broadcast_enforced,
161 execution_id: self.execution_id,
162 tracer: self.tracer,
163 #[cfg(feature = "hd-wallet")]
164 hd_enabled: self.hd_enabled,
165 _params: core::marker::PhantomData,
166 }
167 }
168
169 pub fn set_security_level<L2>(self) -> GenericKeygenBuilder<'a, E, M, L2, D>
171 where
172 L2: SecurityLevel,
173 {
174 GenericKeygenBuilder {
175 i: self.i,
176 n: self.n,
177 optional_t: self.optional_t,
178 reliable_broadcast_enforced: self.reliable_broadcast_enforced,
179 execution_id: self.execution_id,
180 tracer: self.tracer,
181 #[cfg(feature = "hd-wallet")]
182 hd_enabled: self.hd_enabled,
183 _params: core::marker::PhantomData,
184 }
185 }
186
187 pub fn set_progress_tracer(mut self, tracer: &'a mut dyn Tracer) -> Self {
189 self.tracer = Some(tracer);
190 self
191 }
192
193 #[doc = include_str!("../docs/enforce_reliable_broadcast.md")]
194 pub fn enforce_reliable_broadcast(self, enforce: bool) -> Self {
195 Self {
196 reliable_broadcast_enforced: enforce,
197 ..self
198 }
199 }
200
201 #[cfg(feature = "hd-wallet")]
202 pub fn hd_wallet(mut self, v: bool) -> Self {
204 self.hd_enabled = v;
205 self
206 }
207}
208
209impl<'a, E, L, D> GenericKeygenBuilder<'a, E, NonThreshold, L, D>
210where
211 E: Curve,
212 L: SecurityLevel,
213 D: Digest + Clone + 'static,
214{
215 pub async fn start<R, M>(self, rng: &mut R, party: M) -> Result<CoreKeyShare<E>, KeygenError>
217 where
218 R: RngCore + CryptoRng,
219 M: Mpc<ProtocolMessage = non_threshold::Msg<E, L, D>>,
220 {
221 non_threshold::run_keygen(
222 self.tracer,
223 self.i,
224 self.n,
225 self.reliable_broadcast_enforced,
226 self.execution_id,
227 rng,
228 party,
229 #[cfg(feature = "hd-wallet")]
230 self.hd_enabled,
231 )
232 .await
233 }
234
235 #[cfg(feature = "state-machine")]
239 pub fn into_state_machine<R>(
240 self,
241 rng: &'a mut R,
242 ) -> impl round_based::state_machine::StateMachine<
243 Output = Result<CoreKeyShare<E>, KeygenError>,
244 Msg = non_threshold::Msg<E, L, D>,
245 > + 'a
246 where
247 R: RngCore + CryptoRng,
248 {
249 round_based::state_machine::wrap_protocol(|party| self.start(rng, party))
250 }
251}
252
253impl<'a, E, L, D> GenericKeygenBuilder<'a, E, WithThreshold, L, D>
254where
255 E: Curve,
256 L: SecurityLevel,
257 D: Digest + Clone + 'static,
258{
259 pub async fn start<R, M>(self, rng: &mut R, party: M) -> Result<CoreKeyShare<E>, KeygenError>
261 where
262 R: RngCore + CryptoRng,
263 M: Mpc<ProtocolMessage = threshold::Msg<E, L, D>>,
264 {
265 threshold::run_threshold_keygen(
266 self.tracer,
267 self.i,
268 self.optional_t.0,
269 self.n,
270 self.reliable_broadcast_enforced,
271 self.execution_id,
272 rng,
273 party,
274 #[cfg(feature = "hd-wallet")]
275 self.hd_enabled,
276 )
277 .await
278 }
279
280 #[cfg(feature = "state-machine")]
284 pub fn into_state_machine<R>(
285 self,
286 rng: &'a mut R,
287 ) -> impl round_based::state_machine::StateMachine<
288 Output = Result<CoreKeyShare<E>, KeygenError>,
289 Msg = threshold::Msg<E, L, D>,
290 > + 'a
291 where
292 R: RngCore + CryptoRng,
293 {
294 round_based::state_machine::wrap_protocol(|party| self.start(rng, party))
295 }
296}
297
298#[derive(Debug, displaydoc::Display)]
300#[cfg_attr(feature = "std", derive(thiserror::Error))]
301#[displaydoc("keygen protocol is failed to complete")]
302pub struct KeygenError(#[cfg_attr(feature = "std", source)] Reason);
303
304crate::errors::impl_from! {
305 impl From for KeygenError {
306 err: KeygenAborted => KeygenError(Reason::Aborted(err)),
307 err: IoError => KeygenError(Reason::IoError(err)),
308 err: Bug => KeygenError(Reason::Bug(err)),
309 }
310}
311
312#[derive(Debug, displaydoc::Display)]
313#[cfg_attr(feature = "std", derive(thiserror::Error))]
314enum Reason {
315 #[displaydoc("protocol was aborted by malicious party")]
317 Aborted(#[cfg_attr(feature = "std", source)] KeygenAborted),
318 #[displaydoc("i/o error")]
319 IoError(#[cfg_attr(feature = "std", source)] IoError),
320 #[displaydoc("bug occurred")]
322 Bug(Bug),
323}
324
325impl From<KeygenAborted> for Reason {
326 fn from(err: KeygenAborted) -> Self {
327 Reason::Aborted(err)
328 }
329}
330
331#[derive(Debug, displaydoc::Display)]
335#[cfg_attr(feature = "std", derive(thiserror::Error))]
336enum KeygenAborted {
337 #[displaydoc("party decommitment doesn't match commitment: {0:?}")]
338 InvalidDecommitment(Vec<utils::AbortBlame>),
339 #[displaydoc("party provided invalid schnorr proof: {0:?}")]
340 InvalidSchnorrProof(Vec<utils::AbortBlame>),
341 #[displaydoc("party secret share is not consistent: {parties:?}")]
342 FeldmanVerificationFailed { parties: Vec<u16> },
343 #[displaydoc("party data size is not suitable for threshold parameters: {parties:?}")]
344 InvalidDataSize { parties: Vec<u16> },
345 #[displaydoc("round1 wasn't reliable")]
346 Round1NotReliable(Vec<(PartyIndex, MsgId)>),
347 #[cfg(feature = "hd-wallet")]
348 #[displaydoc("party did not generate chain code: {0:?}")]
349 MissingChainCode(Vec<utils::AbortBlame>),
350}
351
352#[derive(Debug, displaydoc::Display)]
353#[cfg_attr(feature = "std", derive(thiserror::Error))]
354enum Bug {
355 #[displaydoc("resulting key share is not valid")]
356 InvalidKeyShare(#[cfg_attr(feature = "std", source)] InvalidCoreShare),
357 #[displaydoc("unexpected zero value")]
358 NonZeroScalar,
359 #[cfg(feature = "hd-wallet")]
360 #[displaydoc("chain code is missing although we checked that it should be present")]
361 NoChainCode,
362 #[displaydoc("key share of one of the signers is zero - probability of that is negligible")]
363 ZeroShare,
364 #[displaydoc("shared public key is zero - probability of that is negligible")]
365 ZeroPk,
366}
367
368pub fn keygen<E: Curve>(eid: ExecutionId, i: u16, n: u16) -> KeygenBuilder<E> {
373 KeygenBuilder::new(eid, i, n)
374}