mirror of
https://github.com/luxfi/corona.git
synced 2026-09-24 00:25:54 +00:00
main
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
84465bc876 |
corona: a peer's message is walked before it is decoded, and cannot end the process
Two properties this package did not hold. A lattice frame arriving from a peer went straight to the decoder, which sizes its work from counts the peer wrote. A frame naming far more than it carries therefore cost the receiver time proportional to what it named: twenty-four bytes buy seconds. RecvVector and RecvMatrix now walk the frame first, which costs microseconds and bounds the work to the bytes actually present. The walk is the same one the signature and group-key decoders use, so there is one rule for what a lattice frame may claim and one place it is enforced. The transfer methods answered every failure with log.Fatalf, which is os.Exit — so a malformed message, a peer that hung up, or a slow socket ended the process. A library may not make that decision for its caller: they return errors now, and the command decides what is fatal. The same applied to the three matrix multiplications, which exited on a shape mismatch while MatrixAdd beside them already returned an error, and to LocalRun. The receive tests were calling t.Fatal from inside a goroutine, which is not valid; they carry the error out and check it at the join instead. Proven against a real peer connection: a raw frame naming 2^40 is refused at once, and without the walk the receiver does not answer within five seconds. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
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.
|
||
|
|
676805acea | WIP: session checkpoint 2026-06-05 (codec rip in flight) | ||
|
|
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 |
||
|
|
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).
|