Skip to content

Commit

Permalink
chilldkg: Allow recovery of the coordinator
Browse files Browse the repository at this point in the history
  • Loading branch information
real-or-random committed May 16, 2024
1 parent da3945b commit ec82958
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 50 deletions.
95 changes: 52 additions & 43 deletions reference/chilldkg.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Reference implementation of BIP DKG.
from typing import Tuple, List, NamedTuple, NewType
from typing import Tuple, List, NamedTuple, NewType, Optional

from secp256k1ref.secp256k1 import Scalar
from secp256k1ref.bip340 import schnorr_sign, schnorr_verify
Expand Down Expand Up @@ -241,48 +241,6 @@ async def participant(
return participant_finalize(state2, cmsg2)


# Recovery requires the seed and the public recovery data
def participant_recover(
seed: bytes, recovery: RecoveryData, context_string: bytes
) -> Tuple[DKGOutput, SessionParams]:
try:
(t, sum_vss_commit, hostpubkeys, enc_shares_sums, cert) = (
deserialize_recovery_data(recovery)
)
except DeserializationError as e:
raise InvalidRecoveryDataError("Failed to deserialize recovery data") from e

n = len(hostpubkeys)
(params, params_id) = session_params(hostpubkeys, t, context_string)

# Verify cert
certifying_eq_verify(hostpubkeys, recovery[: 64 * n], cert)

# Find our hostpubkey
hostseckey, hostpubkey = hostkey_gen(seed)
try:
idx = hostpubkeys.index(hostpubkey)
except ValueError as e:
raise InvalidRecoveryDataError("Seed and recovery data don't match") from e

# Decrypt share
seed_, enc_context = encpedpop.session_seed(seed, hostpubkeys, t)
shares_sum = encpedpop.decrypt_sum(
enc_shares_sums[idx], hostseckey, hostpubkeys, idx, enc_context
)

# Derive self_share
vss = VSS.generate(seed_, t)
self_share = vss.share_for(idx)
shares_sum += self_share

# Compute threshold pubkey and individual pubshares
(threshold_pubkey, pubshares) = common_dkg_output(sum_vss_commit, n)

dkg_output = DKGOutput(shares_sum, threshold_pubkey, pubshares)
return dkg_output, params


###
### Coordinator
###
Expand Down Expand Up @@ -335,3 +293,54 @@ async def coordinator(
chans.send_all(cmsg2)

return dkg_output, recovery_data


###
### Recovery
###


# Recovery requires the seed (can be None if recovering the coordinator) and the
# public recovery data
def recover(
seed: Optional[bytes], recovery: RecoveryData, context_string: bytes
) -> Tuple[DKGOutput, SessionParams]:
try:
(t, sum_vss_commit, hostpubkeys, enc_shares_sums, cert) = (
deserialize_recovery_data(recovery)
)
except DeserializationError as e:
raise InvalidRecoveryDataError("Failed to deserialize recovery data") from e

n = len(hostpubkeys)
(params, params_id) = session_params(hostpubkeys, t, context_string)

# Verify cert
certifying_eq_verify(hostpubkeys, recovery[: 64 * n], cert)

if seed:
# Find our hostpubkey
hostseckey, hostpubkey = hostkey_gen(seed)
try:
idx = hostpubkeys.index(hostpubkey)
except ValueError as e:
raise InvalidRecoveryDataError("Seed and recovery data don't match") from e

# Decrypt share
seed_, enc_context = encpedpop.session_seed(seed, hostpubkeys, t)
shares_sum = encpedpop.decrypt_sum(
enc_shares_sums[idx], hostseckey, hostpubkeys, idx, enc_context
)

# Derive self_share
vss = VSS.generate(seed_, t)
self_share = vss.share_for(idx)
shares_sum += self_share
else:
shares_sum = None

# Compute threshold pubkey and individual pubshares
(threshold_pubkey, pubshares) = common_dkg_output(sum_vss_commit, n)

dkg_output = DKGOutput(shares_sum, threshold_pubkey, pubshares)
return dkg_output, params
2 changes: 1 addition & 1 deletion reference/encpedpop.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class ParticipantState(NamedTuple):
simpl_state: simplpedpop.ParticipantState # TODO Move up?


def session_seed(seed, enckeys, t):
def session_seed(seed: bytes, enckeys: List[bytes], t: int) -> Tuple[bytes, bytes]:
enc_context = t.to_bytes(4, byteorder="big") + b"".join(enckeys)
seed_ = tagged_hash_bip_dkg("EncPedPop seed", seed + enc_context)
return seed_, enc_context
Expand Down
1 change: 1 addition & 0 deletions reference/simplpedpop.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def participant_pre_finalize(
# Sum the commitments to the i-th coefficients from the given vss_commitments
# for i > 0. This procedure is introduced by Pedersen in section 5.1 of
# 'Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing'.
# TODO Should this be called coordinator_pre_finalize? (same in encpedpop)
def coordinator_step(
pmsgs: List[ParticipantMsg], t: int, n: int
) -> Tuple[CoordinatorMsg, DKGOutput, bytes]:
Expand Down
12 changes: 6 additions & 6 deletions reference/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ def test_correctness_dkg_output(t, n, dkg_outputs: List[simplpedpop.DKGOutput]):


def test_correctness(t, n, simulate_dkg, recovery=False):
seeds = [secrets.token_bytes(32) for _ in range(n)]
seeds = [None] + [secrets.token_bytes(32) for _ in range(n)]

# rets[0] are the return values from the coordinator
# rets[1 : n + 1] are from the participants
rets = simulate_dkg(seeds, t)
rets = simulate_dkg(seeds[1:], t)
assert len(rets) == n + 1

dkg_outputs = [ret[0] for ret in rets]
Expand All @@ -215,10 +215,10 @@ def test_correctness(t, n, simulate_dkg, recovery=False):

if recovery:
rec = eqs_or_recs[0]
# test correctness of chilldkg_recover
for i in range(1, n + 1):
(secshare, threshold_pubkey, pubshares), _ = chilldkg.participant_recover(
seeds[i - 1], rec, b""
# test correctness of chilldkg.recover
for i in range(0, n + 1):
(secshare, threshold_pubkey, pubshares), _ = chilldkg.recover(
seeds[i], rec, b""
)
assert secshare == dkg_outputs[i][0]
assert threshold_pubkey == dkg_outputs[i][1]
Expand Down

0 comments on commit ec82958

Please sign in to comment.