Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions cocktail-dkg-ineiti.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Comments from ineiti (Linus Gasser)

I'm a hobby cryptographer involved as a
software engineer in developing similar protocols like
[CoSi](https://arxiv.org/pdf/1602.06997) and
[Calypso](https://eprint.iacr.org/2018/209.pdf).
Currently I'm working on a research grant on evaluating and proposing
advanced cryptographic schemes for electronic identities:
[Secure and Privacy-Preserving Credentials for E-ID](https://github.com/eid-privacy)
This means that I have a pretty good grasp of elementar algebra and saw
more than one way for a protocol to fail.
This also means that I often follow a dead end and get distracted
too often by pretty things...
I hope these suggestions help, and I'm sorry I didn't read the
papers and RFCs...

Also, I wrote this between commits 19270381332689124e406069cf197d90ee16fed5
and 3a8cdae9a4598f724c5a79667ea4b3c24d0a34ec,
so some things might be moot now.

Without other indication, I base myself on the definitions in
[cocktail-dkg.md](./cocktail-dkg.md).

# DKG

This is probably a redundant explanation from the paper, but just to
make sure I get the basics right, this is what I base myself on:

## Input and Output

From a high level, I suppose that every participant has the following
inputs available to them, which have been received over a trusted
channel:

- $n$ public keys $P_1$ .. $P_n$
- the protocol $time$ as milliseconds since the Unix Epoch
- a threshold $t$ who can sign a message
- a minimal number $l$ of live participants needed to finish the protocol
with $t <= l <= n$

The goal of the protocol is to produce the following information:

- the $context = H( "COCKTAIL-DKG-V0.1" | P_1 | ... | P_n | time | t | l )$
- the group public key $Y$
- $n$ secret keys $x_i$, one for each participant $i$
- a common transcript $T = (context || Y || sig_{P_1}, \cdots, sig_{P_n})$

## Protocol Abort and Blame Assignment

If any of the participants detects a cryptographic error in
any of the messages related to a $context$, they must abort
the protocol. They must not accept any other message for this
$context$, including signing requests.
In case of an error from the coordinator, they must send the
$BlameProof$ to all other participants.
In case of an error from another participant, they must call
$CoBlame(context, BlameProof)$.

To make sure that blame assignment works in a reliable manner
without participants or the coordinator being able to blame
an honest entity, care must be taken that all messages are
signed and reliably assignable.
Compared to Cocktail-DKG this means that there are some more
signatures to avoid false blames.

A valid $BlameProof$ from participant $i$ looks like the following:

```math
Blame = (i || context || FaultyMessage || AdditionalInfo)
BlameProof = (Blame || Sig_{P_i}(Blame))
```

To verify a $BlameProof$, only the public keys $P_1, \cdots, P_n$
must be necessary.

The $AdditionalInfo$ is (probably) only used in `Round 2` of the
protocol, in case of a decryption failure, or share verification
failure. In that case, $AdditionalInfo = d_i$ WHICH IS OF COURSE
STUPID, BECAUSE IT'S THIS PARTICIPANTS LONG-TERM PRIVATE KEY!
So either there is another way to securely blame a decryption
failure, or the ephemeral keys must be exchanged beforehand,
and only these must be used for the ECDH.

## DKG Threat Model

For the participants and the coordinator, we consider the following threat model:

- the participants eventually send $msg_{1|i}$, the first step of the protocol
- a number $l$ of _live_ participants follow through with the whole
protocol
- they have limited computing capabilities which does not allow
them to find $x$ given $[ x ] B$
- they will not deviate from the protocol if they can be correctly blamed
(but will happily do so if an honest entity gets blamed)

With regard to the network:

- every message sent over the network eventually gets to the destination
- there is no authentication, encryption, or verification of the message
on the network
- the adversary can read and modify all messages on the network, but will
only do so if it doesn't lead the participants to abort the protocol or to
stall the protocol

## Communication Optimization

The protocol can work in a completely peer-to-peer fashion with
all the particpants communicating directly with each other.
However, this leads to $O(n^2)$ messages to finish the protocol
which can be prohibitive.
For this reason we allow for a coordinator with the following
set of operations.
They are kept generic to allow for different kinds of implementations
like a smart contract or a centralized server:

- $CoID$ is the identity of the coordinator which can be used
to verify proofs of the coordinator.
- $CoVerify(CoID, CoProof, msg)$ -> $()$ or $(Error)$ verifies if the
$msg$ corresponds to the $CoProof$ created by the $CoID$. If the
verification succeeds, it returns empty, else it returns an error.
- $CoStart(P_1, ..., P_n, time, t, l)$ -> $(context)$ or $(Error)$
starts a new DKG. The coordinator returns an error if one or more $P_i$
are invalid curve points, or if $t <= l <= n$ does not hold.
Else the coordinator stores the public
keys and thresholds and returns the $context$.
- $CoMsg1(context, i, msg_{1|i}, sig_{P_i})$ -> $()$ or $(Error)$
allows each participant to store their $msg_{1|i}$. The coordinator
returns an error if the context is not known, $i$ is out of bounds,
the $sig_{P_i}$ does not verify the $msg_{1|i}$, or all
$msg_{1|i}$ are already stored. Else the coordinator stores $msg_{1|i}$ and
$sig_{P_i}$.
- $CoMsg2(context, i)$ -> $(msg_{2|i}, CoProof_msg_{2|i})$ or $(Error)$
allows each participant to retrieve their respective $msg_{2|i}$
for the round 2 of the protocol. The coordinator returns an error if
the context is not known, $i$ is out of bounds, not all participants
sent their $msg_{1|i}$, or a valid $BlameProof$ exists.
Else the coordinator returns $msg_{2|i}$ and the $CoProof_msg_{2|i}$.
- $CoFinalize(context, i, sig_{P_i})$ -> $()$ or $(Error)$
allows each participant to indicate that they successfully finished
the round 2 of the protocol. The coordinator returns an error if the context
is not known, $i$ is out of bounds, the $sig_{P_i}$ does not
verify the message $(context | Y)$, or a valid $BlameProof$ exists.
In case of success it returns empty.
- $CoTranscript(context)$ -> $(T, CoProof_transcript)$ or $(Error)$
allows anybody to get the transcript
of the corresponding context. The coordinator returns an error if
the $context$ is not known, less than $l$ participants finalized
the protocol, or if a valid $BlameProof$ exists.
Else it returns the group public key and at least $l$ valid signatures.
- $CoBlame(context, BlameProof)$ -> $()$ or $(Error)$ aborts the
protocol in case of a valid $BlameProof$. The coordinator returns
an error if the $context$ is not known, or if the $BlameProof$ is
invalid.

### Blockchain

In case of a blockchain, $CoID$ represents the blockchain identity, and
$CoVerify$ must be able to verify that the $CoProof$ is actually
stored somewhere on the blockchain.
The $CoProof$ must point to the transaction and the block where the proof
can be found.
A smart contract can implement the different messages shown here, with the
caveat that the cryptographic operations might be very expensive,
depending on the type of smart contracts available.

### Central Server

For a central server, $CoID$ is the public key of the server,
$CoProof$ is a signature on $msg$, and $CoVerify$ is a signature verification
which returns either an error or an empty result if the verification
is successful.

# Comments and questions

- The document should use section-# and subsection-#, so it would be easier
to reference comments in a document like this :)
- In the $msg2$ I don't understand the $C_{agg}$. I'm used to see "Aggregated"
in elliptic curves as the sum of several elliptic curve points, but here it seems
to be a list of $t-1$ points. However, I don't see how the participants
in round 2 can then use the $C_{j,k}$ if they only have the sum of the points.
So shouldn't the $msg_2$ have all $C_j$ in it?
- Also for the $msg2$, which I changed to $msg_{2|i}$: In the `Protocol Messages`, you
write that the coordinator returns the $msg_{2|i}$ to participant $i$. However, as
described above, this message is not sufficient for participant $i$ to finish
round 2 of the protocol. In the `Round 2` you state that the participants receive
all $msg_{1|i}$ from all other participants, which is too much, as most of the
encrypted shares will not be able to be decrypted. I propose to redefine
$msg_{2|i}$ as follows:

```math
msg_{2|i} = (C_1 || \cdots || C_n) || (PoP_1 || \cdots || Pop_n) || E_1 || \cdots || E_n || c_{1,i} || \cdots || c_{n,i}
```

with all indexes $1 \cdots n$ excluding $i$. And then `Round 2` can state that
each participant fetches $msg_{2|i}$ from the coordinator.

- I would put a visible link to the actual signing protocol!
40 changes: 24 additions & 16 deletions cocktail-dkg.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[c2sp.org/cocktail-dkg](https://c2sp.org/cocktail-dkg)

- **Version**: v0.0.1
- **Authors**:
- **Authors**:
- [Daniel Bourdrez](https://github.com/bytemare)
- [Soatok Dreamseeker](https://github.com/soatok)
- [Tjaden Hess](https://github.com/tjade273), *[Trail of Bits](https://trailofbits.com)*
Expand Down Expand Up @@ -41,7 +41,7 @@ COCKTAIL is an independent derivative of ChillDKG intended to be used with any F

## Abstract

COCKTAIL-DKG is a standalone, three-round distributed key generation protocol for threshold signature schemes like
COCKTAIL-DKG is a standalone, three-round distributed key generation protocol for threshold signature schemes like
FROST.

COCKTAIL-DKG allows a group of $n$ participants to securely generate a shared group public key and individual secret
Expand Down Expand Up @@ -90,6 +90,7 @@ throughout the COCKTAIL-DKG protocol.
- $x_i$: The final, long-lived secret share for participant $i$, where $x_i = \sum_{j=1}^{n} s_{j,i}$.
- $Y_i$: The public verification share for participant $i$.
- $Y$: The final group public key, where $Y = \sum_{j=1}^{n} C_{j,0}$.
- $T$: The transcript of the DKG session.

### Operations

Expand Down Expand Up @@ -131,7 +132,7 @@ encrypted shares for all other participants.
* Format: $C_{i,0} || C_{i,1} || \cdots || C_{i,t-1}$
* $PoP_i$: The Proof of Possession, which is a signature. The size depends on the signature scheme used by the
ciphersuite.
* $E_i$: The ephemeral public key, an elliptic curve point.
* $E_i$: The ephemeral public key, an elliptic curve point.
* It does not refer to isogenies. Here, E stands for "ephemeral".
* $c_{i,j}$: An encrypted secret share. The size depends on the AEAD scheme used by the ciphersuite.

Expand All @@ -141,7 +142,7 @@ The full message is the concatenation of these elements:
msg_{1|i} = C_i || PoP_i || E_i || c_{i,1} || c_{i,2} || \cdots || c_{i,n}
```

**2. $msg2$ (Coordinator -> All Participants, Round 2)**
**2. $msg_{2|i}$ (Coordinator -> All Participants, Round 2)**

This message aggregates the public information from all participants.

Expand Down Expand Up @@ -214,6 +215,7 @@ Each participant $i$ is assumed to have:
using the secret $a_{i,0}$ as the private key and $C_{i,0}$ as the public key. The message to be signed is
`context || C_i || E_i`. The specific signature algorithm is defined by the ciphersuite.
5. **Compute and Encrypt Shares:** For each participant $j$ from $1$ to $n$:
<<<<<<< HEAD
1. **Compute Share:** Participant `i` computes the secret share $s_{i,j} = f_i(j)$.
2. **Derive Key:** Participant `i` computes an ECDH shared secret with participant $j$'s static public key:
$S_{i,j} = e_i * P_j$. It then derives a symmetric key and nonce for the AEAD.
Expand All @@ -226,21 +228,27 @@ Each participant $i$ is assumed to have:
* $k_{i,j} = H("COCKTAIL-derive-key" || ikm)$
* $iv_{i,j} = H("COCKTAIL-derive-nonce" || ikm)[0:24]$
* Here, $H(x)$ is the underlying hash function (e.g., SHA-256).
=======
1. **Compute Share:** Participant $i$ computes the secret share $s_{i,j} = f_i(j)$.
2. **Derive Key:** Participant $i$ computes an ECDH shared secret with participant $j$'s static public key:
$S_{i,j} = e_i * P_j$. It then derives a symmetric key and nonce for the AEAD:
$(k_{i,j}, iv_{i,j}) = H6(S_{i,j}, E_i, P_j, context)$.
>>>>>>> a6797b1 (More comments on the protocol)
3. **Encrypt Share:** Participant $i$ encrypts the share for participant $j$:
$c_{i,j} = Enc(s_{i,j}, k_{i,j}, iv_{i,j})$.
6. **Broadcast:** Participant $i$ sends their $msg_{1|i}$ to the coordinator.

### Round 2: Share Decryption and Verification

The coordinator waits to receive $msg_{1|i}$ from all $n$ participants. It then broadcasts a list of all received messages
to every participant. Upon receiving the list of all $msg_{1|i}$ messages, each participant `i` performs the following
to every participant. Upon receiving the list of all $msg_{1|i}$ messages, each participant $i$ performs the following
steps:

1. **Verify All PoPs:** For each other participant $j$ from $1$ to $n$:
1. **Verify All PoPs:** For each participant $j \neq i$ from $1$ to $n$:
- Participant $i$ verifies the proof of possession $PoP_j$. The signature is checked against the message
`context || C_j || E_j`, using participant $j$'s public commitment $C_{j,0}$ as the public key.
- If any $PoP_j$ is invalid, participant $i$ **MUST** abort, identifying participant $j$ as malicious.
2. **Decrypt and Verify Shares:** For each other participant $j$ from $1$ to $n$:
2. **Decrypt and Verify Shares:** For each participant $j \neq i$ from $1$ to $n$:
1. **Derive Key:** Participant $i$ computes the ECDH shared secret with participant $j$'s ephemeral public key:
$S_{j,i} = d_i * E_j$. They then derive the symmetric key and nonce:
* If the hash function has an output length at least 480 bits long:
Expand Down Expand Up @@ -324,7 +332,7 @@ The following categories cover the most common errors:
3. **Protocol Logic Errors**:
* **Description**: These errors relate to violations of the protocol's state machine or rules, such as:
* A participant sending a message at the wrong time.
* The coordinator broadcasting an inconsistent `msg2` (e.g., omitting a participant's data).
* The coordinator broadcasting an inconsistent $msg_{2|i}$ (e.g., omitting a participant's data).
* **Action**: These errors indicate a faulty participant or coordinator. The protocol **MUST** be aborted.
If the error can be traced to a specific participant, they should be blamed.

Expand All @@ -341,7 +349,7 @@ accountability in decentralized systems.
* **Participant's Role and Public Proofs**: Participants **MUST** validate all data they receive.
* If participant $i$ fails to verify a share $s_{j,i}$ from participant $j$, it **MUST** abort. To prove that $j$
is cheating, participant $i$ can broadcast a blame message containing $j$'s index and the invalid share $s_{j,i}$.
Any third party can then verify this claim by checking the VSS equation
Any third party can then verify this claim by checking the VSS equation
($s_{j,i} \cdot B = \sum_{k=0}^{t-1} i^k \cdot C_{j,k}$)
using the public commitment $C_j$. A failure of this equation is a public and undeniable proof of $j$'s misbehavior.
* Similarly, if a PoP from participant $j$ is invalid, this is also a publicly verifiable proof of misbehavior,
Expand All @@ -358,7 +366,7 @@ accountability in decentralized systems.
broadcasting it, the PoP will fail for all other participants, who will blame $j$. However, in Round 3,
participant $j$ will construct a transcript based on their *original*, valid $msg_{1|j}$. Their signature $sig_j$
will be valid for their transcript but not for the altered transcript held by others. When this signature
mismatch is detected, participant $j$ can reveal their original $msg_{1|j}$ (with its valid PoP) and their
mismatch is detected, participant $j$ can reveal their original $msg_{1|j}$ (with its valid PoP) and their
transcript signature. This evidence proves their honesty and definitively identifies the coordinator as malicious.

## Ciphersuites
Expand All @@ -370,12 +378,12 @@ Each ciphersuite defines a key derivation function $H6(x, pk1, pk2, extra)$, an
and a decryption method $Dec(cipher, key, iv)$. Ciphersuites **SHOULD** use an AEAD mode for $Enc()$ and $Dec()$.

The choice of AEAD is guided by the principle of preventing nonce reuse. For ciphersuites where the underlying hash
function provides a large enough output (at least 480 bits; e.g., SHA-512), we can derive both the 256-bit key and a
function provides a large enough output (at least 480 bits; e.g., SHA-512), we can derive both the 256-bit key and a
24-byte (192-bit) nonce (which is long enough to be generated randomly with a negligible chance of collision).

For ciphersuites based on SHA-256, where the output is smaller than 480 bits, we use $H6()$ to derive an Input Keying
Material (IKM), which is then used with the underlying hash function with two different prefixes. For the key, we use
$Sha256("COCKTAIL-derive-key" || ikm)$. For the nonce, we use the most significant 192 bits of
$Sha256("COCKTAIL-derive-key" || ikm)$. For the nonce, we use the most significant 192 bits of
$Sha256("COCKTAIL-derive-nonce" || ikm)$. The AEAD of choice for the SHA-256 based ciphersuites we specify here is
[XAES-256-GCM](https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md).

Expand Down Expand Up @@ -463,9 +471,9 @@ The output of $H6$ is used to derive the key and nonce for the AEAD.
confidentiality against an eavesdropper on the communication channel and authenticity to prevent a man-in-the-middle
from tampering with the shares. The key is derived using an ECDH key exchange, ensuring that only the intended
recipient can decrypt their share.
- **Cofactor Security**: As noted in the [Ciphersuite-Specific Considerations](#ciphersuite-specific-considerations)
- **Cofactor Security**: As noted in the [Ciphersuite-Specific Considerations](#ciphersuite-specific-considerations)
section, curves like Ed25519 and Ed448 have small cofactors. It is critical that implementations use prime-order group
abstractions like Ristretto255 and Decaf448 to prevent small subgroup attacks, where an attacker could submit a
abstractions like Ristretto255 and Decaf448 to prevent small subgroup attacks, where an attacker could submit a
low-order point to leak information.
- **Participant Authentication**: Throughout the protocol, participants are authenticated to each other via their
long-term static key pairs. The pairwise ECDH key agreement used to encrypt shares in Round 1 provides deniable
Expand Down Expand Up @@ -509,7 +517,7 @@ will be too.
participants, which can be complex to set up. COCKTAIL-DKG removes this requirement by building in its own encryption
layer (EncPedPop), making it usable over insecure, unauthenticated channels.

# Appendix A: Pseudocode
# Appendix A: Pseudocode

This appendix provides a series of algorithms that describe the COCKTAIL-DKG protocol in a high-level,
implementation-agnostic manner. The notation is meant to be illustrative rather than strictly formal.
Expand Down Expand Up @@ -657,7 +665,7 @@ function VerifyShare(s_ji, i, C_j, G, t):
- ikm = H6(ecdh_secret, E_i, P_j, context)
- key = H("COCKTAIL-derive-key" || ikm)
- nonce = H("COCKTAIL-derive-nonce" || ikm)[0:24]
- If the hash function used has an output size greater than equal to 480 bits, just split them:
- If the hash function used has an output size greater than equal to 480 bits, just split them:
- tmp = H6(ecdh_secret, E_i, P_j, context)
- key = tmp[0:32] (32 bytes)
- nonce = tmp[32:46] (24 bytes)
Expand Down