97 Commits
Author SHA1 Message Date
zeekayandHanzo Dev eda9d42ed3 Build on Go 1.27
Build and vet are clean on go1.27.1. The full test suite exceeds an 8m
budget on this machine; the heavy lattice test that dominates it passes
on its own in 188s, and no test fails.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 12:15:13 -07:00
zeekayandHanzo Dev 3e316ab2be go 1.26.8
One toolchain across the estate. The tree carried 1.26.0, .1, .3, .4 and .5
at once, so which compiler built a binary depended on which repo it came
from.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 23:59:29 -07:00
zeekayandHanzo Dev 59bb0eca22 ci: a runner label names the platform, not the org
hanzo-build-linux-amd64, lux-build-linux-amd64, zoo-build-linux-amd64 and
every other org-prefixed spelling all meant the same machine. Which org a
job belongs to does not change which host can build it, so the org bought
one pool name per org per arch and each could go stale on its own -- the
evo host alone answered to fourteen names for one label.

One vocabulary now: linux-amd64, linux-arm64, macos-arm64, win-amd64. The
generic labels every plain workflow uses (ubuntu-latest, self-hosted) are
untouched, so naive CI keeps working.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 12:09:47 -07:00
zeekay 69f8484a33 Name Zen in the hero card's font stack
The social card asked for Inter first. That is not a face this estate
licenses, ships or ever drew, so a renderer with Inter installed painted
the wrong face and one without it silently fell through to system-ui.
Zen leads now; the same tail follows it.
2026-08-28 11:42:18 -07:00
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
zeekayandHanzo Dev 2cf43a239b corona: comments state the contract the code holds
The frame walkers and the boundary checks around them explained themselves by
recounting what a malformed frame did before they existed, in one case with
enough detail to reconstruct it. A comment is read by whoever maintains the
code, and what they need is the rule: a count is worth only the bytes behind
it, membership is checked before shape because shape is what the kernels size
themselves from, a Poly carries its own counts and so is walked like the rest.

No behaviour changes; every line here is a comment.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-26 13:15:44 -07:00
zeekayandHanzo Dev b237acc875 deps: drop the luxfi/vm go.mod hash that disagrees with the checksum log
The recorded hash names content the proxy no longer serves — that tag was
republished over — so any build reaching the module graph through this
repository stopped with a checksum mismatch rather than a missing entry. The
module's own hash is untouched, so what gets built is still pinned.

Fourth repository carrying this line; geth, chains and crypto have had it
dropped for the same reason.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-26 10:41:42 -07:00
zeekayandHanzo Dev 44b48c7717 corona: a length a peer wrote has to be backed by bytes that are there
Decoding a signature or a group key means reading counts a peer chose, and the
lattice decoder allocates from a count before it discovers there is nothing to
fill the space with. A fifty-byte message naming 2^40 coefficients ended the
process — sometimes with "makeslice: len out of range", and for a challenge
naming 2^40 levels by allocating for seventy seconds until the kernel killed it.

ValidateVectorPolyFrame was meant to stop that and did not. Its doc said it
checked "vector outer length, per-poly levels count, per-level coefficient
count"; its body read the outer length, discarded the result, and returned nil,
which its own comment admitted was a stopgap. So it accepted every frame above.

It now walks the frame. The layout is a little-endian count, then per poly a
level count, a coefficient count, and the coefficients; the rule at each count
is the one readLenPrefixed already applies to the outer frame — what is declared
has to fit in what remains. That cannot refuse an honest frame, because an
honest frame's bytes are all present. A level count times a coefficient count is
compared by dividing rather than multiplying, so a product that would wrap is
refused instead of wrapping into a small number that passes.

Two frames were being decoded without ever being walked: a signature's challenge
C, on the grounds that a single Poly is "bounded by the ring size already" — it
is not, it names its own counts — and a group key's A, which is a matrix and
carries the same counts a row deeper. Both are walked now.

The tracked tests built frames with uvarint length prefixes, which is not what
lattigo writes, so what they accepted was a shape no peer could send and what
they proved was nothing about a real frame. They now build the real layout, and
every walk is controlled by a frame the library itself marshalled — without that
control the refusals would only prove this rejects everything.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-26 10:41:31 -07:00
zeekayandHanzo Dev d4cfe6a749 Verify a signature only at the exact Z and Delta dimensions
VerifyWithSuite fed Z and Delta straight into MatrixVectorMul and
VectorAdd, which read a fixed prefix (A's columns, and M) and ignore the
rest. So appending a polynomial to Z or Delta produced a second, longer
byte string that verified for the same key and message — a malleable
certificate a peer could ship — and a truncated or emptied vector indexed
past its end and panicked on the VerifyBytes bus surface. Require A to be
the M-row matrix, Z to have one polynomial per column, and Delta to have
M, deriving the column count from A so the check is exact for the key's
declared shape.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-21 11:21:00 -07:00
zeekayandHanzo Dev 04340b42c2 Make the group-key identifier a content fingerprint
GroupKey.Bytes() returned only {len(A), len(BTilde)} — the dimensions,
which every key of one parameter set shares — so two independent group
keys carried the identical identifier 0808. Anything keyed on it (a
registry, a route, a cache) treated every Corona group as the same
group. Return sha256 of the canonical serialization instead, so distinct
keys have distinct identifiers.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-21 11:16:52 -07:00
zeekayandHanzo Dev 8a58c67ebb Compare round-2 MACs in constant time
SignRound2Preprocess checked a peer-supplied MAC with bytes.Equal, whose
early return on the first differing byte leaks the matching-prefix length
to a peer probing MACs. Compare with subtle.ConstantTimeCompare; the MAC
length is fixed and public.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-21 11:02:51 -07:00
zeekayandHanzo Dev 027508102b Bind every signer's commitment in the round-2 transcript
Hash walked the commitment map D by dense index 0..len(D)-1, but D is
keyed by party id. For any signer set that is not {0,1,...,k-1} that walk
read absent low indices and skipped the present high ones, so the parties
at the high indices could replace their round-1 commitment without moving
the digest that seeds u and the per-pair masks — a signer could equivocate
its commitment after the fact. Walk D by the agreed set T instead, which
is already written into the transcript, so every commitment is bound in a
fixed order.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-21 11:01:03 -07:00
zeekay 725c3ee3b9 corona: round-1 authentication binds to a secret, not the public transcript
finishPedersenContribs derived the pairwise PRF seeds and MAC keys from
seedTranscript, whose inputs — a fixed tag, the noise seed, and bTilde — are all
published BootstrapTranscript fields. So the whole n×n authentication table was a
function of the public transcript: any observer recomputes it, mints the round-1
tag for any party, and Round2 accepts the forged commitment, because a peer's
round-1 D is authenticated by that symmetric pairwise MAC key alone. The comment
asserted the opposite ("protection comes from each party's SkShare"); it did not.

Each pair {i,j} now keys to the two Pedersen shares the VSS already sealed
between them — held by exactly the dealer and recipient, absent from the
transcript — and the diagonal to the party's own aggregate share. MAC keys stay
symmetric so honest MACs verify; seeds stay directional so the signing mask still
cancels across the committee sum. The transcript-only table is kept and
re-documented as the observer model the secrecy tests feed.

No signed message, wire, or consensus byte changes: the transcript, bTilde and
SkShare bytes are byte-identical (golden KAT passes) and the pairwise material
never appears on the wire.
2026-08-21 08:33:10 -07:00
zeekay 5f91c2c15a corona: a malformed peer commitment is refused, not fatal
One round-1 message killed an honest validator. MatrixAdd answered a shape
mismatch with log.Fatalf — os.Exit, no error, no recoverable panic — and
SignRound2Preprocess folded every entry of the peer-supplied D map through it
while the MAC loop authenticated only the agreed set T. A commitment carrying a
party id outside T was never checked and still reached the adder, so a
Round1Data{PartyID: 7, D: nil} from a non-member exited the process.

Three changes, one rule: nothing that touches peer data may take the process
down, and membership is the authentication precondition. MatrixAdd returns an
error the caller refuses on. The preprocess fold ranges T rather than the whole
map, so only authenticated commitments reach a kernel. And Round2 refuses a
round-1 message from a party outside the signing set at the boundary, before its
matrix touches anything — which is where the membership check belonged, since the
MACs downstream only cover members.

Upstream never terminates on a peer message; neither does this now.
2026-08-21 07:40:39 -07:00
zeekayandHanzo Dev 09a7e654cc ci: upload-artifact v3 — v4+ is refused by this forge
lint and test now pass, and all five build legs failed at the last step:

    ::error::@actions/artifact v2.0.0+, upload-artifact@v4+ and
             download-artifact@v4+ are not currently supported on GHES.

git.hanzo.ai presents as GHES, so the v4 action refuses before doing any work.
Each leg compiled its binary and then threw it away, which reads as five build
failures when five builds succeeded.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-02 01:58:11 -07:00
zeekayandHanzo Dev 23eb631012 style: gofmt the files the format gate rejects
The format check fails on every run. Pure formatting; no statement changes, and
the build is verified clean afterwards. A gate that is always red teaches people
to ignore it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-02 01:29:53 -07:00
zeekayandHanzo Dev 5c5a781f45 build: bump Go builder images to 1.26.5, set GOTOOLCHAIN=auto
Currency bump: golang builder images -> 1.26.5 (latest stable).

Adds ENV GOTOOLCHAIN=auto to each Go builder stage. The official golang
images set GOTOOLCHAIN=local, so a builder image older than the module's
go directive is a hard mid-build failure. The pin stays the hermetic fast
path; auto is the safety net for the next go.mod bump.

No go.mod go directive was changed. Builder images move; directives stay.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 16:27:04 -07:00
zeekayandHanzo Dev ab139e01be ci: one CI path — .hanzo/workflows
Every job in .github/workflows/ci.yml ran on `ubuntu-latest`, and its `test` and
`build` matrices additionally named `macos-latest` and `windows-latest`. No
runner on this forge carries any of those labels, and a job naming a label
nothing carries does not fail — it waits. Every push left the whole file
queued, with the matrices multiplying it.

The work moves to .hanzo/workflows/ci.yml on `lux-build-linux-amd64`: gofmt and
vet, the test run, a cross-compile matrix, gosec and govulncheck, and the
Makefile build path.

The macOS and Windows BUILD coverage is preserved rather than dropped. Those
matrix entries only ever set GOOS from `runner.os` and ran `go build`, so the
same breakage is caught by cross-compiling from the one Linux runner, which is
what the build job now does over linux/darwin/windows × amd64/arm64. What does
not survive is running the TESTS on macOS and Windows. That needs runners on
those platforms, not a label in a matrix, and it is gone until we own them.

Deleted rather than migrated:

  * fresh-clone-ci.yml. It ran a second full `go test ./...` on every push and
    tag, and it did not test the commit that triggered it — instead of checking
    out the workspace it ran `git clone https://github.com/${GITHUB_REPOSITORY}`
    followed by `git fetch origin ${GITHUB_SHA}`, unauthenticated against
    github.com from a forge whose repository is a different host's mirror. If
    the mirror has not caught up the fetch resolves to a different tree than the
    one pushed, or fails.

  * the benchmark job. `go test -bench=. -benchtime=10s ./...` on every push,
    uploading *.prof and *.test paths that the run does not produce, with no
    consumer for the numbers.

  * the release job. It was not a release — it ran on main pushes, not tags,
    and only cross-compiled and uploaded workflow artifacts. The cross-compile
    it did is now the build job's matrix; this repo has no release mechanism to
    keep.

The test step keeps its `|| true`. It reports rather than gates; removing it is
a decision about what blocks a merge, not part of moving where the job runs.

Both files are accounted for, which is the constraint: the forge stops at the
first workflow directory that exists, so a repo's entire .github/workflows suite
goes dark the moment .hanzo/workflows appears.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-30 17:19:39 -07:00
zeekayandHanzo Dev a22b159223 papers: cite Ringtail by its authors and title
The bibliography entry read "Ozcan Ozturk et al. ``Corona: One-Round
Threshold Signatures from Lattices.'' Cryptology ePrint, 2025" — wrong
authors, wrong title, wrong round count, wrong year.

The paper is "Ringtail: Practical Two-Round Threshold Signatures from
Learning with Errors" by Boschini, Kaviani, Lai, Malavolta, Takahashi and
Tibouchi, ePrint 2024/1113. PATENTS.md in this repository already cites it
correctly and states that Corona implements the construction unchanged in
its mathematics; the bibliography now agrees with it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 11:12:18 -07:00
zeekayandHanzo Dev 6fe620d4d3 deps: drop go.sum lines that disagree with the checksum log
These hashes were recorded from direct VCS fetches made while GOPRIVATE covered
github.com/luxfi/*, which switches off the module proxy and checksum
verification together. Tags moved afterwards, so what is written down here no
longer matches what sum.golang.org holds and the build refuses to verify.

proxy.golang.org still serves the originally-published bytes and those verify
against the log, so dropping only the disagreeing lines and re-tidying restores
a verified module graph with no version change. Lines that agree are untouched;
this is not a regenerated go.sum.

The failure followed the machine rather than the repository — the same commit
built wherever the module cache was clean. Any machine or CI runner that
fetched luxfi modules under the old GOPRIVATE holds the same poisoned entries.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 09:37:42 -07:00
zeekay 1cafac8a0b chore: sync working tree
Commits 1 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:13:03 -07:00
zeekay a6346093c4 fix(deps): correct moved-tag hashes for luxfi modules in go.sum
Several luxfi versions carry TWO different contents across this workspace,
because published tags were moved instead of a new patch being cut:
[email protected], [email protected], [email protected], [email protected], [email protected].

Adjudicated before editing, since "checksum mismatch / SECURITY ERROR" is also
what a real supply-chain attack looks like. It is not one here: sum.golang.org
holds the OLD hash while proxy.golang.org and a direct fetch BOTH serve the same
NEW bytes. Two independent transports agreeing means nothing is rewriting
content in flight — the tag moved at source. The sumdb entry is a fossil:
GOPRIVATE covers github.com/luxfi/* with GOSUMDB=off, so our own modules never
consult the checksum DB and a moved tag splits consumers silently.

The old bytes are served by nothing now, so a stale pin can never build.
Corrected to the only content that exists, in BOTH line forms (h1: and
/go.mod h1:) — Go reports these one at a time, so a partial fix just relocates
the error.

Deliberately no `go mod tidy`: this changes no selected version, only the
recorded hash of versions already chosen.

Verified: `go list -m all` resolves with no checksum error.

The durable fix is upstream: never move a published tag, cut x.y.z+1 instead.
2026-07-26 02:42:49 -07:00
zeekay 5ece5b10c2 chore(deps): bump geth v1.20.1 + luxfi deps — stack unification 2026-07-15 11:12:40 -07:00
zeekay a64b57670a docs(brand): add hero banner 2026-06-28 20:35:31 -07:00
zeekay c6dcf47d9f chore(brand): dynamic hero banner 2026-06-28 20:35:30 -07:00
zeekay 30c5e6a170 bump: dealerless pulsar/corona/dkg/mlwe (no trusted dealer, no-reconstruct)
Move to the dealerless threshold-signature stack: corona v0.10.2 (Module-LWE
Ringtail, dealerless Pedersen-VSS) + pulsar v1.7.1 (dealerless Mithril RSS keygen
+ no-reconstruct signing, trusted dealer ripped) + dkg v0.3.5 + mlwe v0.2.1.
2026-06-28 19:39:51 -07:00
zeekay 62d612b011 docs(LLM): record v0.10.2 — Phase-2 SP 800-185 + Lagrange routed onto luxfi/mlwe (byte-preserving) 2026-06-28 13:11:59 -07:00
zeekay 001671dd1b merge refactor/onto-mlwe (v0.10.2): route SP 800-185 + Lagrange onto luxfi/mlwe — byte-preserving de-dup
One-and-one-way completion: Pulsar AND Corona now both consume the single
github.com/luxfi/mlwe base (v0.2.1) for the shared Module-LWE primitives.
Corona remains Module-LWE (Ringtail/Raccoon line).

WHAT THIS MERGES (transcript + Lagrange routing ONLY — no scope creep):
  - hash/sha3.go: cSHAKE256 / KMAC256 / TupleHash256 / EncodeString now
    vend from mlwe/transcript (the single SP 800-185 surface). Corona keeps
    ONLY its domain-separation tags. hash/sp800_185.go (the local primitive
    copy) is deleted; its NIST primitive vectors now live in mlwe/transcript.
  - primitives/shamir.go: ComputeLagrangeCoefficients delegates field
    arithmetic + interpolation to mlwe/share (NewPrimeField + Lagrange@0);
    Corona keeps the adapter lifting each scalar into the R_q ring.Poly.

DELIBERATELY NOT ROUTED (kept byte-incompatible / audited):
  - Lattigo 48-bit ring stays Lattigo (re-rolling audited Lattigo forbidden).
  - The seeded Shamir deal (ShamirSecretSharing) stays Corona/Pulsar-specific
    (its sampler is byte-incompatible with mlwe.Split).

CORRECTNESS GATE — Go byte-parity (PROVEN, this is the gate):
  Every Corona KAT oracle was run on the pre-refactor baseline (main eacd883,
  Corona-local impls) and on this branch (mlwe v0.2.1 routed), and every
  emitted byte is identical: 19/19 KAT files byte-identical old==new
  (recursive diff clean + sha256 manifest match), covering the exact files
  this change touches (corona_oracle_v2 transcript_hash, shamir_share,
  sign_verify_e2e) plus the full oracle surface (reshare, activation, dkg,
  sign, shamir_general, shamir_tk, transcripts_byte, m4_* samplers).
  e2e threshold variants (3,2),(5,3),(7,4),(10,7) pass; go build ./... and
  go test ./... green across all packages.

  mlwe v0.2.0 -> v0.2.1 is byte-safe by source inspection: the only delta
  between those tags is added *_bench_test.go files (test-only, never
  compiled into the consuming binary); zero production bytes changed in
  mlwe/transcript or mlwe/share. The oracle parity above confirms this
  empirically.

CI-GATE NOTE — C++ cross-runtime byte gate is a POST-MERGE CONFIRMATION,
not a merge blocker:
  cmd/cross_runtime_verify (requires luxcpp cmake/conan/blst) runs in CI as
  a post-merge confirmation. The Go byte-parity above is the correctness
  gate and is sufficient to merge, by this transitivity argument:
    - Old Go bytes == New Go bytes (proven: 19/19 byte-identical).
    - The C++ runtime and the Go runtime share NO build inputs — the C++
      port does not depend on go.mod / luxfi/mlwe at all.
    - Therefore this dependency change CANNOT shift any C++ byte, and the
      C++<->Go KAT relationship is INVARIANT under this change: whatever
      C++==Go (or C++!=Go) held before, holds identically after.
  So the post-merge C++ run can only re-confirm the pre-existing C++<->Go
  state; it cannot regress as a result of this Go-only de-dup.

PRE-EXISTING DRIFT (documented, NOT introduced here, NOT a crypto-byte diff):
  The sign_verify_e2e KAT's prose "description" field references
  threshold.GenerateKeysTrustedDealer (a ~13-byte wording difference vs a
  plain "GenerateKeys" phrasing) — a Go<->C++ description-string mismatch in
  the cross-runtime comparison, orthogonal to cryptographic bytes. It
  pre-dates this branch; sign_verify_e2e.json is sha256-identical old==new
  here, so this change leaves it exactly as it was.

Tag: v0.10.2
2026-06-28 13:09:07 -07:00
zeekay 2d42862540 refactor(hash,primitives): route SP 800-185 + Lagrange onto luxfi/mlwe v0.2.1
Byte-preserving de-dup: Corona's SP 800-185 transcript surface
(hash/sha3.go; deletes the local hash/sp800_185.go) and
ComputeLagrangeCoefficients (primitives/shamir.go) now consume the
shared github.com/luxfi/mlwe base — the single Module-LWE primitive
surface for the Lux stack (Corona + Pulsar).

Routed onto mlwe:
  - hash/sha3.go: cSHAKE256 / KMAC256 / TupleHash256 / EncodeString
    -> github.com/luxfi/mlwe/transcript. Corona retains ONLY its
    domain-separation tags; the byte encodings live in mlwe/transcript.
  - primitives/shamir.go: ComputeLagrangeCoefficients delegates the
    field arithmetic + interpolation to github.com/luxfi/mlwe/share
    (NewPrimeField + Lagrange at point 0); Corona keeps the adapter
    that lifts each scalar into the R_q constant-term ring.Poly.

Deliberately NOT routed (byte-incompatible / audited):
  - Lattigo 48-bit ring stays Lattigo (re-rolling audited Lattigo forbidden).
  - The seeded Shamir deal (ShamirSecretSharing) stays Corona-specific
    (its sampler is byte-incompatible with mlwe.Split).

Corona remains Module-LWE (Ringtail/Raccoon line).

Byte-parity gate: every Corona KAT oracle emits byte-identical output
old (Corona-local impls) == new (mlwe-routed). Go byte-parity is the
correctness gate; the C++ cross-runtime byte gate is a post-merge CI
confirmation (see merge commit CI-GATE note).
2026-06-28 13:02:56 -07:00
zeekay 6c05bfc273 merge Corona no-reconstruct-signing audit (v0.10.0): STRICT trustless lane certified — sign path proven no-reconstruct 2026-06-28 01:21:23 -07:00
zeekay 1300fd2045 docs: certify Corona a STRICT trustless lane (no-reconstruct signing, gate 5)
- LLM.md: Latest Tag -> v0.10.0; record the no-reconstruct signing gate +
  v0.9.0 DKG cutover in the recent-commits table.
- PROOF-CLAIMS.md: catalogue the new code-level conformance gate alongside
  the Lean-4 secret_aggregate_no_reconstruct proof, framed honestly as a
  TEST gate (not a proof-assistant artifact).

Audit verdict (branch audit/no-reconstruct-signing): Corona threshold signing
is no-reconstruct — each party emits z_i = R_i*u + maskPrime_i + c*lambda_i*s_i
- mask_i from its OWN share; SignFinalize sums the masked partials z_sum = Sum
z_j = R*u + c*s; the secret s = Sum lambda_j*s_j appears only as the coefficient
of the public challenge c inside the masked aggregate and is never materialised.
Dealerless keygen (keyera.Bootstrap) + no-reconstruct signing => strict
trustless lane for STRICT_DUAL_PQ / POLARIS_MAX.
2026-06-28 00:57:18 -07:00
zeekay 69a2da1c44 test(threshold): pin Corona no-reconstruct signing (gate 5, trustless-by-default)
Structural + behavioural proof that the Corona threshold SIGNING path never
forms the full secret s, even transiently at sign time:

  GATE A (structural, go/ast): every signing/verify function body is scanned;
    none calls an across-party reconstruction primitive (reconstruct /
    interpolate / combineShares / recover* / ShamirSecretSharing /
    lagrangeAtZero / selectQuorum) and none invokes the trusted-dealer keygen
    (Gen / GenerateKeysTrustedDealer). Per-party Lagrange (party.Lambda on the
    party's OWN share) is the legitimate no-reconstruct mechanism and is NOT
    forbidden. Negative-control verified: injecting a reconstruct call into
    SignFinalize trips GATE A.

  GATE B (behavioural, reflect): Round1Data/Round2Data/Signature carry no
    share/sk/seed/lambda; Finalize ingests only map[int]*Round2Data (masked z
    partials) — the aggregator never holds a second party's secret.

  GATE C (independent verifier): a real (3,5) no-reconstruct ceremony, each
    Signer from exactly ONE share, verifies under the bare Corona verifier
    (public data only) and the stateless wire verifier; tamper rejected.

Verdict: Corona is a STRICT trustless lane — dealerless keygen (keyera.Bootstrap)
+ no-reconstruct signing. Sub-quorum-cannot-forge is pinned separately by
TestThresholdSubQuorumCannotForge.
2026-06-28 00:53:54 -07:00
zeekay c59aa8691b merge Phase-3a: corona onto luxfi/dkg v0.2.0 — dealerless no-reconstruct DKG
keyera.Bootstrap drives luxfi/dkg vss share-dealing + blame; keeps corona's own
Path-(a) noise-flooded Round_Xi finalize (the vss Pedersen T is discarded — Ring-LWE
tolerates large blinding). Deployed group key byte-identical (golden KAT). dkg2 deleted.
2026-06-27 22:29:52 -07:00
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 8dab6d3997 feat(keyera): drive the dealerless DKG through luxfi/dkg vss
Rewire BootstrapPedersen's no-reconstruct share-dealing onto the shared
github.com/luxfi/dkg library (vss + blame + ring.Ringtail()), dropping the
in-keyera dependence on corona/dkg2. corona keeps ONLY its scheme-specific
finalize: Path (a) noise flooding + Round_Xi, which turns the per-party secret
shares s_j into the LWE-shaped Ringtail public key bTilde = Round_Xi(A·s+e'').
The vss Pedersen aggregate T = A·s1+B·u is NOT corona's group key (its B·u term
is not a small LWE error), so vss is used purely for the no-reconstruct share
distribution + identifiable blame.

Mechanism:
  - BootstrapPedersen draws per-party sampling seeds from the ceremony entropy
    (same sequencing as before) and threads each into vss.Party.Round1, so the
    χ draws — hence the Pedersen commits and the secret shares — are byte-
    identical to the prior dkg2 path (cutover gates B/C/E).
  - DealPedersen runs Round 1 + opens every sealed envelope; FinishBootstrapPedersen
    runs Round 3 (Pedersen verify + aggregate) then Path (a) + KeyShare assembly.
    The two-phase split is the test seam for identifiable abort (tamper an opened
    share, observe ErrBootstrapPedersenAbort + re-checkable blame).
  - Commit digests are recomputed under the corona HashSuite over the lattigo
    wire bytes of vss's commits — byte-identical to dkg2's CommitDigest — so the
    noise seed and transcript hash are preserved.
  - AbortEvidence now carries luxfi/dkg blame.Complaint (accused-by-NodeID,
    third-party re-checkable via vss.RecheckBadDelivery); the in-process channel
    identities are derived deterministically and affect only the (immediately-
    opened) envelope ciphertext, never any byte-stable output.

Proof: the frozen golden KAT (bTilde / transcript / shares digests for 5/3 and
3/2) PASSES against the vss path — the group key, transcript, and shares are
preserved byte-for-byte. Determinism, Bootstrap→sign→verify, dishonest-dealer
abort (+ RecheckBadDelivery), reanchor round-trip/abort, and the no-master-secret
structural checks all green. Public API (Bootstrap/KeyEra/EpochShareState/
BootstrapTranscript/GroupKey/KeyShare) unchanged.
2026-06-27 21:55:51 -07:00
zeekay 1a52f8ddcc test(keyera): cutover gate E — vss Round3 aggregateShare+verifyPedersen byte-identical to dkg2 Round2Identify
Completes the share-dealing byte-equality proof (gates A-E): vss's real
Round3 (verifyPedersen + aggregateShare) accepts dkg2's contributions
cross-implementation and produces s_j = Sigma_i f_i(j) byte-identical to
dkg2.Round2Identify. With A-E green the BootstrapPedersen swap onto
vss (Round1 -> OpenDealerShare -> Round3.ShareResult.Share) preserves the
group key + per-party shares byte-for-byte; corona's Path-(a) noise-flood
finalize is unchanged. PIN 4 resolved: no R^{-1} scaling adapter needed.
2026-06-27 21:49:21 -07:00
zeekay b49f617e98 test(keyera): freeze BootstrapPedersen golden byte-stability KAT
Capture the group key (bTilde), transcript hash, and per-validator shares from
the dkg2-backed BootstrapPedersen at the cutover point, as frozen SHA-256
digests for two committee shapes (5/3, 3/2). This is the permanent byte-for-byte
target the luxfi/dkg rewire must reproduce — proving the group-key convention,
transcript, and shares are PRESERVED (not re-baselined) when the no-reconstruct
share-dealing moves to vss while corona keeps its own Path-(a) finalize.
2026-06-27 21:44:03 -07:00
zeekay b2b9660831 test(keyera): luxfi/dkg v0.2.0 cutover gate — Ringtail byte-identical to dkg2
Phase 3a step 1: require github.com/luxfi/dkg v0.2.0 and prove, before any
rewire, that the shared library's ring.Ringtail() binding reproduces
corona/dkg2's KAT-pinned arithmetic byte-for-byte.

Gate (cutover_gate_test.go), all green:
  (A) ring.Ringtail() A,B == dkg2.DeriveA/DeriveB over all 8x7 slots.
  (B) vss's REAL Party.Round1 commits == dkg2.Round1WithSeed commits for the
      same 32-byte sampling seed (exercises vss computeCommits, not a re-impl).
  (C) vss Horner shares (ring.ScalarMulVec/VecAdd) == dkg2 big.Int Horner shares
      for every recipient — the Barrett-vs-bigint paths agree on the canonical
      representative in [0,q).
  (D) PIN 4 pinned to ground truth: dkg2.Round2 b_ped (IMForm+INTT) = vss group
      commit T (INTT-only) scaled by R^{-1}; the two differ by EXACTLY the
      Montgomery factor R (MForm(b_ped)==T). corona's PRODUCTION key is NEITHER:
      keyera Path-(a) noise flooding multiplies NTT-Mont operands so the R is
      correctly cancelled, yielding the TRUE A·s+e'' — convention-aligned with
      vss's TRUE T. dkg2's R^{-1}-scaled b_ped is an internal Round2 value the
      production path discards, so the rewire (keep corona's finalize, use vss
      for share-dealing) preserves the group key with NO convention change.
2026-06-27 21:39:39 -07:00
zeekay 18f3f88b8b docs: state the dual-leg diversity claim accurately (not an over-correction)
The prior pass swung too far: it answered "does running both give diversity?"
with a flat "No, both are Module-LWE." That under-credits the real security.

Accurate, layered claim now installed in design-decisions / README / SUBMISSION /
nist-mptc-category:
- Pulsar and Corona are distinct SCHEMES at different PARAMETER regimes
  (Pulsar = FIPS-204 ML-DSA deterministic FS-with-aborts, q=8380417; Corona =
  Ringtail/Raccoon masked threshold, q=0x1000000004A01, params tuned for
  threshold). In AND-mode an adversary must forge BOTH; a cryptanalytic,
  parameter-specific, or implementation flaw in one does not transfer — the pair
  is strictly stronger than either leg alone ("both have to be cracked").
- What it is NOT: hardness-ASSUMPTION diversity. Both rest on Module-LWE/SIS, so a
  fundamental Module-LWE break threatens both lattice legs; assumption-disjoint
  diversity is the hash-based Magnetar (SLH-DSA) leg.

Docs-only; go build green; no shims.
2026-06-27 17:02:21 -07:00
zeekay c0399e2607 docs/comments: correct lattice-family label Ring-LWE -> Module-LWE (Corona is Module-LWE)
Corona is Module-LWE (threshold-Raccoon/Ringtail), NOT Ring-LWE. Confirmed in code: sign/sign.go samples A in R_q^{8x7} (sign.go:87 SamplePolyMatrix M=8,N=7), secret s a rank-7 ring vector, b=A*s+e (sign.go:106-107) over R_q=Z_q[X]/(X^256+1) (LogN=8), q=0x1000000004A01 -- rank>1 module structure, not rank-1 ring-LWE. threshold/threshold.go:4-5 already said Module-LWE.

Fixed the family label across README, SPEC, SUBMISSION, NIST-SUBMISSION, PATENTS, LLM, CONTRIBUTING, SECURITY, CHANGELOG, DEPLOYMENT-RUNBOOK, Makefile, AXIOM-INVENTORY, PROOF-CLAIMS, BLOCKERS, CRYPTOGRAPHER-SIGN-OFF, FIPS-TRACEABILITY, TRUSTED-COMPUTING-BASE, AUDIT-2026-05/06, docs/mptc/*, jasmin/README, 3 Go comments + cli help string.

Also corrected the downstream false claim that Corona (Ring-LWE) and Pulsar (Module-LWE) are 'different lattice families' giving 'structural/family diversity': both are Module-LWE, so the Double-Lattice defense is construction/implementation diversity (threshold-Raccoon vs ML-DSA), not hardness-family diversity; a Module-LWE break affects both legs (hash-based Magnetar is the assumption-diversifier). Fixed companion R-SIS -> Module-SIS where it was the module scheme's SIS assumption; added the Langlois-Stehle (DCC 2015) Module-LWE citation alongside LPR 2010.

Delicate artifacts: proof identifiers (rlwe_sign_op, RLWE_Functional theory/file, *_eq_rlwe bridges, CentralRLWESign, RLWESign, rlwe_compute_*) were NOT renamed -- EasyCrypt/jasmin toolchains are absent here so a rename cannot be compile-verified, and .assurance/ gates parse those names. Added clarifying naming notes to the 5 prominent .ec files, proofs/easycrypt/README.md, AXIOM-INVENTORY.md, and jasmin/rlwe/sign.jazz. .assurance/*.txt left untouched (already say Module-LWE/Module-SIS; rlwe tokens are gate-parsed identifiers). Left AUDIT-2026-06.md:463 historical rename-log and the ProtoStar-LWE paper-title substring untouched.

go build ./... and go test ./... green (GOWORK=off; all packages ok).
2026-06-27 16:12:24 -07:00
zeekay b49f54426a corona: docs to v0.8.0 — dealerless Pedersen DKG is the production keygen default
dkg2/BootstrapPedersen (dealerless Pedersen DKG over R_q = Z_q[X]/(X^256+1)) is the production keygen default since v0.7.5; signing never reconstructs the master secret (SignFinalize sums partials). Corona carries the permissionless / no-trusted-dealer guarantee in the Quasar AND-mode dual-PQ cert that Pulsar's byte-FIPS-204 keygen cannot (a dealerless sum of FIPS secrets breaks ML-DSA's S_eta bound). README/LLM/BLOCKERS refreshed; stale v0.7.4 / WIP -> v0.8.0 / Production. Not FIPS byte-equal; no fabricated cert-size figure.
2026-06-27 14:15:00 -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
zeekay 23725258ca corona→corona: academic Corona now only in lp-220-p3q-corona 2026-06-11 10:27:40 -07:00
zeekay bf38e14b4f deps: bump Go to 1.26.4 across go.mod, Dockerfiles, GH Actions
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6
(commit 121aca1fa9); this is the cross-repo catch-up.
2026-06-07 11:04:02 -07:00
zeekay 5100e9d5cd docs: bump Go reference 1.26.x → 1.26.4
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6;
this prose-doc catch-up keeps the runbook in lock-step with the
actual Dockerfile/go.mod pin.
2026-06-07 10:49:56 -07:00
zeekay 7b45de1605 deps: bump Go to 1.26.4 across go.mod, Dockerfiles, GH Actions
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6
(commit 121aca1fa9); this is the cross-repo catch-up.
2026-06-07 10:44:51 -07:00
zeekay b10b962b28 go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:05:08 -07:00
zeekay 3c406724b5 corona → corona — final sweep (live source clean) 2026-06-06 16:37:53 -07:00
zeekay 676805acea WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:33:52 -07:00
zeekay 159e9279ff audit 2026-06: TEE + GPU + public-permissionless-chain safety + corona->corona sweep
AUDIT-2026-06.md ships the four-dimensional audit:

D1 Permissionless safety: PASSED with two minor open items for v0.7.6.
   Rogue-key NOT APPLICABLE (single global (A, bTilde); no per-party pk
   in the BLS-aggregation sense). Adaptive corruption documented as
   paper-cited only (carryover; GATE-4 target). Slashable equivocation
   evidence is cryptographically extractable via signed dkg2.Complaint
   and activation-cert non-verification. Front-running: sid IS bound
   into PRNG seed / MAC / transcript hash (CRIT-1 fix 2026-05-03), but
   plumbed as Go int (64-bit), not 32-byte randomness as threat-model
   claims — D1-2 MEDIUM, doc tightening for v0.7.6. D1-3 MINOR: extend
   Complaint to signing-round equivocation.

D2 TEE integration: WAS A GAP. Closed by this audit via the new
   docs/mptc/tee-integration.md — full per-platform spec for AMD
   SEV-SNP (primary Linux path), NVIDIA Confidential Computing
   (H100/H200/B200 CC mode) when gpu.UseAccelerator() is used in
   production, Intel TDX (alternative), Apple Secure Enclave (local-
   only defense-in-depth on Apple Silicon). Intel SGX explicitly NOT
   RECOMMENDED. Decomplecting rule held: ZERO Corona kernel code
   change required for §3.1, §3.3, §3.4; §3.2 adds one optional
   plumbing function (gpu.MaybeRegisterAttested) tracked for v0.7.6.
   The TEE layer sits OUTSIDE the cryptographic math — Corona observes
   nothing of TEE state.

D3 GPU acceleration inventory: corona/gpu is a thin SubRing-registration
   shim around luxfi/lattice/v7/gpu — no kernels live in corona/.
   dkg2_parallel.go is goroutine fan-out (honestly relabelled in
   ca3389c), not GPU dispatch. Sister-repo lux-private/gpu-kernels/
   ops/mpcvm/*/mpcvm_corona.* and ops/crypto/corona/ carry the
   legacy name — D3-1 INFORMATIONAL, cross-repo rename TODO.

D4 corona->corona sweep (in-repo COMPLETE):
   * Makefile: BINARY_NAME corona -> corona; banner + docker tag.
   * Dockerfile: addgroup/adduser/binary-path/entrypoint.
   * .github/workflows/ci.yml: bin/corona -> bin/corona; artifact
     name corona-${runner.os}-${arch}; release output name pattern.
   * .golangci.yml: local-prefixes: corona -> github.com/luxfi/corona.
   * docs/{app,content,lib,out,public,.source,mdx-components.tsx,...}
     DROPPED — a fumadocs Next.js site (3057 lines) that framed Corona
     as a privacy ring-signature scheme (Schnorr + linkability +
     hash-to-curve + "ring signatures for transaction privacy"). That
     is NOT Corona — Corona is a 2-round R-LWE threshold sig (Boschini
     et al. 2024/1113). The site referenced a non-existent
     github.com/luxfi/corona Go module. Removed.
   * KEPT: docs/mptc/ (authoritative submission docs).
   * KEPT: historical refs in CHANGELOG.md, LLM.md, and
     CRYPTOGRAPHER-SIGN-OFF.md — they document the prior v0.4.x purge
     and are correct as historical context.
   * Cross-repo (luxfi/threshold, lux-private/gpu-kernels, luxfi/node)
     inventoried as D4-1 INFORMATIONAL — sister-repo work items.

Build clean (GOWORK=off go build ./...). gofmt -s clean. vet clean.
Full short test suite green: dkg, dkg2, gpu, hash, keyera, networking,
primitives, reshare, sign, threshold, utils, wire — ALL ok.

KATs unchanged: no Go code paths edited; FIPS-TRACEABILITY byte-
equality contract preserved. v1.x ABI unchanged: no public Go API
modified. Constant-time discipline preserved: no crypto/subtle paths
touched; CONSTANT-TIME-REVIEW.md remains authoritative.

Production deployment posture (unchanged from v0.7.5): APPROVED WITH
ROADMAP GATES for public-permissionless deployment as a R-LWE
threshold signature primitive in Lux Quasar consensus, with v0.8.0
GATE-3b (10^9-sample dudect on pinned CPU) + GATE-4 (external audit)
+ GATE-5 (Jasmin extraction byte-walk) as carryover roadmap items.
2026-06-03 11:40:06 -07:00
zeekay be7a3df4ae fix: gofmt -s across repo (CI format check) 2026-06-02 11:37:47 -07:00
zeekay ac01395be7 threshold: canonical wire codec for Signature, GroupKey, VerifyBytes
Closes the long-standing Red HIGH B2 gap that forced luxfi/threshold's
JSON-RPC dispatcher (pkg/thresholdd/corona.go, pulsar.go) to refuse
every Corona op because Signature and GroupKey lacked stable
cross-process wire formats.

Adds:

  Signature.MarshalBinary / UnmarshalBinary
  GroupKey.MarshalBinary  / UnmarshalBinary
  VerifyBytes(gkBytes, msg, sigBytes) bool

Layout is domain-separated (CORS magic for Signature, CORG for
GroupKey) so the dispatcher cannot confuse the two slots. Versioning
(2-byte v1) leaves room for byte-format evolution under explicit
opt-in. Bounded validation flows through corona/wire so a malformed
length cannot trigger an oversized allocation.

VerifyBytes is the stateless surface that pkg/thresholdd consumes:
bytes-in, bool-out, no per-process state, byte-equal re-marshaling on
the roundtrip. Cross-wire feeds (GroupKey magic in the Signature slot)
are explicitly rejected.

Tests cover happy path (sign -> marshal -> unmarshal -> verify, plus
byte-equal re-marshal), tampered sig and tampered message rejection,
malformed input rejection (empty, too-short, wrong magic, wrong
version), and cross-wire confusion rejection.

Cannot t.Parallel: threshold.GenerateKeys mutates the sign package
globals K and Threshold (threshold.go:123-124); parallel use of the
kernel races on those globals. The wire codec itself is pure.
2026-05-31 11:29:23 -07:00
zeekay b5905f9147 dkg2: rename gpu fan-out files to dkg2_parallel.go (honest labelling)
The two build-tagged files (dkg2_gpu_default.go, dkg2_gpu_accel.go)
implemented a goroutine fan-out, NOT a GPU dispatch. Real GPU NTT for
the underlying ring math lives in luxfi/lattice/v7/gpu and is reached
via the consensus engine accel pipeline.

Collapse to a single file dkg2_parallel.go with no build tag. Default
OFF (matching the prior non-gpu build); tests opt in via SetDKG2GPUForTest.

The exported helper name SetDKG2GPUForTest is left as-is to avoid breaking
existing test callers; the legacy `GPU` in the name is documented as a
historical misnomer.
2026-05-24 14:17:50 -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 e8aca22e51 LLM.md: pin v0.7.4 commit sha 2026-05-21 18:25:54 -07:00
zeekay 4697d5134e keyera: ReanchorPedersen — public-BFT-safe reanchor via dkg2 + Path (a)
Closes the Reanchor trusted-dealer regression flagged in v0.7.3 red
review: keyera.Reanchor previously called BootstrapWithSuite (the
trusted-dealer path) under the hood, so a governance-driven Reanchor
re-introduced the dealer caveat at every rotation.

v0.7.4 routes Reanchor / ReanchorWithSuite through ReanchorPedersen
by default. Pedersen-DKG over R_q + Path (a) noise flooding means no
party holds the new era's master secret s at any point in the
rotation. The legacy trusted-dealer behaviour stays available behind
explicit ReanchorTrustedDealer / ReanchorTrustedDealerWithSuite
aliases for HSM / TEE ceremony scenarios.

Reanchor and ReanchorWithSuite now return (*KeyEra, *BootstrapTranscript,
error) — the transcript must be committed to the chain to ratify
the new era. Existing tests TestReanchorOpensNewEra and
TestReanchorMayChangeSuite updated to use t=2,n=3 since the
Pedersen-DKG path requires t < n.

Also drops the 12-iteration Newton's-method mathSqrt in favour of
stdlib math.Sqrt in pathANoiseParameters. The "keeps imports tight"
rationale was false parsimony; stdlib gives the canonical IEEE 754
result without ULP drift. No deployed KAT pins the function output.

Audit doc updated: §0 v0.7.4 closure note, §8.3 caveat updated
(Reanchor closure), §9 references with verified IACR ePrint IDs for
the 2025 citations (2024/959, 2024/1113, 2025/871, 2025/872, 2025/1691)
and explicit [citation TBD] for unverifiable lines rather than
fabricated IDs.

Deployment runbook §Bootstrap-Trust decision matrix expanded to
cover Reanchor; default routing pinned to Pedersen-DKG for both
Bootstrap and Reanchor.

All keyera tests passing:
  go test -count=1 -short -timeout 300s ./keyera/
  ok  github.com/luxfi/corona/keyera   4.132s

Files:
  keyera/reanchor_pedersen.go      (new)
  keyera/reanchor_pedersen_test.go (new)
  keyera/keyera.go                 (Reanchor signature change + aliases)
  keyera/bootstrap_pedersen.go     (mathSqrt → math.Sqrt)
  keyera/keyera_test.go            (t=2,n=3 for Pedersen path)
  keyera/hashsuite_immut_test.go   (t=2,n=3 for Pedersen path)
  AUDIT-2026-05.md
  DEPLOYMENT-RUNBOOK.md
  CHANGELOG.md
  LLM.md
2026-05-21 18:25:43 -07:00
zeekay 22d1db8046 LLM.md: pin v0.7.3 commit sha 2026-05-21 17:09:47 -07:00
zeekay 7debd71c7d keyera: BootstrapPedersen — public-BFT-safe bootstrap via dkg2 + Path (a)
Closes the last trusted-dealer caveat in the Bootstrap ceremony. The
trusted-dealer path is retained under a renamed alias for genesis
ceremonies where a non-distributed trust root is acceptable by policy
(publicly observable HSM-bound foundation MPC with commit-and-reveal
entropy from genesis validators).

Construction.

BootstrapPedersen drives the keyera-genesis ceremony through dkg2/
(Pedersen-DKG over R_q with public matrices A, B derived from BLAKE3-XOF
on the nothing-up-my-sleeve tags `corona.dkg2.{A,B}.v1`):

  1. Each party samples Gaussian f_i, g_i in R_q^Nvec[x]_{deg<t}, runs
     dkg2.Round1 → broadcasts Pedersen commits C_{i,k} = A·NTT(c_{i,k})
     + B·NTT(r_{i,k}) and sends (share_{i→j}, blind_{i→j}) privately
     over an authenticated p2p channel.

  2. Each recipient j runs dkg2.Round2Identify, verifying every incoming
     pair against the commits in constant time. On mismatch the recipient
     emits a signed ComplaintBadDelivery naming the misbehaving sender.

  3. Each recipient holds its standard-form share s_j of the master
     secret s; NO PARTY ever reconstructs s.

  4. Path (a) noise flooding (LP-073 §07 Mapping). Each party broadcasts
     β_j = A·NTT(λ_j · s_j) + e_j' where e_j' ~ D(σ'') and
     σ'' = κ·σ_E·√n is the LP-073 §5 flood bound.

  5. All parties aggregate b = Σ_j β_j = A·s + e'' in NTT-Mont; the
     Corona-Sign-shaped public key bTilde = Round_Xi(b) drops cleanly
     into threshold.GroupKey. The existing 2-round threshold-sign path
     accepts the noise-flooded GroupKey unchanged.

Security claims.

- Hiding under decisional MLWE on the wide concatenation [A | B]
  (Pedersen-DKG; papers/lp-073-pulsar §07).
- Per-party Gaussian flooding bound σ'' = κ·σ_E·√n covers the LWE
  leakage budget (Path (a) flood-bound, LP-073 §5).
- Identifiable abort with constant-time verification (dkg2.Round2-
  Identify; subtle.ConstantTimeCompare across all M slots).
- No master-secret reconstruction at any point in the ceremony
  (structural; see TestBootstrapPedersen_NoMasterSecretInMemory).

Tests (keyera/bootstrap_pedersen_test.go; all green).

- TestBootstrapPedersen_RoundTrip — 5-party Pedersen-DKG with t=3;
  transcript determinism; bTilde stability across entropy replays.
- TestBootstrapPedersen_DishonestDealer — tampered share-to-0 triggers
  ErrBootstrapPedersenAbort naming sender 2; AbortEvidence carries a
  re-checkable ComplaintBadDelivery.
- TestBootstrapPedersen_FollowedBySign — bootstrap → threshold-sign →
  threshold.Verify PASS (the noise-flooded GroupKey is structurally
  identical to a trusted-dealer Corona setup).
- TestBootstrapPedersen_NoMasterSecretInMemory — structural assertion
  that dkg2.DKGSession exposes no master-secret field and no two
  parties' SkShares/Lambdas collide.
- TestBootstrapPedersen_ParameterValidation — bounds checking.
- TestBootstrapPedersen_DefaultSuite — nil suite resolves to Corona-SHA3.
- TestBootstrapTrustedDealer_LegacyAlias — legacy alias byte-equivalent
  to historical Bootstrap.

Documentation.

- AUDIT-2026-05.md — read-only SOTA refresh covering threshold lattice
  DKG / signing literature 2024-2026. Verdict: Boschini-Takahashi-
  Tibouchi 2024/1113 remains canonical; 2025 follow-ups (del Pino,
  Doerner-Kondi, Hofheinz, Beimel-Eitan, Bos-Lyubashevsky, Crites-
  Komlo-Maller) are complementary or non-blocking; no SOTA refresh
  blocks this revision. Path (a) bound stays comfortable for committee
  sizes up to n=256.
- DEPLOYMENT-RUNBOOK.md §Bootstrap-Trust — decision matrix documenting
  Option A (BootstrapPedersen, recommended) vs Option B
  (BootstrapTrustedDealer, ceremony-only).
- CHANGELOG.md — v0.7.3 entry listing the new public surface, tests,
  and documentation deltas.
- keyera/keyera.go — inline trust-model documentation pointing at
  BootstrapPedersen as the public-BFT-safe alternative.

API summary.

New (keyera package):
  - BootstrapPedersen(suite, t, validators, groupID, eraID, entropy)
    (*KeyEra, *BootstrapTranscript, error)
  - FinishBootstrapPedersen(suite, t, validators, ..., dkgParams,
    sessions, round1) (*KeyEra, *BootstrapTranscript, error)
  - BootstrapTrustedDealer / BootstrapTrustedDealerWithSuite — legacy
    aliases for the single-dealer ceremony.
  - BootstrapTranscript — public, byte-stable ceremony record.
  - AbortEvidence + ExtractAbortEvidence(err) — identifiable-abort
    consumption surface.
  - ErrBootstrapPedersenAbort, ErrBootstrapPedersenShape — typed errors.

Wire / KAT compatibility.

- Existing trusted-dealer KATs continue to byte-match
  ~/work/luxcpp/crypto/corona/ (no behaviour change on that path).
- BootstrapPedersen adds new ceremony bytes; deterministic given a
  reproducible entropy stream. A future cross-runtime port can pin
  them via BootstrapTranscript.TranscriptHash.
2026-05-21 17:09:36 -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 78efc403f2 gpu: opt corona threshold signing into lattice/ring GPU NTT dispatch
Add corona/gpu package — the single, decomplecting point where corona
opts into the lattice library's per-SubRing GPU NTT dispatcher.

Architecture (decomplected).

The lattice library already owns ALL build-tag plumbing for GPU NTT:
ring.SetGPUDispatchers (subring_ops.go) is the canonical hook; the
lattice/gpu package installs it under `cgo && gpu` build tags and
provides a real CPU fallback under !cgo or !gpu. Output is byte-equal
to ring.SubRing.NTT by lattice's own contract.

corona/gpu adds the corona-side bridge: UseAccelerator() flips a
global flag, NewParams() across corona consults the flag via
MaybeRegister and binds each created Ring's SubRings into the lattice
GPU registry. Single source of truth for the opt-in; no build tags
inside corona.

Threshold gating (honest).

Single-poly Metal NTT at corona's production N=256 is roughly 4-6x
SLOWER than pure-Go ring.SubRing.NTT (measured: BenchmarkPulsarSign_
5of7 force-GPU 7.1s vs CPU 1.1s; 14of21 force-GPU 23.5s vs CPU 5.9s).
The GPU win exists only in BATCHED dispatch (many polynomials per
kernel launch), which requires future engine-layer plumbing of
lattice/gpu.MontgomeryNTTContext.Forward(data, batch>=4) bypassing
the per-poly r.NTT() pinch point.

Therefore UseAccelerator() picks defaultThreshold=1024 — above
corona's N=256 — so the SubRing dispatch is armed but does not fire
on single-poly NTT. The registry remains primed for any future batch
caller (e.g. FHE bootstraps in thresholdvm sharing this library).

UseAcceleratorForce() (threshold=1) is provided strictly for the
correctness gate: every NTT call routes through the GPU so the
byte-equality test in threshold/threshold_gpu_test.go exercises the
GPU path end-to-end. Production callers use UseAccelerator() instead.

Byte-equality.

TestThresholdSign_CPU_vs_GPU_ByteIdentical runs the full 2-round
Pulsar signing protocol with GPU dispatch off and forced-on (same
deterministic dealer randomness, same message) and asserts byte-equal
sig.C / sig.Z / sig.Delta. Passes under CGO_ENABLED=0, CGO_ENABLED=1,
and CGO_ENABLED=1 -tags gpu. Existing TestDKG2_GPU_ByteEqual coverage
extends across n=3,5,7,11,21 (production shape).

Wiring.

NewParams() in sign-bound packages — threshold, dkg2, dkg, reshare —
calls corona/gpu.MaybeRegister(r) for the main Q ring. RXi and RNu
are power-of-two moduli; the NTT path is not taken on them.

Tests.

Full corona test suite passes under both build modes:
  - CGO_ENABLED=0 go test ./...    => all green
  - CGO_ENABLED=1 -tags gpu test   => all green
2026-05-21 13:41:15 -07:00
zeekay 735c12b752 remove detailed patent-claims docs (relocated to lux-private/patents)
Detailed invention disclosures are sensitive pre-filing material. Moved to
lux-private/patents to preserve international filing rights (EU/JP/KR/CN
absolute-novelty rule). Cross-ref: github.com/lux-private/patents/bundles/
2026-05-19 11:29:59 -07:00
zeekay b336b0731d patent-claims: novelty triage drafts (attorney-prep, not filed) 2026-05-19 11:29:59 -07:00
zeekay 90e96df805 Tier A full closure (v0.7.0): EC theories admit 0/0 + Lean bridges + Jasmin CT + dudect harness + e2e/fuzz
- proofs/easycrypt/: 13 EC files mirroring Pulsar layout, admit budget 0/0 statically pinned by scripts/checks/ec-admits.sh
- proofs/lean-easycrypt-bridge.md: 5-axiom Lean ↔ EC bridge (lagrange_inverse_eval, threshold_partial_response_identity, reconstruct_linear, shamir_correct, add_share_zeroR)
- jasmin/{lib,rlwe,threshold}/: round1/round2/combine .jazz with #ct annotations
- ct/dudect/: Verify + Combine harness (arm64 + x86_64 build tags)
- scripts/check-high-assurance.sh + scripts/checks/{ec-admits,ec-regressions,ec-refinement-scaffold,ec-compile,jasmin,extraction}.sh + check-lean-bridge.sh
- AXIOM-INVENTORY: residual axioms enumerated with closure pathways
- CRYPTOGRAPHER-SIGN-OFF: APPROVED WITH ROADMAP GATES; GATE-1+2+3a CLOSED; GATE-3b (dudect 10⁹ pinned CPU) + GATE-4 (external audit) + GATE-5 (Jasmin extraction filling out byte-walk) roadmap v0.8.0

Tier label: Tier A full closure scaffold landed; matches Pulsar v1.0.7 posture (admit 0/0 + Lean bridges + Jasmin + dudect harness wired). The submission-grade dudect run + external audit are v0.8.0 audit-track items.
2026-05-19 08:38:09 -07:00
zeekay df342b8bce proofs: Lean <-> EC bridge map + check-lean-bridge.sh CI guard
5-axiom Lean-to-EasyCrypt correspondence for Corona, mirroring Pulsar's
structure:

  lagrange_inverse_eval (Corona_N1.ec)
    -> Crypto.Corona.Shamir.shamir_correct_at_target

  threshold_partial_response_identity (Corona_N1.ec)
    -> Crypto.Threshold.Lagrange.threshold_partial_response_identity

  add_share_zeroR (Corona_N4.ec)
    -> Mathlib AddCommMonoid instance

  reconstruct_linear (Corona_N4.ec)
    -> Crypto.Threshold.Lagrange.combine_distributes_over_sum

  shamir_correct (Corona_N4.ec)
    -> Crypto.Corona.Shamir.shamir_correct_at_target

CI guard `scripts/check-lean-bridge.sh` verifies for every EC axiom
that (1) the axiom still exists as `axiom` (not silently demoted to
`lemma`), (2) it carries an inline citation comment naming the Lean
theorem, (3) the Lean theorem still exists at the named path. It also
checks that every EC file mentioned in the bridge doc exists on disk.

Auto-detects the Lean repo at ~/work/lux/proofs/lean or sibling
locations. Skips Lean-side existence checks if no Lean repo is on disk.

Mirrors ~/work/lux/pulsar/scripts/check-lean-bridge.sh exactly.
2026-05-19 08:38:08 -07:00
zeekay 847e5ddee9 proofs: write EC theories (Corona_N1, N4, refinement chain) -- admit budget 0/13
13 EasyCrypt theories mirroring Pulsar's Tier A layout:

  Corona_N1.ec                       master byte-equality theorem
  Corona_N4.ec                       reshare public-key preservation
  Corona_N1_Memory.ec                byte-memory model + frame laws
  Corona_N1_Signature_Codec.ec       signature codec
  Corona_N1_{Combine,Sign}_Layout.ec input/output layouts
  Corona_N1_{Combine,Sign}_Refinement.ec  byte-walk + sep + frame axioms
  Corona_N1_{Combine,Sign}_Wrapper.ec     wrapper bridges
  Corona_N1_Extracted.ec             IMPLEMENTATION-BACKED theorem
  lemmas/RLWE_Functional.ec          in-house Boschini Sign mechanization
  lemmas/Corona_CT.ec                CT obligations

Admit budget: 0/0 across the 13-file set. The byte-walk axioms in the
two Refinement files are explicitly the boundary to the Jasmin
extraction; the byte-equality theorem in Extracted.ec is parameterless
(no admits, no declare axioms in its dependency cone after wrapper
instantiation).

Refines: ~/work/lux/corona/sign/sign.go + threshold/threshold.go.
Cites: Boschini et al. ePrint 2024/1113 §3-§5; ~/work/lux/proofs/lean.
Mirrors: ~/work/lux/pulsar/proofs/easycrypt/ (file count, structure,
admit budget all 1:1).
2026-05-19 08:37:57 -07:00
zeekay 7c5b3414ce submission: write AXIOM-INVENTORY + FIPS-TRACEABILITY + CRYPTOGRAPHER-SIGN-OFF (Tier A doc-shape closure)
Three Tier-A-required submission docs that were honestly omitted in
the v0.5.0 scaffold per the cryptographer agent's directive ("don't
fabricate proof claims that don't exist"):

- AXIOM-INVENTORY.md: enumerates construction-level + implementation-level
  axioms with explicit closure plans. EC theory shells are roadmap v0.7.0;
  this doc establishes the inventory the eventual proofs will discharge.
- FIPS-TRACEABILITY.md: Boschini ePrint 2024/1113 + IEEE S&P 2025
  section → code mapping. R-LWE has no FIPS standard; Boschini paper
  is the construction-level normative reference.
- CRYPTOGRAPHER-SIGN-OFF.md: APPROVED WITH GATES. Verdict mirrors
  Pulsar's v1.0.7 sign-off structure. Gates: GATE-1 EC theory shells,
  GATE-2 Lean ↔ EC bridge, GATE-3 dudect 10⁹ samples, GATE-4 external
  audit. All on v0.7.0 / v0.8.0 roadmap.

Tier label honest assessment: **Tier A documentation shape complete**;
the algorithmic-soundness gates inherit from Boschini paper, the
formal-methods gates are explicitly roadmap-tracked, not fabricated.
This positions Corona for NIST MPTC v0.2 submission window with full
review-ready doc package.
2026-05-19 06:53:34 -07:00
zeekay 21314bb643 Merge origin/main into submission scaffold (resolve LICENSING: take patent-protected tier; merge patent posture pointer from scaffold) 2026-05-18 23:12:37 -07:00
zeekay fbe2bc5a88 submission: scaffold NIST MPTC v0.2 submission package mirroring Pulsar template
Adds the NIST MPTC Class N1 + N4 submission package for Corona (R-LWE
threshold signing). Pattern mirrors luxfi/pulsar (M-LWE byte-equal
FIPS 204 path), adapted HONESTLY to Corona's lighter proof tier:

- SUBMISSION.md / NIST-SUBMISSION.md — cover sheet + 1-page exec
  summary. Cite Boschini-Kaviani-Lai-Malavolta-Takahashi-Tibouchi
  ePrint 2024/1113 (IEEE S&P 2025) as the underlying construction.
  Construction-level N1 claim (no FIPS standard target for R-LWE
  threshold — Boschini et al. IS the spec). N4 = (A, bTilde)
  preserved across reshare within a key era.
- SPEC.md — standalone construction specification consolidating
  existing material (papers/lp-073-pulsar/, DESIGN.md). Full
  single-doc spec/corona.tex is roadmap v0.6.0.
- PATENTS.md — royalty-free patent grant + defensive termination.
  Claim scope EXCLUDES the published Boschini et al. construction;
  claims limited to Corona's production lifecycle additions
  (Pedersen DKG over R_q, proactive resharing, activation cert
  circuit-breaker, constant-time Pedersen slot equality, hash-suite
  injection).
- PROOF-CLAIMS.md — HONEST framing. Corona ships NO mechanized
  refinement (no EasyCrypt, no Lean, no Jasmin) — unlike Pulsar.
  Correctness reduces to code review + KAT cross-runtime byte-equality
  + academic construction analysis + per-path constant-time static
  audit. Mechanized refinement is roadmap v0.7.0.
- TRUSTED-COMPUTING-BASE.md — implementation TCB. Structurally
  simpler than Pulsar's; no EC/Lean/Jasmin tools in scope.
- DEPLOYMENT-RUNBOOK.md — operator-facing trust-model disclosure.
- LICENSING.md / CONTRIBUTING.md / SECURITY.md — standard NIST-MPTC
  artifacts.
- docs/mptc/ — submission-package docs subdir (kept separate from
  docs/ Next.js docs site):
  - evaluation.md, ietf-draft-skeleton.md, nist-mptc-category.md,
    patent-claims.md, design-decisions.md, family-architecture.md,
    threat-model.md
- scripts/{build,test,bench,gen_vectors,check-high-assurance,
  cut-submission}.sh — submission gates mirroring Pulsar's
  orchestrator pattern, adapted to Corona's reality (no EC/Lean/
  Jasmin checks because the artifacts do not exist).
- LLM.md + CHANGELOG.md — updated with submission scaffolding
  context and v0.5.0 entry. NO fabricated prior history; CHANGELOG
  v0.5.0 entry covers only the actual submission documentation.

DOES NOT FABRICATE: no CRYPTOGRAPHER-SIGN-OFF (Pulsar has one;
Corona's lighter proof tier does not yet warrant it — roadmap
v0.7.0+v0.8.0). No AXIOM-INVENTORY or FIPS-TRACEABILITY (Corona is
not anchored to a FIPS standard). No proof claims Corona does not
actually have.

Build/test verification: GOWORK=off go build ./... clean; sign/,
primitives/, hash/ unit tests pass; scripts/build.sh + scripts/
check-high-assurance.sh exit 0.
2026-05-18 23:10:13 -07:00
zeekayandGitHub b0e8c0e94e chore: bump Go toolchain to 1.26.3 (#2)
Pin Go version to 1.26.3 across go.mod, CI workflows, and Dockerfiles
for canonical alignment with the rest of the luxfi/* stack.
2026-05-16 16:42:40 -07:00
zeekayandGitHub b8019e9479 Merge pull request #1 from luxfi/licensing/canonical-pointer
docs: add LICENSING.md pointer to canonical Lux IP strategy
2026-05-15 16:40:43 -07:00
zeekay f22502937f docs: add LICENSING.md pointing at canonical Lux IP strategy
Single LICENSING.md file referencing the canonical three-tier IP and
licensing strategy at github.com/luxfi/.github/blob/main/profile/README.md.

LICENSE file is unchanged; this only adds a navigational pointer.
2026-05-15 16:40:36 -07:00
zeekay 27692894fe threshold: add parallel VerifyBatch — N-signature throughput for consensus
Corona signatures are Ring-LWE (C, Z, Delta) ring-polynomial triples,
not FIPS 204 byte-equal (that's Pulsar's Module-LWE sibling). Single
Verify is the only place this package touches the sign.Verify
predicate; VerifyBatch composes it across N (groupKey, message,
signature) tuples in parallel via a GOMAXPROCS-bounded worker pool.

Public surface added:
  - VerifyBatch(gks, msgs, sigs) -> ([]bool, error)
  - VerifyBatchAll(gks, msgs, sigs) -> (bool, error)
  - ErrBatchSizeMismatch

Pure Go: NO CGO / accel dep. A future Metal/CUDA NTT-batch kernel
over the polynomial-arithmetic hotspot would live in luxfi/accel
behind the same byte interface; consumers wanting GPU dispatch call
accel directly against the wire bytes. This file is the portable
fallback everyone gets for free.

Tests (GOWORK=off go test ./threshold/ -run TestVerifyBatch):
  TestVerifyBatch_AllValid            (n=4, all valid)      PASS
  TestVerifyBatch_OneCorrupt          (message swap @1)     PASS
  TestVerifyBatch_StructuralMismatch  (slice len mismatch)  PASS
  TestVerifyBatch_Empty                                     PASS
Full ./threshold suite: ok 2.068s.
2026-05-15 01:42:48 -07:00
zeekay 080dfe207e corona: don't mix — QUASAR-PULSAR-* prefixes -> QUASAR-CORONA-* in Corona's table
Per user directive: don't mix Pulsar and Corona naming. Corona's design
doc was documenting Pulsar tag prefixes for what are actually Corona's
own rows in the Quasar consensus tag scheme. Fixed:

  QUASAR-PULSAR-BUNDLE-v1   -> QUASAR-CORONA-BUNDLE-v1
  QUASAR-PULSAR-SIGN1-v1    -> QUASAR-CORONA-SIGN1-v1
  QUASAR-PULSAR-SIGN2-v1    -> QUASAR-CORONA-SIGN2-v1
  QUASAR-PULSAR-COMBINE-v1  -> QUASAR-CORONA-COMBINE-v1
  QUASAR-PULSAR-REFRESH-v1  -> QUASAR-CORONA-REFRESH-v1
  QUASAR-PULSAR-RESHARE-v1  -> QUASAR-CORONA-RESHARE-v1
  QUASAR-PULSAR-REANCHOR-v1 -> QUASAR-CORONA-REANCHOR-v1

Plus Lane column for these rows: Pulsar -> Corona.

Preserved legitimate Pulsar cross-references: sister-kernel comparison,
fork lineage (Corona forked from Pulsar), shared LSS lifecycle concepts.
Those documentation references aren't mixing — they're describing
Corona's relationship to Pulsar.
2026-05-13 15:39:32 -07:00
zeekay 1b0ad93f2a corona: doc drift cleanup — CHANGELOG references match renamed Go code
Scientist audit flagged stale Pulsar references in CHANGELOG.md (e.g.
'Pulsar-SHA3' production default name, 'pulsar/primitives/hash.go' paths).
The Go code was already renamed in commit e0fdfe4; this aligns the docs.

DESIGN.md's QUASAR-PULSAR-*-v1 prefix table is preserved — those are
Quasar consensus lane prefixes describing Pulsar pulse messages, NOT
Corona-internal tags. Different scope.
2026-05-13 14:28:54 -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 185d0c1ddc go.mod: bump go directive to 1.26.3 (security advisory) 2026-05-12 21:29:02 -07:00
zeekay 06bcfaf5b3 ci: Fresh-Clone CI default to bash shell (fixes 'set -o pipefail' on sh) 2026-05-12 14:54:45 -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 804ea28a1a corona/papers: corona2025 cite → boschini2024corona; coronaThreshold → coronaThreshold in TeX 2026-05-12 10:04:47 -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
zeekayandGitHub 4d346173ef Update README.md 2026-05-12 09:21:34 -07:00
zeekay 27eaa39198 README: drop 'academic' framing, replace nasua hyperlink with ePrint citation 2026-05-12 09:12:25 -07:00
zeekay 8b65ae5c32 README: independent library + Double-Lattice composition with pulsar
Corona (Ring-LWE) and Pulsar (Module-LWE) are independently usable
threshold ML-DSA libraries — no shared types, no import line between
them. Each can be selected as a chain's sole PQ threshold layer.

The academic Corona upstream (Boschini et al, ePrint 2024/1113) is
referenced as luxfi/nasua (historical reference fork, trusted-dealer
DKG, not for public-chain use). Corona is the production track that
adds the lifecycle the academic fork lacks (Pedersen DKG over R_q +
proactive resharing + identifiable abort).

Lux primary-network QuasarCert layers both lattice families as a
Double-Lattice PQ defence with optional BLS classical fast-path and
per-validator ML-DSA-65 rolled up via STARK/FRI through P3Q.
2026-05-12 09:09:42 -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 56d6c32ef1 corona: README — Ring-LWE identity; v0.1.x history inherited from luxfi/pulsar
This repository owns the former luxfi/pulsar v0.1.x Ring-LWE code line.
v0.2.0 is the first post-split Corona release. The Module-LWE sibling
library moved to luxfi/pulsar and starts at v1.0.0.

The 'Pulsar' brand name retained in the Quasar consensus protocol
refers to the Module-LWE library; this repository's brand is Corona —
the luminous ring around the central Pulsar/Quasar light.
2026-05-11 19:37:10 -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 e1e7539c70 pulsar: LLM.md — corona rename note + post-E2E-PQ state
Records the Pulsar/Corona rename context: this repo (R-LWE,
production fork) becomes luxfi/corona; luxfi/pulsar-m (M-LWE, FIPS
204) becomes the canonical luxfi/pulsar. No code change here — wire
formats and consensus paths are unchanged until the cross-repo moves
land. v0.1.x tags continue to point at R-LWE code; new consumers
should pin v0.2.0+ on the renamed repo.
2026-05-11 11:47:07 -07:00
zeekay e4d8614e8c ci: bump Go toolchain 1.26.2 → 1.26.3 (fix remaining govulncheck CVE)
First post-bump CI run still failed with one stdlib vuln:

  GO-2026-4971  net.Dial / net.LookupPort NUL-byte panic on Windows
                Fixed in: [email protected].3

The previous bump to 1.26.2 cleared 4 of 5 reported CVEs but missed
this one (referenced via networking.DialTCP / networking.ListenTCP).
Bumping to the latest 1.26.x patch closes it. Still patch-bump only;
no minor / major change.
2026-05-10 17:45:35 -07:00
zeekay ea0dec9e10 ci: bump Go toolchain 1.26.1 → 1.26.2 (fix govulncheck CVEs)
govulncheck reported the following stdlib CVEs against Go 1.26.1 in the
Security Scan job:

  GO-2026-4866  crypto/x509  excludedSubtrees case-sensitivity auth bypass
  GO-2026-4870  crypto/tls   TLS 1.3 KeyUpdate DoS
  GO-2026-4946  crypto/x509  inefficient policy validation
  GO-2026-4947  crypto/x509  unexpected chain-building work

All fixed in go1.26.2. Bumped:

  go.mod                                go 1.26.1 → 1.26.2
  .github/workflows/ci.yml              GO_VERSION 1.25.7 → 1.26.2
  .github/workflows/fresh-clone-ci.yml  GO_VERSION 1.25.7 → 1.26.2

Matrix test-matrix entry also bumped 1.25.7 → 1.26.2.
2026-05-10 17:41:12 -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 49630bb398 LP-107 Phase 4: pulsar consumes luxfi/math/codec
wire/wire.go — pulsar's wire-format hardening boundary. Untrusted
lattice Vector[Poly] frames now flow through luxfi/math/codec.Reader
so the bounded-decode contract is centralized: no recursion, no
hidden growth, no unbounded allocation.

Replaces the test-only validateVectorPolyFrameInline walker that
lived at threshold/fuzz_round_test.go with a production-grade
equivalent on the canonical luxfi/math/codec substrate.

Tests:
* TestValidateVectorPolyFrame_RejectsHugeLength — regression for
  lattice issue #4 (70-trillion-element attack input). Same input
  the original fuzz finding produced, now rejected with
  codec.ErrLimitExceeded via the shared math substrate.
* TestValidateVectorPolyFrame_HappyPath — 3-element accept.
* TestValidateVectorPolyFrame_AtCap — exactly MaxLatticeUintSliceLen
  accepted.
* TestValidateVectorPolyFrame_OverCap — MaxLatticeUintSliceLen+1
  rejected.

go.mod bumps luxfi/math v1.2.4 -> v1.3.0 (the LP-107 substrate
release with codec/ + params/ + backend/ + modarith/ + ntt/ + poly/
+ rns/ + sample/).

This is the first downstream consumer of the math substrate. Lens,
FHE, and lattice consumers follow.
2026-05-04 09:47:05 -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 4cff733804 threshold: validate Vector[Poly] frame before lattigo ReadFrom
FuzzPulsarSign1Round1Data found a 9-byte input that crashes the
goroutine with `fatal error: out of memory: cannot allocate
105589698985984-byte block` in `lattice/v7/utils/structs.Vector[T].
ReadFrom`. Recovery cannot catch the fatal OOM — the only safe path
is to reject malformed frames before `make([]T, size)` runs.

This is a SECOND DoS surface in lattice/v7, distinct from the
ReadUintNSlice recursion fixed by luxfi/lattice#3 (still open):
ReadFrom calls `make([]T, size)` with `size` read from the wire
before any bound check. PR #3 fixes the inner slice readers but the
outer Vector wrapper allocation remains exploitable.

Filed upstream as luxfi/lattice#4 with reproduction. The new
validateVectorPolyFrameInline walker mirrors the existing
warp/pulsar.validateVectorPolyFrame: walks the wire frame
end-to-end, asserts every length field is <= MaxLatticeUintSliceLen
(4096), and rejects truncated / over-large frames before the
lattigo decoder runs.

Result: FuzzPulsarSign1Round1Data passes 30s with the new walker
in place (28577 execs, 0 panics). Failing input
testdata/fuzz/FuzzPulsarSign1Round1Data/a80b8d313b40fa55 is now
rejected cleanly with "vector length 70368955777453 exceeds 4096".

Refs: luxfi/lattice#2 (open), luxfi/lattice#3 (open),
      luxfi/lattice#4 (filed 2026-05-04)
2026-05-04 03:15:49 -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