Skip to content
Open
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
89 changes: 89 additions & 0 deletions docs/threshold_policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Threshold Policies & Security Assumptions

This document defines the **non-negotiable invariants** between participant count `N`, tolerated malicious parties `f`, reconstruction threshold `t`, and liveness requirements. All protocols in this repository **must** enforce these rules unless a future `SchemeRules` table explicitly allows an exception.

## Global Principle

**The tuple `(N, f, t)` chosen at key generation is fixed for all subprotocols**
(keygen → presign/offline → signing → refresh → resharing).
Any deviation **must be rejected** unless explicitly whitelisted by `SchemeRules`.
Comment on lines +7 to +9
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be accurate. During resharing we might need to change the threshold $t$, and with it $f$.
CC: @SimonRastikian @netrome

On a related note, in which cases $f = t - 1$ is not true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gilcu3 true (for current implementation).

I should've mentioned in a Planned policy:
once ThresholdPolicy/SchemeRules land ([step 3/4]), the default will be:

(N,f,t) stays fixed unless the SchemeRules entry for that scheme allows otherwise.


The What (Goal):

Make the constraints explicit and impossible to miss.

The How (Approach):

  • Modify APIs so that mismatched thresholds (e.g., refresh with different threshold) are rejected (step 2/4) unless explicitly intended and supported (step 3/4):
    • Introduce scheme-specific hardcoded rules over allowable threshold.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright, probably in the future PRs then it will make sense. Although also maybe worth asking, do we have a variable for $f$ because at some point that invariant $f = t-1$ might not hold anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet; today we only persist threshold and infer f = t − 1. To allow future schemes where t != f + 1, we should add explicit (N, f, t) metadata (ThresholdMeta) to keys/shares/transcripts and pass f (or at least store it alongside t) so the validator can enforce or relax the relation via SchemeRules.

Updated part of the document: link

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still torn by:

**The tuple `(N, f, t)` chosen at key generation is fixed for all subprotocols**  
(keygen → presign/offline → signing → refresh → resharing).

I would reduce it to presign/offline → signing → refresh as for keygen and specially resharing we need the ability to change the parameters in the tuple


## Why invariance is mandatory

Changing thresholds at any stage can silently weaken security (e.g., reducing signing quorum, increasing adversarial influence, or invalidating proofs from DKG). Recent industry incidents show this is a practical risk. The library therefore enforces all parameters unless explicitly allowed.

## ThresholdPolicy & SchemeRules

- **ThresholdPolicy:**
Defines which parameters (`N`, `f`, `t`, participant set) must remain invariant.
Default: **full invariance across all subprotocols**.

- **SchemeRules:**
Future, scheme-specific exceptions.
No exceptions exist today; all protocols must treat thresholds as fixed.

## Persistence Requirement

All keys, shares, or transcripts that leave the protocol must embed `(N, f, t)` so future API calls can enforce invariants even if the caller did not store them. Treat this as a `ThresholdMeta` record that every subprotocol must serialize and verify.

Keep `f` explicit even when `t = f + 1`. The current schemes tie `t` to `f`, but future `SchemeRules` may relax that relation; persisting both prevents silent downgrades if a protocol later allows `t != f + 1`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+Treat this as a `ThresholdMeta` record that every subprotocol must serialize and verify.

+Keep `f` explicit even when `t = f + 1`. The current schemes tie `t` to `f`, but future `SchemeRules` may relax that relation; persisting both prevents silent downgrades if a protocol later allows `t != f + 1`.

---

# Scheme-Specific Constraints

## Distributed Key Generation (DKG)

- **Fault tolerance:**
`f <= floor(N / 3)` due to asynchronous reliable broadcast.
- **Threshold:**
`t = f + 1` (design choice; distinct from `N - f`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean by "design choice; distinct from N - f"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I say the DKG uses a design choice, I mean its threshold is explicitly set to t = f + 1 because that’s the minimum needed to block f colluding adversaries from recovering the secret.
This is not the same as N - f, which is simply the worst-case count of honest parties.

They differ for a reason: tying t to N - f would force every honest participant to be involved in reconstruction. Using t = f + 1 avoids that—DKG security only depends on resisting f adversaries, not on the total number of honest nodes. It keeps the quorum minimal, avoids accidental assumptions about honesty counts, and gives better liveness under churn.

- **Required checks:**
- Reject if `f >= floor(N / 3)`.
- Reject if `t != f + 1`.
- **Invariance:**
DKG → refresh → resharing all reuse the exact `(N, f, t)`.

## OT-based ECDSA

- **Definitions:**
`f = max_malicious_parties`, `t = f + 1`.
- **Liveness:**
Signing/presigning requires `N_live >= t`.
- **Threshold consistency:**
Any mismatch between supplied `(f, t)` must be rejected.
- **Invariance:**
Keygen, triple generation, presign, sign, refresh all share the same `(f, t)`.

## Robust ECDSA (secret-sharing-based)

- **Parameterization:**
Scheme is defined by `f`; effective threshold derived from `f`.
- **Liveness:**
Signing requires `N_live >= 2f + 1`.
If this fails, resharing is required before signing.
- **Invariance:**
`f` remains constant across keygen, presign (if present), signing, refresh.
(Future SchemeRules may allow `N_live` to exceed offline `N`, but not implemented yet.)

---

# Refresh & Resharing Safety

- Thresholds must *not* be changed during refresh/resharing unless explicitly permitted by `SchemeRules`. Unsanctioned changes break the assumptions of all supported schemes.
- Threshold metadata must always be persisted together with key/share identifiers.

---

# Consequences of Misconfiguration

| Misconfiguration | Failure Mode |
| ------------------------------------------- | --------------------------------------------------------------------- |
| `f >= floor(N/3)` in DKG | Broadcast assumptions break; safety and liveness lost. |
| `t != f + 1` in DKG or OT-ECDSA | Scheme assumptions violated; security/liveness not guaranteed. |
| Threshold lowered during refresh | Old shares become over-powerful; confidentiality/unforgeability fail. |
| Robust ECDSA signing with `N_live < 2f + 1` | Protocol aborts or risks leakage/invalid signatures. |

---

If a scheme allows different parameters between subprotocols, the corresponding `SchemeRules` entry **must** document the exact conditions.