-
Notifications
You must be signed in to change notification settings - Fork 2
Simon/documenting dkg #254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7795a36
87d74e5
89f2648
bb1b693
4d7524f
71e5fcd
c0c2c69
a96fcfa
5164cbc
f48520f
825ec33
c6b36fe
44c0d74
c1dc443
dd9e4bd
209644b
0479b92
8d4f59e
54dcb0d
6da1bf2
dde507d
9c54fd9
4db8fb7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| # Distributed Key Generation | ||
|
|
||
| We define a variant of the two-round DKG protocol PedPop \[[KG](https://eprint.iacr.org/2020/852.pdf)\]. | ||
| Our variant, PedPop+ is less efficient, but achieves a notion of simulatability with aborts, | ||
| a stronger notion of security than the one promised by plain PedPop. | ||
|
|
||
| PedPop+ is a five rounds protocol and makes use in three of its rounds of a reliable broadcast channel. A reliable broadcast is a 3 round protocol, | ||
SimonRastikian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| implying that the total number of PedPop+ rounds 11. The broadcast channel is implemented in `src/protocol/echo_broadcast.rs`. | ||
|
|
||
| The implemented DKG serves as a generic one that can be used with multiple different underlying elliptic curves. We thus use it with `Secp256k1` for ECDSA schemes, `Curve25519` for EdDSA scheme, and `BLS12_381` for the confidential key derivation functionality. | ||
|
|
||
| ## Keygen, Reshare and Refresh | ||
|
|
||
| The core of the dkg protocol is implemented in a function called `do_keyshare` and serves for three applications: | ||
|
|
||
| * Key generation: denoted in the implementation with `keygen`. It allows a set of parties to jointly generate from scratch a private key share each and a master public key. The master public key should be common for all the participants and should reflect each of the private shares. | ||
|
|
||
| * Key resharing: denoted in the implementation with `reshare`. It allows for a set of participants who already own valid private shares to kick away other participants from the pool, create fresh shares for new participants i.e. new joiners to the pool, and/or change the **cryptographic threshold** described in section [Types of Thresholds](#types-of-thresholds). | ||
|
|
||
| * Key refresh: denoted in the implementation with `refresh`. It is a special case of the key resharing in which the set of participants stays the same before and after the protocol run and with no changes to the crypto. The goal being that each participant would refresh their signing share without modifying the master public key. | ||
|
|
||
| ## Types of Thresholds | ||
|
|
||
| There are two types of thresholds one has to be aware of: the **asynchronous distributed systems threshold** a.k.a. the **BFT threshold**, and the **cryptography threshold** a.k.a. the **reconstruction threshold**. | ||
|
|
||
| The BFT threshold states that the maximum number of faulty nodes a distributed system ($\mathsf{MaxFaulty}$) can tolerate while still reaching consensus is at most one-third of the total number of participants $N$. More specifically: | ||
|
Comment on lines
+22
to
+26
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I like that we're getting into the threshold types here but I wonder if it's relevant for the DKG algorithm. Also, I think we take the reconstruction bound as input in the code but we talk abut max malicious here. Is that correct? I wonder if we should consider migrating to use max malicious as input to the dkg protocol in the future (which is a bit of a tricky change to do safely).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I see that the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Could you pinpoint the spot in the implementation where we talk about reconstruction bound? |
||
|
|
||
| $$\mathsf{MaxFaulty} \leq \frac{N - 1}{3}$$ | ||
|
|
||
| The cryptography threshold refers to the maximum number of necessay malicious parties ($\mathsf{MaxMalicious}$) a certain scheme can handle without compromising on the security and assuming the existance of an underlying reliable broadcast channel. $\mathsf{MaxMalicious}$ is scheme dependent and can have a different value than $\mathsf{MaxFaulty}$. For instance, in the OT based ECDSA, $\mathsf{MaxMalicious}$ can be up to $N-1$, but in Robust ECDSA scheme $\mathsf{MaxMalicious}$ must not exceed $\frac{N - 1}{2}$. | ||
|
|
||
| ### DKG and thresholds | ||
|
|
||
| Due to the fact that PedPop+ utilizes reliable broadcast channel to securely generate private shares, it thus lies on the edge between the asynchronous distributed systems and cryptography. For this reason, we set | ||
| $\mathsf{MaxFaulty} = \frac{N - 1}{3}$ as an invariable parameter and allow our key generation and key resharing protocols to fix/modify only the $\mathsf{MaxMalicious}$ threshold depending on the scheme requirements and on the library user's choice. | ||
|
|
||
| ## Technical Details: Key Generation & Key Resharing | ||
|
|
||
| Let $P_1, \cdots P_N$ be $N$ different participants, and $\mathsf{MaxMalicious}$ be the desired cryptography threshold. Let $H_1, H_2, H_3$ be domain separated hash functions. | ||
|
|
||
| We define PedPop+ key generation as follows; All the instruction preceeded with `+++` are added to the key generation, transforming it to the key resharing protocol. | ||
|
|
||
| No special inputs are given to the **key generation** protocol beyond the public parameters defined above. However, the inputs to the **key resharing** are as follows: | ||
|
|
||
| 1. `+++` The old private share $\mathit{OldSK}$ that $P_i$ held prior to the key resharing. This value is set to None only if $P_i$ is a freshly new participant. | ||
|
|
||
| 2. `+++` The old participants set $\mathit{OldSigners}$ that held valid private shares prior to the key resharing. | ||
|
|
||
| 3. `+++` The old master public key $\mathit{OldPK}$ that the $\mathit{OldSigners}$ held prior to the key resharing. | ||
|
|
||
| 4. `+++` The old cryptography threshold $\mathsf{OldMaxMalicious}$ prior to the key resharing. | ||
| `` | ||
|
|
||
| ### Round 1 | ||
|
|
||
| 1.1 Each $P_i$ asserts that $1 < \mathsf{MaxMalicious} < N$. | ||
|
|
||
| $\quad$ `+++` Each $P_i$ sets $I \gets \set{P_1 \ldots P_N} \cap \mathit{OldSigners}$ | ||
|
|
||
| $\quad$ `+++` Each $P_i$ asserts that $\mathsf{OldMaxMalicious} \leq |I|$. | ||
|
|
||
| 1.2 Each $P_i$ generates a random 32-byte sesssion identifier $\mathit{sid}_i$ | ||
|
|
||
| 1.3 Each $P_i$ reliably broadcasts $\mathit{sid}_i$ | ||
|
|
||
| ### Round 2 | ||
|
|
||
| 2.1 Each $P_i$ waits to receive $\mathit{sid}_j$ from every participant $P_j$ | ||
|
|
||
| 2.2 Each $P_i$ computes the hash $\mathit{sid} \gets H_1(\mathit{sid}_1, \cdots \mathit{sid}_N)$ | ||
|
|
||
| 2.3 Each $P_i$ generates a random polynomial $f_i$ of degree $\mathsf{MaxMalicious}$. | ||
|
|
||
|
|
||
| $\quad$ `+++` Each $P_i$ computes the following: | ||
|
|
||
| $\quad$ `+++` If $P_i\notin \mathit{OldSigners}$ then set $f_i(0) \gets 0$ | ||
|
|
||
| $\quad$ `+++` Else set $f_i(0) \gets \lambda_i(I) \cdot \mathit{OldSK}$ | ||
|
|
||
|
|
||
| 2.4 Each $P_i$ generates a commitment of the polynomial $C_i \gets f_i \cdot G$ (commits every coefficient of the polynomial). | ||
|
|
||
| 2.5 Each $P_i$ generates a hash $h_i \gets H_2(i, C_i, \mathit{sid})$ | ||
|
|
||
| 2.6 Each $P_i$ picks a random nonce $k_i$ and computes $R_i \gets k_i \cdot G$ | ||
|
|
||
| 2.7 Each $P_i$ computes the Schnorr challenge $c_i \gets H_3(\mathit{sid}, i, C_i(0), R_i)$ | ||
|
|
||
| 2.8 Each $P_i$ computes the proof $s_i \gets k_i + f_i(0) \cdot c_i$ | ||
|
|
||
| 2.9 Each $P_i$ sends $h_i$ to every participant | ||
|
|
||
| ### Round 3 | ||
|
|
||
| 3.1 Each $P_i$ waits to receive $h_i$ from every participant $P_j$. | ||
|
|
||
| 3.2 Each $P_i$ reliably broadcasts $(C_i, R_i, s_i)$. | ||
|
|
||
| ### Round 4 | ||
|
|
||
| 4.1 Each $P_i$ waits to receive $(C_j, \pi_j)$ from every participant $P_j$. | ||
|
|
||
| 4.2 Each $P_i$ computes: $\forall j\in\set{1, \cdots N}, \quad c_j \gets H_3(\mathit{sid}, j, C_j(0), R_j)$. | ||
|
|
||
| 4.3 Each $P_i$ asserts that: $\forall j\in\set{1, \cdots N}, \quad R_j = s_i \cdot G - c_j \cdot C_j(0)$. | ||
|
|
||
| 4.4 Each $P_i$ asserts that: $\forall j\in\set{1, \cdots N}, \quad h_j = H_2(j, C_j, \mathit{sid})$. | ||
|
|
||
| 4.5 $\textcolor{red}{\star}$ Each $P_i$ **privately** sends the evaluation $f_i(j)$ to every participant $P_j$. | ||
|
|
||
| ### Round 5 | ||
|
|
||
| 5.1 Each $P_i$ waits to receive $f_j(i)$ from every participant $P_j$. | ||
|
|
||
| 5.2 Each $P_i$ asserts that: $\forall j\in\set{1, \cdots N}, \quad f_j(i) \cdot G = \sum_m j^m \cdot C_j[m]$ where $C_j[m]$ denotes the m-th coefficient of $C_j$. | ||
|
|
||
| 5.3 Each $P_i$ computes its private share $\mathit{sk}_i \gets \sum_j f_j(i)$. | ||
|
|
||
| 5.4 Each $P_i$ computes the master public key $\mathit{pk} \gets \sum_j C_j(0)$. | ||
|
|
||
| $\quad$ `+++` Each $P_i$ asserts that $\mathit{pk} = \mathit{OldPK}$ | ||
|
|
||
| 5.5 Each $P_i$ reliably broadcasts $\mathsf{success_i}$. | ||
|
|
||
| #### Round 5.5 | ||
|
|
||
| 5.6 Each $P_i$ waits to receive $\mathsf{success_j}$ from every participant $P_j$. | ||
|
|
||
| **Output:** the keypair $(\mathit{sk}_i, \mathit{pk})$. | ||
|
|
||
|
|
||
| ### Key Refresh | ||
|
|
||
| A key refresh protocol is a special case of the key resharing where $\mathit{OldSigners} = \set{P_1, \ldots P_N}$ and where $\mathsf{OldMaxMalicious} = \mathsf{MaxMalicious}$ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,10 +69,10 @@ fn generate_coefficient_commitment<C: Ciphersuite>( | |
| } | ||
|
|
||
| /// Generates the challenge for the proof of knowledge | ||
| /// H(id, `context_string`, g^{secret} , R) | ||
| /// H(`domain_separator`, id, g^{secret} , R) | ||
| fn challenge<C: Ciphersuite>( | ||
| session_id: &HashOutput, | ||
| domain_separator: u32, | ||
| session_id: &HashOutput, | ||
SimonRastikian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| id: Scalar<C>, | ||
|
Comment on lines
-72
to
76
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it strange that the domain separator is of type
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reasoning behind this?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we use it only as bytes later for example. And one of the important characteristics of domain separators is exactly that they have fixed width, so why have them as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the logic you would have then when you want to update the domain separator? Currently we add +1
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting, so this is a domain separator that is a counter, I couldn't guess that from reviewing this local change. I couldn´t not find an explicit mention of that in the doc. Is it used to separate
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The doc should be agnostic of how the domain separation happens.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As long as it is used correctly right? I looked at the code, and the main logic happens in I think we should do the same, mainly because it looks more standard and robust, but probably not in this PR, could be a relatively easy follow-up. If you agree, could you open an issue for it? |
||
| vk_share: &CoefficientCommitment<C>, | ||
| big_r: &Element<C>, | ||
|
|
@@ -128,8 +128,8 @@ fn proof_of_knowledge<C: Ciphersuite>( | |
| // pick a random k_i and compute R_id = g^{k_id}, | ||
| let (k, big_r) = <C>::generate_nonce(rng); | ||
|
|
||
| // compute H(id, context_string, g^{a_0} , R_id) as a scalar | ||
| let hash = challenge::<C>(session_id, domain_separator, id, &vk_share, &big_r)?; | ||
| // compute H(domain_separator, id, me, g^{a_0}, R_id) as a scalar | ||
| let hash = challenge::<C>(domain_separator, session_id, id, &vk_share, &big_r)?; | ||
| let a_0 = coefficients.eval_at_zero()?.0; | ||
| let mu = k + a_0 * hash.to_scalar(); | ||
| Ok(Signature::new(big_r, mu)) | ||
|
|
@@ -153,7 +153,7 @@ fn internal_verify_proof_of_knowledge<C: Ciphersuite>( | |
|
|
||
| let big_r = proof_of_knowledge.R(); | ||
| let z = proof_of_knowledge.z(); | ||
| let c = challenge::<C>(session_id, domain_separator, id, vk_share, big_r)?; | ||
| let c = challenge::<C>(domain_separator, session_id, id, vk_share, big_r)?; | ||
| if *big_r != <C::Group>::generator() * *z - vk_share.value() * c.to_scalar() { | ||
| return Err(ProtocolError::InvalidProofOfKnowledge(participant)); | ||
| } | ||
|
|
@@ -348,12 +348,12 @@ async fn do_keyshare<C: Ciphersuite>( | |
| let (old_verification_key, old_participants) = | ||
| assert_keyshare_inputs(me, &secret, old_reshare_package)?; | ||
|
|
||
| // Start Round 0 | ||
| // Start Round 1 | ||
| let mut my_session_id = [0u8; 32]; // 256 bits | ||
| rng.fill_bytes(&mut my_session_id); | ||
| let session_ids = do_broadcast(&mut chan, &participants, me, my_session_id).await?; | ||
|
|
||
| // Start Round 1 | ||
| // Start Round 2 | ||
| // generate your secret polynomial p with the constant term set to the secret | ||
| // and the rest of the coefficients are picked at random | ||
| // because the library does not allow serializing the zero and identity term, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.