18 Commits
Author SHA1 Message Date
zeekay a07f77f2ca refactor(corona): delete the dkg2 package — superseded by luxfi/dkg vss
The Pedersen-VSS DKG share-dealing now runs entirely on github.com/luxfi/dkg
(proven byte-identical at cutover); corona's own copy is dead. Remove it and its
now-orphaned tooling, keeping only corona-specific code (Ringtail signing, β
rounding, Path (a) finalize — all in sign/, utils/, keyera/, untouched).

Deleted:
  - dkg2/ (the whole package: Round1/Round2/commits/complaint/GPU dispatch).
  - cmd/dkg2_oracle/ (the dkg2 KAT generator; the DKG KAT now lives in
    luxfi/dkg's own ring/vss vectors).
  - keyera/cutover_gate_test.go (the transitional dkg2-vs-vss equality gate; its
    job is done — permanent byte-stability is pinned by the golden KAT, and the
    PIN-4 convention assertion is preserved dkg2-free in pin4_convention_test.go).

Updated:
  - cmd/cross_runtime_oracle drops the dkg2 manifest leg (the luxcpp C++ dkg2
    cross-runtime gate migrates to luxfi/dkg separately — flagged, not silently
    broken).
  - keyera/reanchor doc comments + threshold bench comment point at luxfi/dkg.

go build ./... + go test ./... -count=1 green (13 ok / 0 fail), go vet + gofmt
clean. The golden byte-stability KAT + Bootstrap→sign→verify + dishonest-dealer
abort all still pass on the vss path.
2026-06-27 22:03:55 -07:00
zeekay 30abcdf1b1 threshold: make trusted-dealer keygen explicit, drop dead ReconstructSecret, pin sub-quorum soundness
Part of the Lux threshold-crypto security rip (killing trusted-dealer /
full-key-reconstruction footguns).

- Rename threshold.GenerateKeys -> threshold.GenerateKeysTrustedDealer.
  The function samples the full secret and sets sign.K / sign.Threshold in
  one process: it IS the trusted dealer. The new name mirrors
  keyera.BootstrapTrustedDealer so the trust model is explicit and
  greppable. Doc comment now states it is for test/KAT/CT/CLI/oracle use
  only, and that production chain keygen uses keyera.Bootstrap (dealerless
  Pedersen DKG). Stays exported; every caller updated (cli, ct/dudect,
  same-package tests) plus stale comment refs in cmd, keyera, wire,
  reshare.

- Delete utils.ReconstructSecret. It Lagrange-interpolates the full secret
  from shares and had zero callers anywhere in the repo: a
  reconstruction-shaped footgun left dead. CompareSecrets and the section
  retained.

- Include threshold/minority_soundness_test.go: an adversarial negative
  pinning that a strict sub-quorum cannot assemble a signature that
  verifies under the group key (uncancelled PRF mask fails the L2-norm
  gate). A sub-quorum forgery would be a catastrophic finality break.
2026-06-26 21:13:45 -07:00
zooqueen 6dd359779b Revert "reshare: gate master-secret reconstruction behind corona_research [RED-2]"
This reverts commit 7dabee42be49ba3aea9b6d5bd474aa7defa73aad.
2026-06-21 15:10:41 -07:00
zooqueen d7996947bb reshare: gate master-secret reconstruction behind corona_research [RED-2]
reshare.Verify Lagrange-interpolates the input shares at X=0 and RETURNS the
reconstructed master secret to its caller (its own doc: "MUST NOT be used in
production"). Its only non-test caller is cmd/reshare_oracle (a KAT generator).
Left in the production package with no boundary, it is a latent master-key
footgun.

Fix (surgical split, not whole-file tag): reshare.go also defines the
production Reshare/Refresh/Share that keyera imports, so the file cannot be
tagged out. Move ONLY Verify into verify_reconstruct.go behind
//go:build corona_research; its shared interpolation helpers (selectQuorum,
lagrangeAtZero, sortedKeys) stay in reshare.go because production uses them.
Gate the sole non-test caller cmd/reshare_oracle/main.go with the same tag.

Tests: the reshare/refresh tests that reconstruct the secret (via Verify) move
into reshare_research_test.go / refresh_research_test.go / the tagged
integration_test.go and run under -tags corona_research. The production-
behaviour tests that do NOT reconstruct (determinism, arg validation, t=1
identity, fuzz, transcript) stay in the default build — no coverage lost.
Generic test helpers shared across both builds (cloneVector, runRoundsAndVerify)
move to untagged clone_test.go / signrounds_test.go.

Verification:
- default `go build ./...` exits 0; reshare.Verify ABSENT from the default
  build (proven: a Verify-calling test does not compile into the default run).
- default `go test ./...`: 0 failures; production tests run.
- `go test -tags corona_research ./...`: green; reconstruction tests + oracle
  compile and run (TestReshareSecretInvariant etc. pass).
- gofmt + vet clean in both build modes.
- security/framework/check.sh corona: PASS (no CLOSED-but-reconstructs blocker).
2026-06-21 15:02:59 -07:00
zooqueen 7fcf1d2f3c harden: hedged 256-bit nonce key, DRY CT comparator, honest CT/proof docs
Closes the CRIT-1 residual (D1-2) sid-entropy gap and the CT/doc hygiene
gaps to bring Corona to the no-leak/no-gap bar. EasyCrypt proofs untouched.

PRIORITY 1 — anti-nonce-reuse / no-leak durability:
- The Round-1 nonce-PRF key no longer keys on a bare 64-bit sid. It now
  derives from a 256-bit domain-separated SessionID (primitives.DeriveSessionID:
  TranscriptHash("corona.sign.session-id.v1" || be64(sid) || T)) AND a fresh
  per-signature 256-bit hedge salt drawn inside the kernel from party.Rand
  (default crypto/rand). PRNGKeyForRound = PRF(skShare, "CoronaNonceV3" ||
  sessionID || salt). Hedging restores threshold-Raccoon's fresh-per-signature
  nonce posture: reuse durability no longer rests on the external consensus
  layer never reissuing an sid — even an sid collision yields distinct R with
  prob 2^-256. A deterministic 256-bit SessionID alone is necessary but NOT
  sufficient (it repeats when sid repeats); the salt is what closes the leak.
- SignRound1 is fail-closed: rejects an all-zero derived SessionID or all-zero
  salt with ErrDegenerateSession, and surfaces a short-read error. The
  consensus slot-uniqueness invariant is documented as a HARD precondition at
  SignRound1 and Signer.Round1 (no longer a buried comment).
- KAT/oracle determinism preserved via one seam: sign.DeterministicNonceSource
  (KeyedPRNG over seed || "corona.sign.nonce-salt.v1" || partyIndex), set on
  Party.Rand / Signer.SetNonceRand only by reproducibility harnesses.
- SignRound1 / Signer.Round1 now return an error; all call sites updated.
- KAT REGEN: only sign_verify_e2e.json changes (nonce-key bytes moved);
  transcript_hash.json / MAC / legacy PRNGKey vectors are byte-stable, proving
  the consensus-agreed transcript path was left untouched. Regenerated via
  `bash scripts/regen-kats.sh`; `--verify` confirms byte-determinism (10 files).
- Regression: sign/nonce_reuse_test.go proves same-(skShare,sid) yields distinct
  D (fresh R), pinned-nonce reproduces byte-identically, and the degenerate-
  session guard fires. TestE2EKATReplayDeterminism rewritten to assert both the
  hedged-differs and pinned-reproduces properties (was a defanged no-op).

PRIORITY 2 — constant-time hygiene:
- reshare/commit.go already used a constant-time comparator; the real gap was
  the verbatim duplication of constTimePolyEqual+uint64SliceToBytes across dkg2
  and reshare. Consolidated to one canonical utils.ConstantTimePolyEqual; both
  delegate (no dkg2<->reshare dependency). Orphaned imports removed.
- FullRankCheck and the reshare commit path are now covered in the CT review
  with their public-operand justification.

PRIORITY 3 — doc accuracy:
- CONSTANT-TIME-REVIEW.md rewritten Corona-specific and file:line-accurate:
  drops the stale Pulsar/lens/warp/secp256k1 content; audits the real call
  sites (hedged nonce key, masking PRF, lattigo samplers as the residual TCB
  axiom, utils.ConstantTimePolyEqual, CheckL2Norm/Verify/FullRankCheck big.Int
  variable-time on PUBLIC operands, activation/commit-digest array equality,
  keyera/reshare zeroization).
- PROOF-CLAIMS.md §1: threshold.Combine / sign.LocalSign (nonexistent) -> the
  real sign.Party.SignFinalize exposed as threshold.Signer.Finalize.
- threshold/threshold.go package doc: Ring-LWE -> Module-LWE.
2026-06-21 08:21:58 -07:00
zeekay f04e70924d 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).
2026-05-21 19:04:55 -07:00
zeekay 5c2fb26191 cmd/m4_{precrand,shamir_tk}_oracle: switch from legacy /Users/z/work/lux/cpp to luxcppDir() 2026-05-21 14:59:30 -07:00
zeekay d42a7cc0d6 cmd/*_oracle: use luxcppDir() everywhere — kill all hardcoded /Users/z paths
5 oracle tools were writing KATs to hardcoded /Users/z/work/luxcpp/...
paths. All now go through the luxcppDir() helper (LUXCPP_DIR env override,
$HOME/work/luxcpp default), consistent with the lux/CLAUDE.md convention.

Also .gitignore stray oracle binaries from `go build ./cmd/<oracle>`.
2026-05-21 14:57:03 -07:00
zeekay bc40a9b9f8 cmd/m4_uniform_oracle: use luxcppDir() helper, drop hardcoded /Users/z path 2026-05-21 14:56:43 -07:00
zeekay e0fdfe475b corona: symmetric domain separation — PULSAR-* tags -> CORONA-*
Leftover from Corona's "Pulsar-R" lineage. Pulsar (M-LWE) and Corona
(R-LWE) are independent constructions with separate hardness assumptions,
so their cSHAKE personalisation strings must be distinct.

Changes (Go, non-luxcpp):
  - hash tags: PULSAR-HC-v1 -> CORONA-HC-v1, etc. (HU, TRANSCRIPT, PRF, MAC, PAIRWISE)
  - profile IDs: "Pulsar-SHA3" -> "Corona-SHA3", "Pulsar-BLAKE3" -> "Corona-BLAKE3"
  - context strings: pulsar.dkg2.A.v1 -> corona.dkg2.A.v1, etc.
  - env vars: PULSAR_RESHARE_KAT_PATH -> CORONA_RESHARE_KAT_PATH, etc.
  - struct names: pulsarSHA3 -> coronaSHA3
  - KAT derive roots: sign_e2e_pulsar -> sign_e2e_corona

What's preserved (different scope):
  - luxcpp/crypto/pulsar/* path references in comments (separate repo,
    out of scope; the C++ side will rename in its own commit)
  - Cross-runtime KAT files on disk (will regenerate next CI run)

All 11 packages test green: dkg, dkg2, hash, keyera, networking,
primitives, reshare, sign, threshold, utils, wire.
2026-05-13 14:19:13 -07:00
zeekay bf272721b0 gofmt: fix formatting in cmd/{corona_oracle_v2,dkg_oracle,shamir_general_oracle,sign_oracle}/main.go 2026-05-12 12:24:22 -07:00
zeekay 6d9e1192a3 corona: kill all remaining Corona identifiers in code + docs
After this commit zero 'corona' / 'Corona' / 'CORONA' references
remain in any Go file, Markdown doc, or TeX source in the corona
repository. The library is referenced by its production name (Corona)
everywhere; the upstream R-LWE construction is cited by author
(Boschini et al, ePrint 2024/1113) where attribution is required.

Renames:
  cmd/corona_oracle_v2/ → cmd/corona_oracle_v2/
  doc paths to luxcpp/crypto/corona → luxcpp/crypto/corona
  coronaThreshold (import alias in INTEGRATION.md) → coronaThreshold
  wrapAsCoronaShares                                → wrapAsCoronaShares
  CONSTANT-TIME-REVIEW.md, INTEGRATION.md, oracle cmd comments — all
  prose references swept Corona → Corona.
2026-05-12 10:04:40 -07:00
zeekay f28c574360 corona: docs reference luxfi/nasua (academic upstream), not luxfi/corona
The academic 2-round R-LWE threshold construction was renamed Corona
→ Nasua at the family taxonomy level. Update Corona's documentation
references to point at the new name. Corona itself imports nothing
from luxfi/nasua — the dependency line is on luxfi/lattice/v7 for the
ring/sampling primitives — these comment refs only name the conceptual
upstream for design provenance.
2026-05-12 09:00:59 -07:00
zeekay 8c010dd8ef corona: rename module path github.com/luxfi/pulsar → github.com/luxfi/corona
Repo forks from luxfi/pulsar (R-LWE production threshold for Lux
consensus) to luxfi/corona under the family rename. Same Ring-LWE
code, same hash family (Pulsar-SHA3 / Pulsar-BLAKE3 -- wire-format
customization tags stay stable per MED-2). Only the Go module path
and internal imports change.

Lux family alignment:
  - Pulsar  = M-LWE / FIPS 204 / NIST MPTC N1 (was luxfi/pulsar-m)
  - Corona  = R-LWE / NIST MPTC S1            (this repo)
  - Corona = academic upstream + Lux DKG expansions
2026-05-11 14:55:26 -07:00
zeekay 666e6b8950 style: gofmt -s across cmd/ + reshare/ + dkg2/ + keyera/ + threshold/
Whitespace-only struct-tag alignment changes. Closes the CI Lint gate
that was failing across multiple recent commits on otherwise-green
build+test runs.
2026-05-10 17:23:40 -07:00
zeekay b70f60dc31 pulsar: plumb HashSuite through every Sign-path primitive (F22)
primitives/hash.go: PRNGKey / GenerateMAC / GaussianHash / PRF / Hash /
LowNormHash all take HashSuite as first param. suite == nil resolves to
the production default Pulsar-SHA3 (cSHAKE256 / KMAC256 / TupleHash256
per FIPS 202 + NIST SP 800-185).

sign/sign.go: Party gains Suite field; NewParty defaults to
hash.Default(); NewPartyWithSuite for operator-explicit construction.
sign.Verify keeps its signature for callers and resolves the default
internally; the suite-explicit form is sign.VerifyWithSuite.

reshare/commit.go: suite-aware commit derivation aligned with the new
hash.HashSuite contract used by the rest of pulsar/reshare/dkg2/keyera.

Closes the gap where the HIP-0077 claim that Pulsar uses SHA-3
cSHAKE256/KMAC256/TupleHash256 in production was structurally false at
the Sign layer (only pulsar/reshare/, pulsar/dkg2/, pulsar/keyera/ were
consuming pulsar/hash/HashSuite previously). Pulsar-BLAKE3 retained as
legacy/non-normative only for cross-port byte checks.

go.mod: luxfi/math v1.3.0 -> v1.4.0 (consumes the codec for the new
hash-suite tag).

CHANGELOG.md added. sign_roundtrip_test.go pins the new contract.
Tests: primitives + sign + reshare PASS. Patch-bump.
2026-05-10 17:19:13 -07:00
zeekay 4d7b048aa9 pulsar: cross-runtime oracles + sign KAT generator (v0.1.2 follow-on)
cmd/sign_oracle/  — emits the canonical Pulsar sign+verify KAT JSON
(Gen + SignRound1 + SignRound2{Preprocess,} + SignFinalize + Verify) for
(t, n) ∈ {2/3, 3/5, 5/7, 7/11}. The C++ port at
luxcpp/crypto/pulsar/cpp/sign/ replays these entries byte-equal.

cmd/cross_runtime_oracle/  — Go writer for the cross-runtime release
gate. Emits a SHA-256 manifest tying together the three canonical
Pulsar KATs (sign, reshare, dkg2). C++ side at
luxcpp/crypto/pulsar/test/cross_runtime_test.cpp replays each KAT and
checks byte-equality (forward direction: 30/30 PASS).

cmd/cross_runtime_verify/  — Go reader for the reverse direction
(C++ writes, Go verifies). Currently 3/3 PASS against the SHA-256s
emitted by luxcpp/crypto/pulsar/cmd/cross_runtime_oracle/.

threshold/testdata/fuzz/FuzzPulsarSign1Round1Data/  — corpus seed for
the in-tree fuzz harness; mitigates the second lattice DoS (issue #4)
where Vector[T].ReadFrom calls make([]T, size) with no bound.

23 fuzz harnesses × 30s = 10.9M execs, 0 panics.
2026-05-04 03:32:05 -07:00
zeekay 261ad9d290 pulsar: lattice threshold kernel with key-era lifecycle + Pulsar-SHA3 + Nebula binding
Forked from github.com/luxfi/corona @ d09b2c2. Pulsar inherits the
2-round signing math byte-equal but adds blockchain-grade key
lifecycle and a SHA3-based domain-separated hash profile for
permissionless validator-set rotation.

Inherits from upstream Corona (byte-equal):
  - Sign1/Sign2/Combine 2-round signing math
  - Lattice ring R_q = Z_q[X]/(X^256+1), q = 0x1000000004A01
  - Module-LWE / Module-SIS hardness
  - NTT, Discrete-Gaussian Ziggurat, MAC layer

Replaces from upstream:
  - Broken Feldman-style DKG (pseudoinverse-recoverable). Replaced
    with: (a) one-time foundation MPC ceremony / trusted-dealer
    Bootstrap; (b) Pedersen DKG over R_q (research, dkg2/) hiding
    under MLWE on B / binding under MSIS on [A | B].
  - Trusted-dealer-per-epoch lifecycle (wrong shape for permissionless
    rotation). Replaced with Verifiable Secret Resharing (VSR) every
    epoch under the persistent group public key.

Key-era lifecycle (KeyEraID / Generation / RollbackFrom):
  - KeyEraID bumps only at Reanchor (rare governance event)
  - Generation bumps every Refresh / Reshare under the same GroupKey
  - RollbackFrom records prior generation reverted from (0 = forward)
  - Activation cert (QUASAR-PULSAR-ACTIVATE-v1) gates new generations
    under the unchanged GroupKey

Pulsar-SHA3 hash profile (NIST FIPS 202 / SP 800-185):
  - Hc:        cSHAKE256("PULSAR-HC-v1", transcript) → challenge
  - Hu:        cSHAKE256("PULSAR-HU-v1", transcript) → XOF stream
  - TranscriptHash: TupleHash256("PULSAR-TRANSCRIPT-v1", parts...)
  - PRF:       KMAC256(key, msg, "PULSAR-PRF-v1")
  - MAC:       KMAC256(key, msg, "PULSAR-MAC-v1")
  - Pairwise:  KMAC256(kex, encode(...), "PULSAR-PAIRWISE-v1")
  - Default suite is Pulsar-SHA3; Pulsar-BLAKE3 retained as optional
    non-normative fast profile. HashSuiteID is bound into transcripts.
  - Implementation at pulsar/hash/ with HashSuite interface.
  - All KATs regenerated under SHA3 profile; vectors in
    luxcpp/crypto/pulsar/test/kat/.

Nebula root binding (TranscriptInputs + ActivationMessage canonical
bytes, all bound under TupleHash256):
    chain_id, network_id, group_id,
    key_era_id, old_generation, new_generation,
    old_epoch_id, new_epoch_id,
    old_set_hash, new_set_hash,
    threshold_old, threshold_new,
    group_public_key_hash,
    nebula_root,
    hash_suite_id,
    implementation_version,
    variant.

Packages:
  primitives/   Shamir over R_q, Lagrange, hash helpers
  sign/         2-round sign math (byte-equal upstream)
  threshold/    GroupKey, KeyShare, Signer types
  reshare/      VSR kernel — Refresh (HJKY97 zero-poly), Reshare
                (Desmedt-Jajodia), commit (Pedersen R_q), complaint,
                transcript, pairwise KEX, activation cert
  hash/         HashSuite interface; PulsarSHA3 (default), PulsarBLAKE3
  dkg2/         Pedersen DKG over R_q (research, reference only)
  keyera/       Lifecycle wrapper: Bootstrap → Reshare → Reanchor
  papers/       LP-073-pulsar paper (sections + bibliography)
  cmd/*_oracle/ KAT generators (Go ↔ C++ byte-equal validation)

DESIGN.md is the single source of truth:
  - The Pulsar metaphor (rotating beam over persistent body)
  - Vocabulary stack per LP-105: Nebula / Photon / Lumen / Beam /
    Pulsar / Pulse / Prism / Horizon / Quasar
  - Pulsar ≠ Lumen (PQ stream is separate, planned)
  - Pulsar / Lens / LSS three-layer architecture
  - Bootstrap Dealer vs Signature Coordinator
  - No-slashing failure ladder (timeout → retry → rollback → reanchor)
  - MVP VSR vs Robust VSR phasing
  - Borrowed-brand terms ("X-Wing" used casually) replaced with
    "hybrid KEM" / "Hybrid Lumen Handshake"; X-Wing cited only as
    the IETF combiner primitive name where exact reference matters.

Tests: full suite green
  hash 5+/5+, keyera 5/5, reshare 45+/45+, threshold, sign, dkg,
  dkg2, primitives, networking, utils, corona_oracle_v2 KAT —
  all pass.

Honest claim: the crypto primitives are not new (HJKY97,
Desmedt-Jajodia97, Wong-Wang-Wing02, Corona, Hermine, Threshold
Raccoon all exist). The novel contribution is the systems
composition: a permissionless-consensus deployment architecture that
turns static two-round PQ threshold signatures into a dynamic,
leaderless, validator-rotation-tolerant finality layer.

Companion docs: papers/lp-073-pulsar/ (academic paper); LP-073 +
LP-103 + LP-105 in github.com/luxfi/lps; threshold/protocols/lss/
lss_pulsar.go (LSS adapter wrapping this kernel).
2026-03-03 12:00:00 -08:00