Skip to content

Commit

Permalink
python: Add blame mode (identifiable aborts)
Browse files Browse the repository at this point in the history
  • Loading branch information
real-or-random committed Oct 18, 2024
1 parent 53959ce commit 2db8e49
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 53 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ Perform a participant's first step of a ChillDKG session.
#### participant\_step2

```python
def participant_step2(hostseckey: bytes, state1: ParticipantState1, cmsg1: CoordinatorMsg1) -> Tuple[ParticipantState2, ParticipantMsg2]
def participant_step2(hostseckey: bytes, state1: ParticipantState1, cmsg1: CoordinatorMsg1, blame_rec: Optional[encpedpop.BlameRecord]) -> Tuple[ParticipantState2, ParticipantMsg2]
```

Perform a participant's second step of a ChillDKG session.
Expand All @@ -779,6 +779,7 @@ Perform a participant's second step of a ChillDKG session.
*Raises*:

- `SecKeyError` - If the length of `hostseckey` is not 32 bytes.
FIXME
- `FaultyParticipantError` - If `cmsg1` is invalid. This can happen if
another participant has sent an invalid message to the coordinator,
or if the coordinator has sent an invalid `cmsg1`.
Expand Down Expand Up @@ -837,7 +838,7 @@ of the success of the DKG session by presenting recovery data to us.
#### coordinator\_step1

```python
def coordinator_step1(pmsgs1: List[ParticipantMsg1], params: SessionParams) -> Tuple[CoordinatorState, CoordinatorMsg1]
def coordinator_step1(pmsgs1: List[ParticipantMsg1], params: SessionParams, blame: bool = True) -> Tuple[CoordinatorState, CoordinatorMsg1, List[Optional[encpedpop.BlameRecord]]]
```

Perform the coordinator's first step of a ChillDKG session.
Expand Down
18 changes: 13 additions & 5 deletions python/chilldkg_ref/chilldkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ def participant_step2(
hostseckey: bytes,
state1: ParticipantState1,
cmsg1: CoordinatorMsg1,
blame_rec: Optional[encpedpop.BlameRecord],
) -> Tuple[ParticipantState2, ParticipantMsg2]:
"""Perform a participant's second step of a ChillDKG session.
Expand All @@ -448,6 +449,7 @@ def participant_step2(
Raises:
SecKeyError: If the length of `hostseckey` is not 32 bytes.
FIXME
FaultyParticipantError: If `cmsg1` is invalid. This can happen if
another participant has sent an invalid message to the coordinator,
or if the coordinator has sent an invalid `cmsg1`.
Expand All @@ -468,6 +470,7 @@ def participant_step2(
deckey=hostseckey,
cmsg=enc_cmsg,
enc_secshare=enc_secshares[idx],
blame_rec=blame_rec,
)
# Include the enc_shares in eq_input to ensure that participants agree on all
# shares, which in turn ensures that they have the right recovery data.
Expand Down Expand Up @@ -531,8 +534,8 @@ class CoordinatorState(NamedTuple):


def coordinator_step1(
pmsgs1: List[ParticipantMsg1], params: SessionParams
) -> Tuple[CoordinatorState, CoordinatorMsg1]:
pmsgs1: List[ParticipantMsg1], params: SessionParams, blame: bool = True
) -> Tuple[CoordinatorState, CoordinatorMsg1, List[Optional[encpedpop.BlameRecord]]]:
"""Perform the coordinator's first step of a ChillDKG session.
Arguments:
Expand All @@ -555,14 +558,19 @@ def coordinator_step1(
params_validate(params)
(hostpubkeys, t) = params

enc_cmsg, enc_dkg_output, eq_input, enc_secshares = encpedpop.coordinator_step(
pmsgs=[pmsg1.enc_pmsg for pmsg1 in pmsgs1], t=t, enckeys=hostpubkeys
enc_cmsg, enc_dkg_output, eq_input, enc_secshares, blame_recs = (
encpedpop.coordinator_step(
pmsgs=[pmsg1.enc_pmsg for pmsg1 in pmsgs1],
t=t,
enckeys=hostpubkeys,
blame=blame,
)
)
eq_input += b"".join([bytes_from_int(int(share)) for share in enc_secshares])
dkg_output = DKGOutput._make(enc_dkg_output) # Convert to chilldkg.DKGOutput type
state = CoordinatorState(params, eq_input, dkg_output)
cmsg1 = CoordinatorMsg1(enc_cmsg, enc_secshares)
return state, cmsg1
return state, cmsg1, blame_recs


def coordinator_finalize(
Expand Down
91 changes: 82 additions & 9 deletions python/chilldkg_ref/encpedpop.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Tuple, List, NamedTuple
from typing import Tuple, List, NamedTuple, Optional, cast

from secp256k1proto.secp256k1 import Scalar
from secp256k1proto.secp256k1 import Scalar, GE
from secp256k1proto.ecdh import ecdh_libsecp256k1
from secp256k1proto.keys import pubkey_gen_plain
from secp256k1proto.util import int_from_bytes
Expand Down Expand Up @@ -159,6 +159,11 @@ class CoordinatorMsg(NamedTuple):
pubnonces: List[bytes]


class BlameRecord(NamedTuple):
enc_partial_secshares: List[Scalar]
partial_pubshares: List[GE]


###
### Participant
###
Expand Down Expand Up @@ -223,9 +228,11 @@ def participant_step2(
deckey: bytes,
cmsg: CoordinatorMsg,
enc_secshare: Scalar,
blame_rec: Optional[BlameRecord] = None,
) -> Tuple[simplpedpop.DKGOutput, bytes]:
simpl_state, pubnonce, enckeys, idx = state
simpl_cmsg, pubnonces = cmsg
n = len(enckeys)

reported_pubnonce = pubnonces[idx]
if reported_pubnonce != pubnonce:
Expand All @@ -235,9 +242,38 @@ def participant_step2(
secshare = decrypt_sum(
deckey, enckeys[idx], pubnonces, enc_context, idx, enc_secshare
)
dkg_output, eq_input = simplpedpop.participant_step2(
simpl_state, simpl_cmsg, secshare
)

if blame_rec is not None:
enc_partial_secshares, partial_pubshares = blame_rec
partial_secshares = [
decrypt(
deckey,
enckeys[idx],
pubnonces[i],
enc_context,
idx,
i,
enc_partial_secshares[i],
)
for i in range(n)
]
simpl_blame_rec = simplpedpop.BlameRecord(partial_secshares, partial_pubshares)
else:
simpl_blame_rec = None

try:
dkg_output, eq_input = simplpedpop.participant_step2(
simpl_state, simpl_cmsg, secshare, simpl_blame_rec
)
except simplpedpop.InconsistentSecsharesError as e:
# The secshare is not equal to the sum of the partial secshares in the
# blame records. Since the encryption is additively homomorphic, this
# can only happen if the sum of the *encrypted* secshare is not equal
# to the sum of the encrypted partial sechares.
assert Scalar.sum(*enc_partial_secshares) != enc_secshare
raise FaultyCoordinatorError(
"Sum of encrypted partial secshares not equal to encrypted secshare"
) from e
eq_input += b"".join(enckeys) + b"".join(pubnonces)
return dkg_output, eq_input

Expand All @@ -251,12 +287,19 @@ def coordinator_step(
pmsgs: List[ParticipantMsg],
t: int,
enckeys: List[bytes],
) -> Tuple[CoordinatorMsg, simplpedpop.DKGOutput, bytes, List[Scalar]]:
blame: bool = True,
) -> Tuple[
CoordinatorMsg,
simplpedpop.DKGOutput,
bytes,
List[Scalar],
List[Optional[BlameRecord]],
]:
n = len(enckeys)
if n != len(pmsgs):
raise ValueError
simpl_cmsg, dkg_output, eq_input = simplpedpop.coordinator_step(
[pmsg.simpl_pmsg for pmsg in pmsgs], t, n
simpl_cmsg, dkg_output, eq_input, all_partial_pubshares = (
simplpedpop.coordinator_step([pmsg.simpl_pmsg for pmsg in pmsgs], t, n, blame)
)
pubnonces = [pmsg.pubnonce for pmsg in pmsgs]
for i in range(n):
Expand All @@ -267,6 +310,30 @@ def coordinator_step(
enc_secshares = [
Scalar.sum(*([pmsg.enc_shares[i] for pmsg in pmsgs])) for i in range(n)
]

blame_recs: List[Optional[BlameRecord]]
if blame:
# We called simplpedpop.coordinator_step(..., blame=True), which is
# supposed to return proper partial_pubshares.
assert all(
[
all_partial_pubshares[i][j] is not None
for i in range(n)
for j in range(n)
]
)
all_enc_partial_secshares = [
[pmsg.enc_shares[i] for pmsg in pmsgs] for i in range(n)
]
blame_recs = [
BlameRecord(
all_enc_partial_secshares[i], cast(List[GE], all_partial_pubshares[i])
)
for i in range(n)
]
else:
blame_recs = [None for i in range(n)]

eq_input += b"".join(enckeys) + b"".join(pubnonces)
# In ChillDKG, the coordinator needs to broadcast the entire enc_secshares
# array to all participants. But in pure EncPedPop, the coordinator needs to
Expand All @@ -277,4 +344,10 @@ def coordinator_step(
# chilldkg.coordinator_step can pick it up. Implementations of pure
# EncPedPop will need to decide how to transmit enc_secshares[i] to
# participant i; we leave this unspecified.
return CoordinatorMsg(simpl_cmsg, pubnonces), dkg_output, eq_input, enc_secshares
return (
CoordinatorMsg(simpl_cmsg, pubnonces),
dkg_output,
eq_input,
enc_secshares,
blame_recs,
)
Loading

0 comments on commit 2db8e49

Please sign in to comment.