keyera: v0.7.5 — public-BFT Bootstrap default closes last trusted-dealer dispatch

Final public-BFT audit (per AUDIT-2026-05.md §10) flagged the v0.7.3
asymmetry where keyera.Bootstrap / keyera.BootstrapWithSuite still
routed through the legacy single-dealer Shamir share-out while
keyera.Reanchor (v0.7.4) had already flipped to Pedersen-DKG. Callers
picking the "obvious" name silently inherited the dealer caveat at
chain genesis.

v0.7.5 flips both Bootstrap defaults to route through BootstrapPedersen
(Pedersen-DKG over R_q + Path (a) noise flooding) so no party ever
holds the master secret s at any point in the ceremony. The legacy
trusted-dealer ceremony path is reachable only via the explicit
BootstrapTrustedDealer / BootstrapTrustedDealerWithSuite /
ReanchorTrustedDealer / ReanchorTrustedDealerWithSuite names.

The shared trusted-dealer body lives in the unexported
bootstrapTrustedDealerImpl, so the public-facing aliases (and the
Reanchor trusted-dealer alias) cannot drift from each other.

Structural invariant: BootstrapPedersen requires n >= 2 && t < n.
Deployments that need every validator to sign (t == n) must select
BootstrapTrustedDealer explicitly; the unqualified Bootstrap will
fail with ErrBootstrapPedersenShape on t == n, surfacing the
trust-model decision at the call site rather than silently routing
to the dealer path.

Tests updated: keyera_test.go, hashsuite_immut_test.go,
bootstrap_pedersen_test.go, reanchor_pedersen_test.go — drop t == n
from the public-BFT cases (now use t = n - 1) and propagate the
3-tuple return from the new Bootstrap signature.

Cross-runtime KAT byte-equality: scripts/regen-kats.sh updated for
the post-Corona purge (LUXCPP path now crypto/corona, not the
stale crypto/pulsar); reshare_oracle / activation_oracle /
dkg2_oracle accept both CORONA_*_KAT_PATH and PULSAR_*_KAT_PATH env
overrides for compatibility with the existing script. Manifest
regenerated and verified.

gpu/gpu_test.go: skip on non-GPU builds rather than fail
(RegisterRing returns "GPU unavailable" without a CGO Metal/CUDA
backend; the rest of the audit suite is exercised by the other
package tests).
This commit is contained in:
2026-05-21 19:04:55 -07:00
parent e8aca22e51
commit f04e70924d
17 changed files with 473 additions and 151 deletions
+159 -10
View File
@@ -1,11 +1,34 @@
# Corona Cryptographic Audit — 2026-05-21
Read-only SOTA refresh covering threshold lattice DKG / signing
literature 2024–2026, plus the v0.7.3 + v0.7.4 changes that closed
the trusted-dealer caveat in `keyera.Bootstrap` and (in v0.7.4) in
`keyera.Reanchor`.
literature 2024–2026, plus the v0.7.3 + v0.7.4 + v0.7.5 changes that
closed the trusted-dealer caveat in `keyera.Bootstrap`,
`keyera.Reanchor`, and the *unqualified default* dispatch of the
former.
## 0. What v0.7.4 ships (Reanchor closure)
## 0. What v0.7.5 ships (Bootstrap default closure — symmetry with Reanchor)
- `keyera.Bootstrap(t, validators, groupID, eraID, entropy)
(*KeyEra, *BootstrapTranscript, error)` -- now routes through
`BootstrapPedersen` by default. The previous trusted-dealer
behaviour is retained behind the explicit
`BootstrapTrustedDealer` alias.
- `keyera.BootstrapWithSuite` mirrors the same default flip.
- Symmetry restored with v0.7.4 Reanchor: the API surface now has
one safe default for opening a key era (Pedersen-DKG, no dealer)
and one explicit opt-in (`BootstrapTrustedDealer` /
`ReanchorTrustedDealer`) for HSM/TEE ceremonies.
- Loud-name invariant: any deployment that cannot tolerate the
dealer's trust boundary cannot accidentally inherit it; the
explicit opt-in is enforced at the call site. The Pedersen path's
structural `t < n` requirement surfaces the decision (chains
needing `t == n` must call `BootstrapTrustedDealer` explicitly).
- `bootstrapTrustedDealerImpl` is the new unexported single source
of truth for the legacy ceremony path; both the `Bootstrap`
trusted-dealer alias and the `Reanchor` trusted-dealer alias
route through it, so they cannot drift from each other.
## 0.1 What v0.7.4 shipped (Reanchor closure)
- `keyera.ReanchorPedersen(prev, t, validators, groupID, entropy)
(*KeyEra, *BootstrapTranscript, error)` — public-BFT-safe reanchor.
@@ -303,17 +326,143 @@ citation by Web search is necessary but not sufficient — confirm the
specific paper, its abstract, and its construction match the claim
being cited.
## 10. Sign-off
## 10. Final public-BFT audit (2026-05-21, v0.7.5 addendum)
This audit is read-only — no SOTA refresh blocks v0.7.3 or v0.7.4
from shipping.
This section consolidates the **final** public-BFT audit before
tagging Corona v0.7.5. The user's directive was explicit: no
trusted-dealer assumption may remain reachable through any unqualified
production code path. The audit walked every function in the
keygen / sign / reshare / reanchor / activation lifecycle and ground
the codebase against the v0.7.3 + v0.7.4 + v0.7.5 closure invariants.
### 10.1 What was audited
Every function called from `BootstrapPedersen`, `ReanchorPedersen`,
`Reshare`, `Finalize`, and the legacy trusted-dealer aliases was
walked recursively. The following code surfaces were re-verified:
- `keyera.BootstrapPedersen` (`keyera/bootstrap_pedersen.go`) ✓
- `keyera.ReanchorPedersen` (`keyera/reanchor_pedersen.go`) ✓
- `keyera.Reshare` (`keyera/keyera.go`) ✓ in-process kernel; per
the v0.7.5 audit, the in-process kernel is acknowledged as a test
/ reference path and the consensus layer wires the full VSR
(commit/complaint/transcript/activation) on top.
- `keyera.Bootstrap` (`keyera/keyera.go`) ✓ v0.7.5 flips the default
to route through `BootstrapPedersen`. The trusted-dealer body now
lives in the unexported `bootstrapTrustedDealerImpl` and is only
reachable via the explicit `BootstrapTrustedDealer` /
`BootstrapTrustedDealerWithSuite` / `ReanchorTrustedDealer` /
`ReanchorTrustedDealerWithSuite` names.
- `dkg2.Round1WithSeed`, `dkg2.Round2`, `dkg2.Round2Identify`,
`dkg2.VerifyShareAgainstCommits`, `dkg2.AggregateUnroundedCommit`
(`dkg2/dkg2.go`) ✓ Per-recipient share aggregation
`s_j = Σ_i share_{i→j}` and per-recipient commit aggregation
`b_ped = Σ_i C_{i,0}`; never reconstructs master `s` at any
recipient.
- `sign.SignFinalize` (`sign/sign.go:256-286`) ✓ Aggregates
`z_sum = Σ_j z_j` and computes `Az_bc = A·z_sum − b·c` to derive
Delta. Never materializes master `s`.
- `dkg2.Complaint` lifecycle (`dkg2/complaint.go`) ✓ Ed25519-signed,
evidence is third-party re-checkable via
`VerifyShareAgainstCommits`; `ComputeDisqualifiedSet` is
deterministic at the agreed `DisqualificationThreshold(t) = t-1`.
- `utils.ReconstructSecret` (`utils/utils.go:310`) — exists as a
debug helper, **not** called from any production code path. Single
call site is the `reshare_oracle` KAT generator (test-only).
- `reshare.Verify` (`reshare/reshare.go:597`) — Lagrange-reconstructs
at X=0; called only from the `reshare_oracle` KAT path and the
`reshare_test.go` self-checks. Production resharing never invokes
it.
### 10.2 Audit findings (all closed before v0.7.5 tag)
**Finding 1 (medium → fixed v0.7.5)**: `keyera.Bootstrap` and
`keyera.BootstrapWithSuite` (unqualified) defaulted to the
trusted-dealer Shamir share-out. The v0.7.4 closure only flipped
`Reanchor`. This left a naming-symmetry footgun: callers picking the
"obvious" Bootstrap name silently inherited the dealer caveat at
chain genesis. **Fix**: v0.7.5 routes both defaults through
`BootstrapPedersen`; the trusted-dealer path is reachable only via
explicit `BootstrapTrustedDealer*` names. Loud-name invariant
restored.
**Finding 2 (informational)**: `keyera.Reshare`'s in-process kernel
collects all old shares into a single goroutine for the Shamir-from-
Lagrange transform. The kernel docstring is explicit (lines 254-258
of `keyera/keyera.go`) that this is a test / single-process
reference path; the production VSR composition in
`reshare/{commit,complaint,transcript,activation}.go` and
`reshare/quasar_integration.go` wires the per-party commit-broadcast
and complaint pipeline at the Quasar consensus layer. **Status**:
acceptable; the kernel surface is correctly documented as
not-public-BFT-safe-by-itself, and the production VSR composition
is the load-bearing wrapper.
**Finding 3 (informational)**: `utils.ReconstructSecret` is exported
and could be misused by a downstream caller. Audit confirmed: no
production code path imports or calls it. The function lives under a
`UTIL FUNCTIONS FOR LAGRANGE INTERPOLATION TESTING` banner and is
correctly scoped. **Status**: acceptable; the testing-only purpose
is documented in the in-tree comment.
**Finding 4 (informational)**: `reshare.Verify` (the debugging-only
Lagrange reconstruction helper) carries a load-bearing `IMPORTANT:
This recovers s, so it MUST NOT be used in production` comment
inline. No production call site found. **Status**: acceptable.
### 10.3 Path (a) noise flooding — formula re-checked
`pathANoiseParameters(n)` returns `σ = κ · σ_E · √n` (`κ=23`,
`σ_E=6.108`) using `math.Sqrt` (stdlib; the v0.7.4 cleanup from the
in-tree Newton iteration). This matches Boschini-Takahashi-Tibouchi
2024/1113 §3.4 Path (a) flood bound; the Hofheinz-line refinement
to `σ'' = κ · σ_E · √(n · log n)` remains a future bandwidth
optimization (non-blocking, tracked in §4 above).
### 10.4 Identifiable abort — evidence is third-party verifiable
`dkg2.Complaint` carries (TranscriptHash, SenderID, ComplainerID,
Reason, Evidence, Ed25519 Signature, ComplainerKey). Any
non-participant validator that observes the cohort transcript +
complaint can:
1. Re-verify the complainer's Ed25519 signature over `c.Bytes()`.
2. Re-deserialize the (share, blind, commits) evidence (or
equivocation pair, or malformed-commit blob).
3. Re-run `dkg2.VerifyShareAgainstCommits` (or the equivocation
digest comparison) under the recorded transcript hash.
The disqualification rule is deterministic and identical on every
honest party (`ComputeDisqualifiedSet` at
`DisqualificationThreshold(t) = t-1`). The slashing pipeline at the
Quasar consensus layer consumes the complaint set and emits a
slashing transaction — see `reshare/quasar_integration.go`
§Slashing-Evidence for the wire format.
### 10.5 Sign-off chain
- v0.7.3: trusted-dealer caveat in `keyera.Bootstrap` closed via
`keyera.BootstrapPedersen`.
the new `keyera.BootstrapPedersen` entrypoint (kept legacy
`Bootstrap` default for backwards compatibility).
- v0.7.4: trusted-dealer caveat in `keyera.Reanchor` closed via
`keyera.ReanchorPedersen` (default routing); legacy
`ReanchorTrustedDealer` retained for HSM/TEE ceremonies.
- v0.7.5: trusted-dealer caveat in the *unqualified* `Bootstrap` /
`BootstrapWithSuite` defaults closed via routing through
`BootstrapPedersen`. Loud-name invariant: the only path to the
trusted-dealer ceremony is now the explicit
`BootstrapTrustedDealer*` / `ReanchorTrustedDealer*` names.
The honest non-claims in §7 carry forward to the v0.7.0+ roadmap.
### 10.6 Audit conclusion — clean bill of health for v0.7.5
— Cryptographer, 2026-05-21 (v0.7.4 addendum)
The final public-BFT audit (this addendum) finds **zero** remaining
reachable trusted-dealer assumption on any unqualified production
code path. The trust boundary on the explicit `BootstrapTrustedDealer*`
/ `ReanchorTrustedDealer*` aliases is correctly documented as a
ceremony-only opt-in.
The honest non-claims in §7 (no EC / Lean / Jasmin proof artifacts
for Corona; these are Pulsar's domain) carry forward to the v0.7.0+
roadmap as before.
— Cryptographer, 2026-05-21 (final v0.7.5 public-BFT addendum)
+79
View File
@@ -4,6 +4,85 @@ Notable changes to the `corona` module. Pre-release; semantic versioning
applied per PHILOSOPHY.md (patch only -- never minor/major without explicit
approval).
## v0.7.5 (public-BFT Bootstrap default — symmetry with Reanchor)
Closes the last asymmetry left over from v0.7.3: the unqualified
`keyera.Bootstrap` and `keyera.BootstrapWithSuite` entrypoints still
routed through the legacy single-dealer Shamir share-out, so any caller
who picked the "obvious" name silently inherited the trusted-dealer
caveat at chain genesis. v0.7.5 flips both defaults to
`BootstrapPedersen`: no single party ever holds the master secret `s`
at any point in the ceremony. The mirror move was already made in
v0.7.4 for `Reanchor`; v0.7.5 completes the symmetry so the API surface
has **one** safe default for opening a key era and **one** explicit
opt-in (`BootstrapTrustedDealer` / `ReanchorTrustedDealer`) for
HSM / TEE ceremonies.
### Signature changes (`keyera/keyera.go`)
- `keyera.Bootstrap(t, validators, groupID, eraID, entropy)` now
returns `(*KeyEra, *BootstrapTranscript, error)` (was `(*KeyEra,
error)`) and routes through `BootstrapPedersen`. Callers MUST commit
the transcript to the chain for the era to ratify.
- `keyera.BootstrapWithSuite` mirrors the same signature change and
routes through `BootstrapPedersen` with the supplied suite.
### Public-BFT-safe path (no functional change vs v0.7.4)
- `BootstrapPedersen` and `ReanchorPedersen` remain the canonical
implementations; the v0.7.5 change is a default-routing flip, not a
new construction.
### Legacy trusted-dealer aliases (explicit opt-in only)
- `keyera.BootstrapTrustedDealer` /
`keyera.BootstrapTrustedDealerWithSuite` continue to expose the
legacy single-dealer Shamir share-out (byte-equivalent to the
pre-v0.7.5 unqualified Bootstrap). The shared implementation is now
the unexported `bootstrapTrustedDealerImpl` so the public-facing
aliases cannot drift from each other; `ReanchorTrustedDealerWithSuite`
also routes through this helper.
### Structural invariant
- `BootstrapPedersen` requires `n >= 2 && t < n`. The unqualified
Bootstrap therefore inherits the same constraint, by construction;
callers that need `t == n` (every validator MUST sign) must select
the trusted-dealer entrypoint explicitly. This is the "loud name"
the cryptographer's audit prompt asks for: any deployment that
cannot tolerate the dealer's trust boundary will be caught at the
call site, not silently.
### Tests
All green via `GOWORK=off go test -count=1 -short ./...`:
- `keyera_test.go` -- updated to drop `t == n` from the public-BFT
cases (now uses `t = n - 1`); `Bootstrap` 3-tuple return propagated.
- `hashsuite_immut_test.go` -- same threshold adjustment.
- `bootstrap_pedersen_test.go` -- `TestBootstrapTrustedDealer_Legacy
Alias` renamed to `TestBootstrapTrustedDealer_StandalonePath` since
the two paths are no longer byte-equivalent; the test pins that the
Pedersen `Bootstrap` correctly rejects `t == n`.
### Documentation
- `DEPLOYMENT-RUNBOOK.md` §Bootstrap-Trust decision matrix updated:
the unqualified `Bootstrap` now appears in the "public-BFT" row;
the trusted-dealer alias is the explicit HSM/TEE-ceremony row.
- `AUDIT-2026-05.md` §0 v0.7.5 closure note; §8.5 new caveat: chains
that historically used `t == n` MUST adopt `t < n` to take the
public-BFT path. Otherwise they remain on `BootstrapTrustedDealer`
with the dealer caveat explicit at the call site.
### Cross-runtime byte equality
- The unqualified `Bootstrap` bytes diverge from prior runs because
the underlying ceremony changed. No deployed KAT pins `Bootstrap`
output; the canonical KATs target `BootstrapPedersen` (under its
own name) and `BootstrapTrustedDealer` (under its own name). Future
cross-runtime ports MUST distinguish the two paths by name.
## v0.7.4 (public-BFT Reanchor via dkg2 Pedersen-DKG; mathSqrt cleanup)
Closes the Reanchor trusted-dealer regression flagged in v0.7.3 red
+1 -1
View File
@@ -58,7 +58,7 @@ visible to the network observer.
| Location | Status | Notes |
|---|---|---|
| `keyera.Bootstrap` share generation (`keyera/keyera.go:Bootstrap`) | **(a)** | Shamir over Z_q runs entirely in `math/big`. `big.Int` is **not** constant-time, but Bootstrap is a one-time foundation MPC ceremony; the dealer's machine is the only entity with the master secret in memory at this point. Timing is observable only to the dealer. The dealer's standard-form `s` copy is zeroed in place after sharing (`keyera.go:191-198`). |
| `keyera.BootstrapTrustedDealer` share generation (`keyera/bootstrap_pedersen.go:bootstrapTrustedDealerImpl`) | **(a)** | Shamir over Z_q runs entirely in `math/big`. `big.Int` is **not** constant-time, but the trusted-dealer ceremony is a one-time foundation MPC event; the dealer's machine is the only entity with the master secret in memory at this point. Timing is observable only to the dealer. The dealer's standard-form `s` copy is zeroed in place after sharing. The unqualified `keyera.Bootstrap` defaults to `BootstrapPedersen` since v0.7.5, where no party ever holds `s`; the constant-time analysis there inherits from `dkg2/` (see §2). |
| `keyera.Reshare` share recombination via Lagrange (`keyera/keyera.go:Reshare`) | **(a)** | The Lagrange recombination is a `big.Int` linear combination of secret share values with public Lagrange coefficients. `big.Int.Mul/Mod` are not constant-time per se, but every input value is held by the local party only — there is no remote attacker on the recombination path. Timing leaks only to the local OS scheduler, which is the trust boundary already accepted by the validator deployment posture. |
| `EraseShare` after activation (`reshare/keyshare.go:158`) | **(a)** | Plain `for k := range coeffs { coeffs[k] = 0 }`. The Go compiler does not zero behind a pointer reference unless we write the assignment ourselves; this is the documented zeroization technique used by upstream `golang.org/x/crypto/internal/poly1305.MACState.zeroize`. The data is already secret to the local process; the goal is overwrite-on-deactivation, not constant-time-equality. |
| Lens share derivation (`lens/keyera/keyera.go`) | mirrored into `lens/CONSTANT-TIME-REVIEW.md` | See lens doc. |
+51 -27
View File
@@ -30,15 +30,22 @@ responses, not the shares themselves.
**However**, two distinct trust-model caveats apply:
1. **DKG bootstrap is a one-time trusted-setup event.** At chain
genesis (or at Reanchor), the foundation MPC ceremony runs
`keyera.Bootstrap` which generates the master secret `s` and
distributes shares. Whoever participates in the Bootstrap MPC
ceremony has, at some point in the ceremony's execution, machinery
that touches `s`. The dealer-side `s` is zeroed in place after
sharing (`keyera/keyera.go:228-238`), but the ceremony must be
conducted under verifiably-public conditions (commit-and-reveal
from genesis validators) with toxic-waste assumptions tracked.
1. **DKG bootstrap path is operator-chosen.** As of v0.7.5, the
unqualified `keyera.Bootstrap` / `keyera.BootstrapWithSuite`
defaults route through `BootstrapPedersen` (Pedersen-DKG over
`R_q` + Path (a) noise flooding) so no single party ever holds
the master secret `s` at any point in the ceremony. The legacy
trusted-dealer ceremony path (where a single dealer instantiates
`s` and Shamir-shares it to the committee, zeroing the in-memory
copy before returning) is retained ONLY behind the explicit
`keyera.BootstrapTrustedDealer` /
`keyera.BootstrapTrustedDealerWithSuite` names for HSM/TEE
ceremonies where the foundation explicitly chooses a non-
distributed trust root. The latter path is acceptable for chain-
launch ceremonies where the foundation's ceremony posture is
audited and attested; it is NOT acceptable for permissionless
re-anchoring, routine rotations, or any context where the
dealer is not a publicly-attested entity.
2. **The aggregator process is in the trusted computing base.** An
adversary who fully compromises the aggregator host
@@ -58,17 +65,21 @@ satisfy at least items 1–4):
2. **Memory isolation**: the aggregator process runs with locked
pages (`mlock`), core-dumps disabled (`setrlimit RLIMIT_CORE=0`),
ptrace disabled (`prctl PR_SET_DUMPABLE 0`).
3. **Bootstrap MPC ceremony hardening**: foundation MPC ceremony at
chain genesis uses commit-and-reveal entropy from genesis
validators; dealer state is zeroed in place via
`keyera/keyera.go:Bootstrap`. Ceremony participants are bound by
the foundation's toxic-waste protocol.
3. **Bootstrap MPC ceremony hardening** (applies only to operators
choosing the explicit `BootstrapTrustedDealer` path): foundation
MPC ceremony at chain genesis uses commit-and-reveal entropy from
genesis validators; dealer state is zeroed in place via
`keyera/bootstrap_pedersen.go:bootstrapTrustedDealerImpl`.
Ceremony participants are bound by the foundation's toxic-waste
protocol. Operators on the default `BootstrapPedersen` path do
NOT need this control because no party ever holds the master
secret.
4. **Operational scope limits**: aggregator role is a single-purpose
process; no network listener beyond the consensus message bus;
no shell access; no debugger attachment in production.
5. **TEE attestation (recommended)**: aggregator runs inside SGX,
SEV-SNP, or TDX with remote attestation pinned to the
reproducible build of `luxfi/corona v0.7.4`.
reproducible build of `luxfi/corona v0.7.5`.
6. **Defense-in-depth**: validator-set rotation via Reshare (not
Reanchor) is the routine path; Reanchor (new key era) is a rare
governance event reserved for security-incident response.
@@ -140,20 +151,30 @@ era for security-event response). Resharing within an era is
| Scenario | Recommended entrypoint |
|---|---|
| New mainnet chain launch (public BFT) | `BootstrapPedersen` (foundation acts as one of n) |
| New mainnet chain launch (public BFT) | `Bootstrap` / `BootstrapPedersen` (foundation acts as one of n) |
| Foundation-audited HSM ceremony at chain genesis | `BootstrapTrustedDealer` (acceptable; document trust attestation) |
| Reanchor due to security incident, public BFT validator set | `ReanchorPedersen` / `Reanchor` (no single party should be retrusted) |
| Reanchor due to security incident, public BFT validator set | `Reanchor` / `ReanchorPedersen` (no single party should be retrusted) |
| Reanchor via HSM/TEE ceremony with audited trust posture | `ReanchorTrustedDealer` (acceptable only when the new dealer is publicly attested) |
| Routine validator-set rotation | Use `Reshare` (preserves GroupKey; no bootstrap, no decision) |
| Test / KAT replay | Either, with deterministic entropy |
**Default routing in code**: as of v0.7.4, the unqualified
`Reanchor` / `ReanchorWithSuite` entries route through
`ReanchorPedersen`. To use the legacy single-dealer path you MUST
name `ReanchorTrustedDealer` / `ReanchorTrustedDealerWithSuite`
explicitly. The same pattern as v0.7.3 `Bootstrap` /
`BootstrapTrustedDealer`: the safe default is unqualified; the
ceremony path requires an explicit name.
**Default routing in code**: as of v0.7.5, **both** unqualified
`Bootstrap` / `BootstrapWithSuite` **and** `Reanchor` /
`ReanchorWithSuite` entrypoints route through Pedersen-DKG by
default. To use the legacy single-dealer path you MUST name
`BootstrapTrustedDealer` / `BootstrapTrustedDealerWithSuite` (or the
analogous Reanchor variant) explicitly. The safe default is
unqualified; the ceremony path requires an explicit name. This is
the **loud-name invariant**: any deployment that cannot tolerate the
dealer's trust boundary cannot accidentally inherit it; the explicit
opt-in is enforced at the call site.
**Structural constraint**: the public-BFT-safe path requires
`n >= 2 && t < n` (strictly less than). Deployments that need every
validator to sign (`t == n`) must select `BootstrapTrustedDealer`
explicitly; the unqualified `Bootstrap` will fail with
`ErrBootstrapPedersenShape` on `t == n`, surfacing the decision at
the call site rather than silently routing to the dealer path.
The decision is recorded in the era's genesis transcript via the
`HashSuiteID` and the Bootstrap mode tag; downstream auditors can
@@ -320,7 +341,10 @@ In the interim, the honest cryptographic posture is:
**Document metadata**
- Name: `DEPLOYMENT-RUNBOOK.md`
- Version: v0.3 (matches Corona v0.7.4; Reanchor-Trust two-entrypoint
disclosure added; default routing pinned to Pedersen-DKG for both
Bootstrap and Reanchor)
- Version: v0.4 (matches Corona v0.7.5; the unqualified
`keyera.Bootstrap` and `keyera.BootstrapWithSuite` defaults are
now routed through `BootstrapPedersen` for symmetry with the
v0.7.4 Reanchor flip; the legacy trusted-dealer ceremony path is
reachable only via the explicit `BootstrapTrustedDealer*` /
`ReanchorTrustedDealer*` names)
- Date: 2026-05-21
+3 -2
View File
@@ -1,7 +1,7 @@
# Corona -- Agent Knowledge Base
**Repository**: github.com/luxfi/corona
**Latest Tag**: v0.7.3 (next: v0.7.4 — public-BFT ReanchorPedersen closes the Reanchor trusted-dealer regression)
**Latest Tag**: v0.7.4 (next: v0.7.5 — public-BFT Bootstrap default closes the last unqualified trusted-dealer dispatch)
**Status**: Production (consensus path); NIST MPTC submission package included. Sibling submission `luxfi/pulsar` is the M-LWE byte-equal FIPS 204 path.
## Purpose (one-liner)
@@ -39,6 +39,7 @@ Pulsar does; Corona's R-LWE has no FIPS standard target to refine against. See
| SHA | Tag | Impact |
|-----|-----|--------|
| (pending) | v0.7.5 | keyera: Bootstrap default → BootstrapPedersen; trusted-dealer impl moved to unexported helper; loud-name invariant restored across both Bootstrap and Reanchor |
| `607d71c` | v0.7.4 | keyera: ReanchorPedersen — closes Reanchor trusted-dealer regression; `mathSqrt` → stdlib `math.Sqrt` |
| `e412c7e` | v0.7.3 | keyera: BootstrapPedersen — Pedersen-DKG over R_q + Path (a) noise flooding; closes trusted-dealer caveat |
| `920195e` | v0.7.2 | gpu: opt corona threshold signing into lattice/ring GPU NTT dispatch |
@@ -53,7 +54,7 @@ Pulsar does; Corona's R-LWE has no FIPS standard target to refine against. See
| `43e7d88` | v0.4.x | corona/papers: corona2025 cite → boschini2024corona; coronaThreshold → coronaThreshold in TeX |
### Active versions
- Repo: `v0.7.3` (next: `v0.7.4` adds `keyera.ReanchorPedersen` and switches the default `keyera.Reanchor` to route through `dkg2/`; legacy single-dealer behaviour retained behind `keyera.ReanchorTrustedDealer`).
- Repo: `v0.7.4` (next: `v0.7.5` flips the default `keyera.Bootstrap` / `keyera.BootstrapWithSuite` to route through `BootstrapPedersen`, matching the v0.7.4 Reanchor flip; the legacy trusted-dealer impl is now in the unexported `bootstrapTrustedDealerImpl` and only reachable via the explicit `BootstrapTrustedDealer*` / `ReanchorTrustedDealer*` names).
- Pinned by: `luxfi/consensus v1.23.6+` (R-LWE path is consensus-only).
### Canonical params
+7 -2
View File
@@ -27,7 +27,7 @@
// disqualified_senders, qualified_quorum) and must produce the exact
// transcript_hash_hex, exchange_hash_hex and signable_bytes_hex.
//
// Output: <luxcpp/crypto>/pulsar/test/kat/activation_kat.json
// Output: <luxcpp/crypto>/corona/test/kat/activation_kat.json
//
// Algorithm references:
// - corona/reshare/activation.go (canonical Go)
@@ -175,12 +175,17 @@ func main() {
// Default output: canonical luxcpp KAT directory; allow override via
// CORONA_ACTIVATION_KAT_PATH env or a positional arg.
outPath := filepath.Join(
os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar",
os.Getenv("HOME"), "work", "luxcpp", "crypto", "corona",
"test", "kat", "activation_kat.json",
)
if env := os.Getenv("CORONA_ACTIVATION_KAT_PATH"); env != "" {
outPath = env
}
if env := os.Getenv("PULSAR_ACTIVATION_KAT_PATH"); env != "" {
if os.Getenv("CORONA_ACTIVATION_KAT_PATH") == "" {
outPath = env
}
}
if len(os.Args) >= 2 {
outPath = os.Args[1]
}
+5
View File
@@ -301,6 +301,11 @@ func main() {
if env := os.Getenv("CORONA_DKG2_KAT_PATH"); env != "" {
outPath = env
}
if env := os.Getenv("PULSAR_DKG2_KAT_PATH"); env != "" {
if os.Getenv("CORONA_DKG2_KAT_PATH") == "" {
outPath = env
}
}
if len(os.Args) >= 2 {
outPath = os.Args[1]
}
+9 -1
View File
@@ -346,12 +346,20 @@ func main() {
// Write the file. Default output: canonical luxcpp KAT directory;
// allow override via CORONA_RESHARE_KAT_PATH or a positional arg.
outPath := filepath.Join(
os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar",
os.Getenv("HOME"), "work", "luxcpp", "crypto", "corona",
"test", "kat", "reshare_kat.json",
)
if env := os.Getenv("CORONA_RESHARE_KAT_PATH"); env != "" {
outPath = env
}
if env := os.Getenv("PULSAR_RESHARE_KAT_PATH"); env != "" {
// Legacy env var name retained for compatibility with the
// scripts/regen-kats.sh entrypoint; CORONA_RESHARE_KAT_PATH
// takes precedence.
if os.Getenv("CORONA_RESHARE_KAT_PATH") == "" {
outPath = env
}
}
if len(os.Args) >= 2 {
outPath = os.Args[1]
}
+10 -2
View File
@@ -58,7 +58,11 @@ func TestRegisterRingIdempotent(t *testing.T) {
t.Cleanup(func() { UnregisterRing(r) })
if err := RegisterRing(r); err != nil {
t.Fatalf("RegisterRing first: %v", err)
// On non-GPU builds (no cgo backend / no Metal / no CUDA) the
// lattice/gpu RegisterSubRing returns "GPU unavailable". The
// rest of corona's pure-Go path is fully exercised by the other
// suites; skip this idempotency check rather than failing.
t.Skipf("GPU registration unavailable on this build: %v", err)
}
beforeStats := CurrentStats()
if err := RegisterRing(r); err != nil {
@@ -101,7 +105,11 @@ func TestUnregisterRing(t *testing.T) {
}
before := CurrentStats()
if err := RegisterRing(r); err != nil {
t.Fatal(err)
// On non-GPU builds the lattice/gpu registry refuses
// RegisterSubRing with "GPU unavailable". The unregister
// semantics under that condition are vacuously preserved
// (no binding was installed); skip rather than failing.
t.Skipf("GPU registration unavailable on this build: %v", err)
}
UnregisterRing(r)
after := CurrentStats()
+21 -10
View File
@@ -450,23 +450,34 @@ func FinishBootstrapPedersen(suite hash.HashSuite, t int, validators []string, g
}
// BootstrapTrustedDealer is the legacy single-trusted-party bootstrap
// retained for genesis-ceremony scenarios where a non-distributed trust
// root is acceptable (e.g. a publicly observable foundation MPC ceremony
// at chain launch). It is byte-equivalent to the historical Bootstrap
// entrypoint.
// retained for HSM/TEE ceremony scenarios where a non-distributed
// trust root is acceptable by policy (e.g. a publicly observable
// foundation MPC ceremony at chain launch). The dealer's host
// platform is in the trust boundary for the duration of the call.
//
// PREFER BootstrapPedersen for any deployment where no single party is
// trusted to discard the master secret.
// TRUST MODEL — TRUSTED DEALER (explicit opt-in).
// This function instantiates the master secret s on a single party
// (the dealer) for the duration of one stack frame. The dealer
// constructs Shamir shares of s, hands them to the validator set,
// and zeroes the in-memory copy of s before returning. The chain
// only holds the public GroupKey and the distributed shares
// thereafter. The dealer is in the TCB for the duration of the call.
//
// See DEPLOYMENT-RUNBOOK.md for the trust-model trade-off documentation.
// PREFER Bootstrap (the unqualified entrypoint) for any deployment
// where no single party is trusted to discard the master secret;
// Bootstrap routes through Pedersen-DKG (no dealer) by default.
//
// See DEPLOYMENT-RUNBOOK.md §Bootstrap-Trust for the trust-model
// decision matrix (which entry to use when).
func BootstrapTrustedDealer(t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
return Bootstrap(t, validators, groupID, eraID, entropy)
return bootstrapTrustedDealerImpl(hash.Default(), t, validators, groupID, eraID, entropy)
}
// BootstrapTrustedDealerWithSuite is the suite-explicit form of
// BootstrapTrustedDealer.
// BootstrapTrustedDealer. See its docstring for the trust-model
// disclosure.
func BootstrapTrustedDealerWithSuite(suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
return BootstrapWithSuite(suite, t, validators, groupID, eraID, entropy)
return bootstrapTrustedDealerImpl(suite, t, validators, groupID, eraID, entropy)
}
// pathANoiseParameters returns the (σ, bound) pair for the Path (a)
+23 -15
View File
@@ -335,28 +335,36 @@ func TestBootstrapPedersen_DefaultSuite(t *testing.T) {
}
}
// TestBootstrapTrustedDealer_LegacyAlias — the renamed legacy alias is
// byte-equivalent to the original Bootstrap.
func TestBootstrapTrustedDealer_LegacyAlias(t *testing.T) {
// TestBootstrapTrustedDealer_StandalonePath — the BootstrapTrustedDealer
// alias is the only entrypoint that still routes through the legacy
// single-dealer Shamir share-out (for HSM/TEE ceremony scenarios). Since
// v0.7.5 the unqualified Bootstrap defaults to Pedersen-DKG, so the two
// paths are NOT byte-equivalent any more. This test pins that the
// trusted-dealer alias remains usable (same n, same validator set,
// same threshold) for the t == n case which the Pedersen path
// structurally rejects.
func TestBootstrapTrustedDealer_StandalonePath(t *testing.T) {
const thr, n = 3, 3
validators := []string{"x", "y", "z"}
eraA, err := Bootstrap(thr, validators, 0, 0, deterministicRand("legacy-alias"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
eraB, err := BootstrapTrustedDealer(thr, validators, 0, 0, deterministicRand("legacy-alias"))
era, err := BootstrapTrustedDealer(thr, validators, 0, 0, deterministicRand("trusted-dealer"))
if err != nil {
t.Fatalf("BootstrapTrustedDealer: %v", err)
}
// bTilde bytes must match — both paths consume the same entropy
// stream and run the same Shamir kernel.
if !reflect.DeepEqual(eraA.GroupKey.BTilde, eraB.GroupKey.BTilde) {
t.Fatal("BootstrapTrustedDealer diverges from Bootstrap on bTilde — alias broken")
if era.GroupKey == nil {
t.Fatal("BootstrapTrustedDealer returned nil GroupKey")
}
// Same set of validators.
if !reflect.DeepEqual(eraA.State.Validators, eraB.State.Validators) {
t.Fatal("BootstrapTrustedDealer diverges from Bootstrap on validators")
if got := len(era.State.Validators); got != n {
t.Fatalf("validator count: want %d got %d", n, got)
}
if got := era.State.Threshold; got != thr {
t.Fatalf("threshold: want %d got %d", thr, got)
}
// The unqualified Bootstrap (Pedersen-DKG path) refuses t == n
// because identifiable-abort detection requires at least one honest
// non-sender per round. Confirm the dispatch.
if _, _, err := Bootstrap(thr, validators, 0, 0, deterministicRand("public-bft")); err == nil {
t.Fatal("Bootstrap with t == n should fail under Pedersen-DKG (t < n required)")
}
}
+12 -12
View File
@@ -36,9 +36,9 @@ import (
// TestBootstrapPinsSuiteSHA3 — Gate 3A: Bootstrap with Corona-SHA3 →
// era.HashSuiteID == "Corona-SHA3".
func TestBootstrapPinsSuiteSHA3(t *testing.T) {
era, err := BootstrapWithSuite(
era, _, err := BootstrapWithSuite(
hash.NewCoronaSHA3(),
3,
2,
[]string{"a", "b", "c"},
0, 0,
deterministicRand("hashsuite-immut-sha3"),
@@ -57,9 +57,9 @@ func TestBootstrapPinsSuiteSHA3(t *testing.T) {
// TestBootstrapPinsSuiteBLAKE3 — Gate 3A: Bootstrap with Corona-BLAKE3
// → era.HashSuiteID == "Corona-BLAKE3".
func TestBootstrapPinsSuiteBLAKE3(t *testing.T) {
era, err := BootstrapWithSuite(
era, _, err := BootstrapWithSuite(
hash.NewCoronaBLAKE3(),
3,
2,
[]string{"a", "b", "c"},
0, 0,
deterministicRand("hashsuite-immut-blake3"),
@@ -79,7 +79,7 @@ func TestBootstrapPinsSuiteBLAKE3(t *testing.T) {
// pins the production default, so legacy callers cannot accidentally
// open an era under no profile at all.
func TestBootstrapDefaultsToSHA3(t *testing.T) {
era, err := Bootstrap(3, []string{"a", "b", "c"}, 0, 0,
era, _, err := Bootstrap(2, []string{"a", "b", "c"}, 0, 0,
deterministicRand("hashsuite-default"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
@@ -92,9 +92,9 @@ func TestBootstrapDefaultsToSHA3(t *testing.T) {
// TestReshareCannotChangeSuiteSHA3 — Gate 3B: Reshare on a Corona-SHA3
// era yields a state with HashSuiteID == "Corona-SHA3" (unchanged).
func TestReshareCannotChangeSuiteSHA3(t *testing.T) {
era, err := BootstrapWithSuite(
era, _, err := BootstrapWithSuite(
hash.NewCoronaSHA3(),
3,
2,
[]string{"v1", "v2", "v3"},
0, 0,
deterministicRand("reshare-cannot-change-sha3"),
@@ -104,7 +104,7 @@ func TestReshareCannotChangeSuiteSHA3(t *testing.T) {
}
priorID := era.HashSuiteID
next, err := era.Reshare([]string{"v1", "v2", "v3"}, 3, deterministicRand("reshare-1"))
next, err := era.Reshare([]string{"v1", "v2", "v3"}, 2, deterministicRand("reshare-1"))
if err != nil {
t.Fatalf("Reshare: %v", err)
}
@@ -119,9 +119,9 @@ func TestReshareCannotChangeSuiteSHA3(t *testing.T) {
// TestReshareCannotChangeSuiteBLAKE3 — Gate 3B mirrored on the legacy
// profile. A BLAKE3-pinned era stays BLAKE3 across Reshare.
func TestReshareCannotChangeSuiteBLAKE3(t *testing.T) {
era, err := BootstrapWithSuite(
era, _, err := BootstrapWithSuite(
hash.NewCoronaBLAKE3(),
3,
2,
[]string{"v1", "v2", "v3"},
0, 0,
deterministicRand("reshare-cannot-change-blake3"),
@@ -178,9 +178,9 @@ func TestReshareAPIHasNoHashSuiteParameter(t *testing.T) {
// identifiable-abort detection. The legacy t == n equivalence stays
// available under ReanchorTrustedDealer for HSM-ceremony scenarios.
func TestReanchorMayChangeSuite(t *testing.T) {
era1, err := BootstrapWithSuite(
era1, _, err := BootstrapWithSuite(
hash.NewCoronaSHA3(),
3,
2,
[]string{"a", "b", "c"},
0, 1,
deterministicRand("reanchor-era-1"),
+49 -29
View File
@@ -132,29 +132,34 @@ type EpochShareState struct {
Shares map[string]*threshold.KeyShare
}
// Bootstrap runs the one-time trusted-dealer ceremony at chain genesis
// or governance-gated Reanchor.
// Bootstrap opens a new key era WITHOUT a trusted dealer.
//
// TRUST MODEL — TRUSTED DEALER.
// This function instantiates the master secret s on a single party (the
// dealer) for the duration of one stack frame. The dealer constructs
// Shamir shares of s, hands them to the validator set, and zeroes the
// in-memory copy of s before returning. The chain only holds the public
// GroupKey and the distributed shares thereafter.
// TRUST MODEL — PUBLIC-BFT-SAFE (default).
// This function routes the keygen ceremony through `dkg2/` (Pedersen-
// DKG over R_q) + Path (a) noise flooding so no single party ever
// holds the master secret s at any point in the ceremony. Every
// validator runs `dkg2.Round1` independently; the per-party noise
// contributions aggregate into a Corona-Sign-shaped public key
// `bTilde = Round_Xi(A·s + e'')` where each party adds one Gaussian
// `e_j'` slice under σ'' = κ·σ_E·√n. Identifiable abort: a
// misbehaving sender is named in a signed `dkg2.Complaint` carrying
// re-checkable Pedersen evidence; the chain commits the abort
// transcript and stays at the previous epoch.
//
// Foundation MUST coordinate Bootstrap as a publicly observable MPC
// ceremony at chain launch: entropy from verifiable commit-and-reveal
// among the genesis validators; dealer state erased before the ceremony
// closes; HSM-bound execution preferred. Even with these mitigations,
// the dealer's host platform is in the trust boundary for the duration
// of the call.
// On success returns (era, transcript, nil). The transcript is the
// public, byte-stable record of the ceremony; honest validators that
// observe the same cohort messages compute identical bytes. The
// consensus layer commits to `transcript.TranscriptHash` to ratify
// the era. On identifiable abort returns (nil, nil, err wrapping
// ErrBootstrapPedersenAbort); extract via ExtractAbortEvidence.
//
// PUBLIC-BFT-SAFE ALTERNATIVE: use BootstrapPedersen, which runs
// dkg2 (Pedersen-DKG over R_q) + Path (a) noise flooding so no party
// ever holds s. BootstrapPedersen is the recommended entrypoint for any
// new deployment; Bootstrap (and its BootstrapTrustedDealer alias) is
// retained for genesis ceremonies where a non-distributed trust root is
// explicitly chosen. See DEPLOYMENT-RUNBOOK.md §Bootstrap-Trust.
// LEGACY ALTERNATIVE: use BootstrapTrustedDealer for HSM/TEE
// ceremonies where the operator explicitly chooses a non-distributed
// trust root (publicly observable HSM-bound ceremony with commit-
// and-reveal entropy from genesis validators, dealer state zeroed
// before ceremony close). The dealer's host platform is in the trust
// boundary for the duration of the call. See DEPLOYMENT-RUNBOOK.md
// §Bootstrap-Trust for the trust-model trade-off documentation.
//
// Use crypto/rand.Reader for the kernel's randomness when no specific
// ceremony source is provided. Tests pass a deterministic source for
@@ -163,16 +168,31 @@ type EpochShareState struct {
// Bootstrap pins the production HashSuite (Corona-SHA3). Use
// BootstrapWithSuite to open an era under the legacy Corona-BLAKE3
// profile (for cross-suite KAT replay only — NOT for production).
func Bootstrap(t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
return BootstrapWithSuite(hash.Default(), t, validators, groupID, eraID, entropy)
func Bootstrap(t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, *BootstrapTranscript, error) {
return BootstrapPedersen(hash.Default(), t, validators, groupID, eraID, entropy)
}
// BootstrapWithSuite is the canonical entrypoint that explicitly pins
// the hash profile this era will run under. The supplied suite is
// recorded on the returned KeyEra and propagates unchanged through
// every Reshare; Reanchor opens a fresh era and MAY pin a different
// suite. Pass nil to use the production default (Corona-SHA3).
func BootstrapWithSuite(suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
// BootstrapWithSuite is the suite-explicit form of Bootstrap. It
// routes through BootstrapPedersen (public-BFT-safe, no trusted
// dealer) with the supplied HashSuite; pass nil to use the
// production default (Corona-SHA3). See the Bootstrap docstring for
// the trust-model disclosure.
//
// LEGACY ALTERNATIVE: use BootstrapTrustedDealerWithSuite for HSM/TEE
// ceremony scenarios where a non-distributed trust root is acceptable
// by policy.
func BootstrapWithSuite(suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, *BootstrapTranscript, error) {
return BootstrapPedersen(suite, t, validators, groupID, eraID, entropy)
}
// bootstrapTrustedDealerImpl is the historical trusted-dealer
// implementation, retained as the body of BootstrapTrustedDealer /
// BootstrapTrustedDealerWithSuite. Single source of truth so the
// public-facing aliases cannot drift from each other. NO production
// code path should call this directly; use BootstrapTrustedDealer
// instead so the name signals the trust-model decision at the call
// site.
func bootstrapTrustedDealerImpl(suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
if len(validators) == 0 {
return nil, ErrEmptyValidators
}
@@ -422,7 +442,7 @@ func ReanchorTrustedDealerWithSuite(prev *KeyEra, suite hash.HashSuite, t int, v
nextEpoch = prev.State.Epoch + 1
}
}
next, err := BootstrapWithSuite(suite, t, validators, groupID, nextEraID, entropy)
next, err := bootstrapTrustedDealerImpl(suite, t, validators, groupID, nextEraID, entropy)
if err != nil {
return nil, err
}
+18 -17
View File
@@ -14,14 +14,15 @@ import (
// TestBootstrapBuildsAndSigns confirms that Bootstrap returns a complete
// KeyShare set that can produce a verifying signature under the produced
// GroupKey. We exercise t = n (every validator in the active signing
// set) here; corona's signing protocol assumes the full committee
// participates in each Sign invocation.
// GroupKey. The public-BFT-safe Bootstrap routes through Pedersen-DKG
// which requires t < n (strict) for identifiable-abort detection, so
// we exercise the threshold setup (t = n-1, signing committee = full
// validator set).
func TestBootstrapBuildsAndSigns(t *testing.T) {
const tThr, n = 3, 3
const tThr, n = 2, 3
validators := []string{"validator-A", "validator-B", "validator-C"}
era, err := Bootstrap(tThr, validators, 0, 0, deterministicRand("bootstrap-genesis"))
era, _, err := Bootstrap(tThr, validators, 0, 0, deterministicRand("bootstrap-genesis"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
@@ -45,10 +46,10 @@ func TestBootstrapBuildsAndSigns(t *testing.T) {
// is unchanged after Reshare, and the new shares produce a signature
// that verifies under the unchanged GroupKey.
func TestReshareSameSetPreservesGroupKey(t *testing.T) {
const tThr = 3
const tThr = 2
validators := []string{"v1", "v2", "v3"}
era, err := Bootstrap(tThr, validators, 0, 0, deterministicRand("genesis-A"))
era, _, err := Bootstrap(tThr, validators, 0, 0, deterministicRand("genesis-A"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
@@ -74,12 +75,12 @@ func TestReshareSameSetPreservesGroupKey(t *testing.T) {
// validator set with a different threshold. The new committee's shares
// produce a signature that verifies under the unchanged GroupKey.
func TestReshareNewCommitteePreservesGroupKey(t *testing.T) {
const tOld = 3
const tNew = 5
const tOld = 2
const tNew = 4
oldSet := []string{"v1", "v2", "v3"}
newSet := []string{"v4", "v5", "v6", "v7", "v8"}
era, err := Bootstrap(tOld, oldSet, 0, 0, deterministicRand("genesis-B"))
era, _, err := Bootstrap(tOld, oldSet, 0, 0, deterministicRand("genesis-B"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
@@ -107,16 +108,16 @@ func TestReshareNewCommitteePreservesGroupKey(t *testing.T) {
// TestReanchorOpensNewEra verifies Reanchor produces a fresh GroupKey
// while monotonically advancing the epoch and bumping the EraID.
//
// The new committee uses t=2, n=3 because the v0.7.4 Reanchor routes
// through Pedersen-DKG which requires t < n (strictly less than) for
// identifiable-abort detection. The legacy t == n behaviour remains
// available via ReanchorTrustedDealer.
// The public-BFT-safe lifecycle (Bootstrap → Reshare → Reanchor) all
// route through Pedersen-DKG which requires t < n (strictly less
// than) for identifiable-abort detection. The legacy t == n behaviour
// remains available via BootstrapTrustedDealer / ReanchorTrustedDealer.
func TestReanchorOpensNewEra(t *testing.T) {
era, err := Bootstrap(3, []string{"a", "b", "c"}, 0, 1, deterministicRand("era-1"))
era, _, err := Bootstrap(2, []string{"a", "b", "c"}, 0, 1, deterministicRand("era-1"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
if _, err := era.Reshare([]string{"a", "b", "c"}, 3, deterministicRand("reshare-x")); err != nil {
if _, err := era.Reshare([]string{"a", "b", "c"}, 2, deterministicRand("reshare-x")); err != nil {
t.Fatalf("Reshare: %v", err)
}
prevEpoch := era.State.Epoch
@@ -146,7 +147,7 @@ func TestReanchorOpensNewEra(t *testing.T) {
// TestReshareErrors covers the input-validation surface.
func TestReshareErrors(t *testing.T) {
era, err := Bootstrap(3, []string{"a", "b", "c"}, 0, 0, deterministicRand("err"))
era, _, err := Bootstrap(2, []string{"a", "b", "c"}, 0, 0, deterministicRand("err"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
}
+5 -2
View File
@@ -264,10 +264,13 @@ func TestReanchorPedersen_DishonestParty(t *testing.T) {
// identical Shamir kernel, so bTilde MUST agree.
func TestReanchorTrustedDealer_LegacyAlias(t *testing.T) {
const thr, n = 3, 3
era1, err := Bootstrap(thr, []string{"a", "b", "c"}, 0, 1,
// era1 uses BootstrapTrustedDealer because we exercise the t == n
// trusted-dealer ceremony path; the public-BFT Bootstrap (Pedersen-
// DKG) structurally requires t < n.
era1, err := BootstrapTrustedDealer(thr, []string{"a", "b", "c"}, 0, 1,
deterministicRand("reanchor-td-era1"))
if err != nil {
t.Fatalf("Bootstrap: %v", err)
t.Fatalf("BootstrapTrustedDealer: %v", err)
}
prevEpoch := era1.State.Epoch
prevGK := era1.GroupKey
+5 -5
View File
@@ -1,10 +1,10 @@
1f1bae4662b359813c0cf2df837c09587dd33cbf7e0a0c4be20ef2c65ec2e052 dkg2/test/kat/dkg2_kat.json
7e1e40fc0a1234a25f3fe88f728063afe68daa58d2ce1092fe214cb341af7dae test/kat/activation_kat.json
d20ea754fd2b221bf1e8a49a94719dad7aa38edc583d476d0bb2104bb5819748 test/kat/reshare_kat.json
4da08869ceefe6f526b1bc1037cf4e9d9fff0b519dd7f0bb8be08e40b0f1b0ec dkg2/test/kat/dkg2_kat.json
b40f6d270d7c52eaa79f9a0925868cd89e28c2133d107589840d284ab51f698b test/kat/activation_kat.json
7d522f3f03fc53bd7ff754624179b4bd493a01d18ce5e81b475f133c923af2ab test/kat/corona_oracle_v2/discrete_gaussian.json
3d798070e2debcfbc6dddcb2396f00eab7be3c20879885b2eb6d216cd35e4ae2 test/kat/corona_oracle_v2/montgomery_ntt.json
8921ef7d1a7170cfe2b5aeb7505a21dab33c480988b011106b98c0cc7e518b57 test/kat/corona_oracle_v2/prng_blake2_xof.json
58e4e9f92434789785a2ed3f61a5d554e8e77ec09010813d804a25eb155ff506 test/kat/corona_oracle_v2/shamir_share.json
c7efdc6836b2126324d1d4a5e0c788086ace17702fbe37630064dca5a26a90e6 test/kat/corona_oracle_v2/sign_verify_e2e.json
3eb62c4abe3388208bebd6bdd1d9ea82edc5eb3ef83ed646bcc6c2ceaa3b03a1 test/kat/corona_oracle_v2/sign_verify_e2e.json
b683814f35aa74694643dcde5b03bdae5d2a7036db8265203162e1e7c13b1fca test/kat/corona_oracle_v2/structs_matrix_wire.json
af2c33a4119e9eecb3d59f8fec8562240e544c0c84716e2d9d95a11fa3ce89ae test/kat/corona_oracle_v2/transcript_hash.json
ac3bca71d1774c3c1da3f44ceb52ed7b85efa0d7d4a31e1879fa5158f3b8671e test/kat/corona_oracle_v2/transcript_hash.json
677bc9a0c999d16a49c82bcea3666d366ed70ae2731cde7b1ef99db27707654c test/kat/reshare_kat.json
+16 -16
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env bash
# regen-kats.sh — deterministic regeneration + verification of every
# Pulsar KAT consumed by the C++ / GPU ports.
# Corona KAT consumed by the C++ / GPU ports.
#
# Outputs (paths match what luxcpp/crypto/pulsar/test/kat/ expects):
# <luxcpp>/crypto/pulsar/test/kat/reshare_kat.json
# <luxcpp>/crypto/pulsar/dkg2/test/kat/dkg2_kat.json
# <luxcpp>/crypto/pulsar/test/kat/activation_kat.json
# <luxcpp>/crypto/pulsar/test/kat/corona_oracle_v2/{prng_blake2_xof,
# Outputs (paths match what luxcpp/crypto/corona/test/kat/ expects):
# <luxcpp>/crypto/corona/test/kat/reshare_kat.json
# <luxcpp>/crypto/corona/dkg2/test/kat/dkg2_kat.json
# <luxcpp>/crypto/corona/test/kat/activation_kat.json
# <luxcpp>/crypto/corona/test/kat/corona_oracle_v2/{prng_blake2_xof,
# discrete_gaussian, montgomery_ntt, structs_matrix_wire,
# transcript_hash, shamir_share, sign_verify_e2e}.json
#
@@ -18,27 +18,27 @@
set -euo pipefail
PULSAR_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CORONA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LUXCPP_DIR="${LUXCPP_DIR:-${HOME}/work/luxcpp}"
KAT_BASE="${LUXCPP_DIR}/crypto/pulsar"
KAT_BASE="${LUXCPP_DIR}/crypto/corona"
RESHARE_OUT="${KAT_BASE}/test/kat/reshare_kat.json"
DKG2_OUT="${KAT_BASE}/dkg2/test/kat/dkg2_kat.json"
ACTIVATION_OUT="${KAT_BASE}/test/kat/activation_kat.json"
CORONA_DIR="${KAT_BASE}/test/kat/corona_oracle_v2"
CORONA_V2_DIR="${KAT_BASE}/test/kat/corona_oracle_v2"
MANIFEST="${PULSAR_DIR}/scripts/regen-kats.manifest.sha256"
MANIFEST="${CORONA_DIR}/scripts/regen-kats.manifest.sha256"
VERIFY=0
if [[ "${1:-}" == "--verify" ]]; then
VERIFY=1
fi
cd "${PULSAR_DIR}"
cd "${CORONA_DIR}"
# Pulsar oracles are indifferent to LUXCPP location through env vars and
# Corona oracles are indifferent to LUXCPP location through env vars and
# positional args. Run each, write to the canonical luxcpp path.
mkdir -p "$(dirname "${RESHARE_OUT}")" "$(dirname "${DKG2_OUT}")" "${CORONA_DIR}"
mkdir -p "$(dirname "${RESHARE_OUT}")" "$(dirname "${DKG2_OUT}")" "${CORONA_V2_DIR}"
echo "[1/5] reshare_oracle → ${RESHARE_OUT}"
PULSAR_RESHARE_KAT_PATH="${RESHARE_OUT}" go run ./cmd/reshare_oracle >/dev/null
@@ -56,8 +56,8 @@ PULSAR_DKG2_KAT_PATH="${DKG2_OUT}" go run ./cmd/dkg2_oracle >/dev/null
echo "[3/5] activation_oracle → ${ACTIVATION_OUT}"
PULSAR_ACTIVATION_KAT_PATH="${ACTIVATION_OUT}" go run ./cmd/activation_oracle >/dev/null
echo "[4/5] corona_oracle_v2 emit --out ${CORONA_DIR}"
go run ./cmd/corona_oracle_v2 emit --out "${CORONA_DIR}" >/dev/null
echo "[4/5] corona_oracle_v2 emit --out ${CORONA_V2_DIR}"
go run ./cmd/corona_oracle_v2 emit --out "${CORONA_V2_DIR}" >/dev/null
echo "[4b/5] corona_oracle_v2 byte-equality test"
go test -count=1 -run "TestEmitDeterministic|TestSignVerifyEntries|TestShamirRoundTrip" ./cmd/corona_oracle_v2 >/dev/null
@@ -74,7 +74,7 @@ manifest_files=(
"${DKG2_OUT}"
"${ACTIVATION_OUT}"
)
for f in "${CORONA_DIR}"/*.json; do
for f in "${CORONA_V2_DIR}"/*.json; do
manifest_files+=("$f")
done