Skip to content

Commit

Permalink
WIP python: Cache blame() inputs, piggyback them on exception
Browse files Browse the repository at this point in the history
  • Loading branch information
real-or-random committed Nov 7, 2024
1 parent 4c2852f commit ee5d5c6
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ of the success of the DKG session by presenting recovery data to us.
#### participant\_blame

```python
def participant_blame(hostseckey: bytes, state1: ParticipantState1, cmsg1: CoordinatorMsg1, cblame: CoordinatorBlameMsg) -> NoReturn
def participant_blame(state1: ParticipantState1, blame_state: ParticipantBlameState, cmsg1: CoordinatorMsg1, cblame: CoordinatorBlameMsg) -> NoReturn
```

Perform a participant's blame step of a ChillDKG session. TODO
Expand Down
9 changes: 6 additions & 3 deletions python/chilldkg_ref/chilldkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ class ParticipantState2(NamedTuple):
dkg_output: DKGOutput


class ParticipantBlameState(NamedTuple):
enc_blame_state: encpedpop.ParticipantBlameState


def participant_step1(
hostseckey: bytes, params: SessionParams, random: bytes
) -> Tuple[ParticipantState1, ParticipantMsg1]:
Expand Down Expand Up @@ -529,17 +533,16 @@ def participant_finalize(


def participant_blame(
hostseckey: bytes,
state1: ParticipantState1,
blame_state: ParticipantBlameState,
cmsg1: CoordinatorMsg1,
cblame: CoordinatorBlameMsg,
) -> NoReturn:
"""Perform a participant's blame step of a ChillDKG session. TODO"""
_, idx, enc_state = state1
encpedpop.participant_blame(
state=enc_state,
deckey=hostseckey,
cmsg=cmsg1.enc_cmsg,
blame_state=blame_state.enc_blame_state,
enc_secshare=cmsg1.enc_secshares[idx],
cblame=cblame.enc_cblame,
)
Expand Down
38 changes: 21 additions & 17 deletions python/chilldkg_ref/encpedpop.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from . import simplpedpop
from .util import (
UnknownFaultyPartyError,
tagged_hash_bip_dkg,
prf,
FaultyParticipantOrCoordinatorError,
Expand Down Expand Up @@ -162,6 +163,12 @@ class ParticipantState(NamedTuple):
idx: int


class ParticipantBlameState(NamedTuple):
simpl_bstate: simplpedpop.ParticipantBlameState
pads: List[Scalar]
secshare: Scalar


def serialize_enc_context(t: int, enckeys: List[bytes]) -> bytes:
# TODO Consider hashing the result here because the string can be long, and
# we'll feed it into hashes on multiple occasions
Expand Down Expand Up @@ -223,33 +230,30 @@ def participant_step2(
raise FaultyCoordinatorError("Coordinator replied with wrong pubnonce")

enc_context = serialize_enc_context(simpl_state.t, enckeys)
secshare = decrypt_sum(
deckey, enckeys[idx], pubnonces, enc_context, idx, enc_secshare
)
pads = decaps_multi(deckey, enckeys[idx], pubnonces, enc_context, idx)
secshare = enc_secshare - Scalar.sum(*pads)

try:
dkg_output, eq_input = simplpedpop.participant_step2(
simpl_state, simpl_cmsg, secshare
)
except UnknownFaultyPartyError[simplpedpop.ParticipantBlameState] as simpl_e:
blame_state = ParticipantBlameState(simpl_e.blame_state, pads, secshare)
raise UnknownFaultyPartyError[ParticipantBlameState](blame_state) from simpl_e

dkg_output, eq_input = simplpedpop.participant_step2(
simpl_state, simpl_cmsg, secshare
)
eq_input += b"".join(enckeys) + b"".join(pubnonces)
return dkg_output, eq_input


def participant_blame(
state: ParticipantState,
deckey: bytes,
cmsg: CoordinatorMsg,
blame_state: ParticipantBlameState,
enc_secshare: Scalar,
cblame: CoordinatorBlameMsg,
) -> NoReturn:
simpl_state, _, enckeys, idx = state
_, pubnonces = cmsg
simpl_state, _, _, _ = state
simpl_blame_state, pads, secshare = blame_state
enc_partial_secshares, partial_pubshares = cblame

# Compute the encryption pads once and use them to decrypt both the
# enc_secshare and all enc_partial_secshares
enc_context = serialize_enc_context(simpl_state.t, enckeys)
pads = decaps_multi(deckey, enckeys[idx], pubnonces, enc_context, idx)
secshare = enc_secshare - Scalar.sum(*pads)
partial_secshares = [
enc_partial_secshare - pad
for enc_partial_secshare, pad in zip(enc_partial_secshares, pads, strict=True)
Expand All @@ -258,7 +262,7 @@ def participant_blame(
simpl_cblame = simplpedpop.CoordinatorBlameMsg(partial_pubshares)
try:
simplpedpop.participant_blame(
simpl_state, secshare, partial_secshares, simpl_cblame
simpl_state, simpl_blame_state, secshare, partial_secshares, simpl_cblame
)
except simplpedpop.SecshareSumError as e:
# The secshare is not equal to the sum of the partial secshares in the
Expand Down
26 changes: 17 additions & 9 deletions python/chilldkg_ref/simplpedpop.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ class ParticipantState(NamedTuple):
com_to_secret: GE


class ParticipantBlameState(NamedTuple):
pubshare: GE


# To keep the algorithms of SimplPedPop and EncPedPop purely non-interactive
# computations, we omit explicit invocations of an interactive equality check
# protocol. ChillDKG will take care of invoking the equality check protocol.
Expand Down Expand Up @@ -200,7 +204,8 @@ def participant_step2(
pubshare = sum_coms.pubshare(idx)

if not VSSCommitment.verify_secshare(secshare, pubshare):
raise UnknownFaultyPartyError(
raise UnknownFaultyPartyError[ParticipantBlameState](
ParticipantBlameState(pubshare),
"Received invalid secshare, consider blaming to determine faulty party",
)

Expand All @@ -216,13 +221,18 @@ def participant_step2(

def participant_blame(
state: ParticipantState,
blame_state: ParticipantBlameState,
secshare: Scalar,
partial_secshares: List[Scalar],
cblame: CoordinatorBlameMsg,
) -> NoReturn:
_, n, idx, _ = state
(pubshare,) = blame_state
partial_pubshares = cblame.partial_pubshares

if GE.sum(*partial_pubshares) != pubshare:
raise FaultyCoordinatorError("Sum of partial pubshares not equal to pubshare")

if Scalar.sum(*partial_secshares) != secshare:
raise SecshareSumError("Sum of partial secshares not equal to secshare")

Expand All @@ -242,15 +252,13 @@ def participant_blame(

# We now know:
# - The sum of the partial secshares is equal to the secshare.
# - The sum of the partial pubshares is equal to the pubshare.
# - Every partial secshare matches its corresponding partial pubshare.
# - The secshare does not match the pubshare (because the caller shouldn't
# have called us otherwise).
# Therefore, the sum of the partial pubshares is not equal to the pubshare,
# and this is the coordinator's fault.
raise FaultyCoordinatorError(
"Sum of partial pubshares not equal to pubshare (or participant_blame() "
"was called even though participant_step2() was successful)"
)
# Hence, the secshare matches the pubshare.
assert VSSCommitment.verify_secshare(secshare, pubshare)

# This should never happen (unless the caller fiddled with the inputs).
raise RuntimeError("participant_blame() was called, but all inputs are consistent.")


###
Expand Down
11 changes: 8 additions & 3 deletions python/chilldkg_ref/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any
from typing import Any, TypeVar, Generic

from secp256k1proto.util import tagged_hash

Expand Down Expand Up @@ -55,5 +55,10 @@ class FaultyCoordinatorError(ProtocolError):
"""


class UnknownFaultyPartyError(ProtocolError):
pass
S = TypeVar("S")


class UnknownFaultyPartyError(ProtocolError, Generic[S]):
def __init__(self, blame_state: S, *args: Any):
self.blame_state = blame_state
super().__init__(*args)

0 comments on commit ee5d5c6

Please sign in to comment.