5 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 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]>
2026-08-26 14:47:26 -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 676805acea WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:33:52 -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 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