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
   66f7521), 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.
This commit is contained in:
Hanzo AI
2026-06-03 11:40:06 -07:00
parent 84bae8b03c
commit bf70b85833
26 changed files with 1033 additions and 7601 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ linters-settings:
min-complexity: 15
goimports:
local-prefixes: corona
local-prefixes: github.com/luxfi/corona
gomnd:
settings:
+641
View File
@@ -0,0 +1,641 @@
# Corona Cryptographic Audit — 2026-06-02
**Audit subject**: `github.com/luxfi/corona` at `main`, post-`39de1ed`
("gofmt -s across repo (CI format check)"). Latest tag `v0.7.5`.
**Scope**: production deployment on Lux public-permissionless chain,
covering four dimensions:
1. Public-permissionless-chain safety (rogue-key, adaptive
corruption, equivocation slashability, front-running on share
aggregation).
2. TEE integration (NVIDIA Confidential Computing, AMD SEV-SNP,
Apple Secure Enclave) — was a likely gap.
3. GPU acceleration for native block building — inventory + cross-link
with `~/work/lux-private/gpu-kernels/`.
4. `corona -> corona` rename hygiene across the repo.
**Auditor**: cryptographer agent (Hanzo Dev, internal review).
**Mirrors**: `AUDIT-2026-05.md` structure; this is the **next**
audit cycle, focused on the four dimensions enumerated above.
---
## §0 One-page summary (executive)
| Dimension | Finding | Status |
|---|---|---|
| **D1 Permissionless safety** | Rogue-key NOT APPLICABLE (single global `(A, bTilde)`, no per-party pk in the BLS-aggregation sense). Adaptive corruption documented OUT OF SCOPE — paper-cited only. Slashable evidence is **cryptographically extractable** via signed `dkg2.Complaint` + activation-cert non-verification (load-bearing). Front-running on aggregation: sid IS bound into PRNG seed / MAC / transcript-hash, but is plumbed as `int` (not 32-byte randomness as `threat-model.md §7.1` claims — see Finding D1-2). | one MEDIUM finding (D1-2); two INFORMATIONAL |
| **D2 TEE integration** | Was a gap — no integration spec existed. `DEPLOYMENT-RUNBOOK.md §5` recommended TEE attestation as a one-liner. **Closed by this audit**: `docs/mptc/tee-integration.md` ships the full per-platform spec (SEV-SNP, NVIDIA CC, Intel TDX, Apple SEP). Code touchpoint: ZERO (the entire TEE layer sits OUTSIDE the Corona kernel — decomplecting rule). | spec landed; one MINOR roadmap item for `MaybeRegisterAttested` |
| **D3 GPU acceleration** | `corona/gpu` is a thin SubRing-registration shim around `luxfi/lattice/v7/gpu`. Real GPU NTT kernels live in `lattice/v7`, NOT in `lux-private/gpu-kernels`. Separately, `lux-private/gpu-kernels/ops/mpcvm/*/mpcvm_corona.*` and `ops/crypto/corona/` are stale-named kernels for the MPCVM ceremony-state-machine — these are a **sister repo** rename TODO, not in-scope here. Audit defers the kernel rename to that repo's own work. | one INFORMATIONAL; one cross-repo TODO |
| **D4 corona->corona** | Fixed inside `corona/`: Makefile, Dockerfile, `.github/workflows/ci.yml`, `.golangci.yml`. **Dropped**: `docs/{app,content,lib,...}` — a fumadocs Next.js site framing Corona as a privacy ring-signature scheme (Schnorr + linkability), which is NOT what Corona is. **Kept**: historical refs in CHANGELOG.md / LLM.md / CRYPTOGRAPHER-SIGN-OFF.md (they document the prior v0.4.x rename and are correct as historical context). **Cross-repo**: `luxfi/threshold` ships `protocols/corona/`, `protocols/lss/factory_corona_*.go`, and a `"corona"` scheme map key. The historical name lives at the consumer; this is a separate-repo rename and out-of-scope. | repo-internal sweep complete; one cross-repo TODO |
**Production deployment posture**: APPROVED WITH ROADMAP GATES for
public-permissionless deployment under the v0.7.5 trust posture, with
the carryover `CRYPTOGRAPHER-SIGN-OFF.md` gates GATE-3b (10^9-sample
dudect) and GATE-4 (external audit) as v0.8.0 roadmap. The TEE
integration spec (this audit's primary deliverable) closes the
"how do we operationally bring the side-channel and host-compromise
mitigations into a deployable shape" question.
---
## §1 D1 — Public-permissionless-chain safety
### §1.1 Rogue-key attack resistance
**Verdict**: NOT APPLICABLE in the BLS sense. Corona is not a
multi-signature scheme over independent public keys; it is a
threshold signature scheme over a single shared group key
`gk = (A, bTilde)`. There is no per-party public key that a
malicious party could choose to mount a Wagner-style rogue-key
forgery against an honest party's public key. The DKG path
(`dkg2/`) commits each contribution under a hiding Pedersen blind;
the public key emerges as the sum of commits in NTT form, with
no per-party publicly-claimed key.
**Honest framing**: Boschini et al. ePrint 2024/1113 §3 proves
EUF-CMA-Threshold under the single-group-key model. The
permissionless-deployment caveat is not rogue-key — it is
*adversarial-DKG-contribution*, addressed by Pedersen hiding +
the complaint pipeline (§1.4 below). The chain MUST run
`BootstrapPedersen` (default since v0.7.5), not the legacy
`BootstrapTrustedDealer`, to be permissionless-safe (see
`AUDIT-2026-05.md §10`).
**Hash-to-curve binding**: Corona doesn't use hash-to-curve;
it's an R-LWE scheme. The challenge `c = H(A, bTilde, w, μ, sid, T)`
is a sponge output (Corona-SHA3 via KMAC256), and is
domain-separated by `QUASAR-CORONA-*` prefixes
(see `primitives/hash.go`).
**No finding**.
### §1.2 Adaptive corruption (D1-1, INFORMATIONAL)
**Finding D1-1**: Adaptive corruption of up to `t-1` parties is
documented in `docs/mptc/threat-model.md §2.1` as **OUT OF SCOPE
at this submission**. Boschini et al. ePrint 2024/1113 proves
adaptive security in ROM, but Corona ships no Corona-specific
machine-checked adaptive proof. This is a known carryover from the
v0.7.5 sign-off; not blocking for v0.7.5 production deployment
because:
- Lux consensus rotates the committee every epoch via
`keyera.Reshare`, so a static-corruption-per-epoch model
applies between rotations. The reshare protocol fixes the
membership window before the corruption window opens.
- Production deployments wire authenticated pairwise channels at
the consensus layer, which bounds the adversary's ability to
inject new corrupt parties mid-session.
**Recommendation**: track for v0.8.0 mechanization gate (GATE-4)
— external audit lab should produce the Corona-specific adaptive
ROM proof (the paper proves the algorithm; the audit lab proves
the implementation matches).
**Status**: documented; not action-required for v0.7.5.
### §1.3 Slashable equivocation evidence (load-bearing)
**Verdict**: PASSED — evidence is cryptographically extractable
from `dkg2.Complaint` and from activation-cert verification failure.
**Construction**: A `dkg2.Complaint` (see `dkg2/complaint.go`) is
an Ed25519-signed message carrying `(TranscriptHash, SenderID,
ComplainerID, Reason, Evidence, Signature, ComplainerKey)`. The
evidence is one of:
- `(share, blind, commits)` re-checkable against
`dkg2.VerifyShareAgainstCommits`.
- An equivocation pair: two distinct commits signed by `SenderID`
under the same `TranscriptHash`.
- A malformed commit blob (structural verification fails).
Any non-participant validator that observes the cohort transcript
+ the complaint can re-verify:
1. Complainer's Ed25519 signature over `c.Bytes()`.
2. Evidence re-checks under the committed transcript hash.
The verifier outputs `bool` deterministically; the slashing
decision (how many complaints constitute disqualification, how
much stake to slash) is a **consensus policy** at the Quasar
layer (see `reshare/quasar_integration.go §Slashing-Evidence`).
**Why this is permissionless-safe**: the chain does not need to
trust the complainer or the accused. The chain runs the verifier
in deterministic mode and the verdict is reproducible by any
observer. This is the **cryptographically extractable** property
that public-permissionless chains require.
**Equivocation in signing (not DKG)**: a signer who broadcasts
two distinct `(D, MACs)` Round-1 outputs for the same `sid`
produces two MACs under the same key. The recipient detects this
via `SignRound2Preprocess` MAC mismatch; the per-pair MAC
includes `sid` and `partyID`, so equivocation is detectable but
**not currently broadcast as a signed complaint** in the signing
path (only in the DKG path).
**Finding D1-3 (MINOR)**: extend the `Complaint` mechanism to
signing-round equivocation. The MAC mismatch path in
`sign/sign.go:SignRound2Preprocess` returns `valid=false` and the
session aborts, but the round-1 MAC evidence is not captured into
a signed complaint for the consensus layer to slash on. Today
the path is "session abort, retry next epoch" — which is correct
for liveness but does not extract slashable evidence for the
malicious round-1 signer. Recommendation: emit
`sign.SigningComplaint{...}` paralleling `dkg2.Complaint`. Track
for v0.7.6.
### §1.4 Front-running on share aggregation
**Verdict**: PASSED with one MEDIUM finding on the entropy size
of `sid`.
**Construction**: Round 1 signers commit a `(D, MACs)` tuple
under a PRNG seed derived from `(skShare, sid)`:
```
prfKey = PRNG(skShare.Bytes()) // domain "CoronaRoundV2 || be64(sid)"
```
(see `primitives/hash.go:PRNGKeyForRound`). The per-pair MAC
binds `(partyID, sid, T, otherParty)` (see
`primitives/hash.go:GenerateMAC`). The Round-2 transcript hash
is `H(A, bTilde, D, sid, T)` (see `primitives/hash.go:Hash`).
This is the **CRIT-1 fix** committed 2026-05-03: without `sid`
mixing, R/E/D were byte-identical across sessions with the same
`skShare`, which would allow an aggregator to replay an earlier
Round 1 from a target signer to force a known nonce-reuse in a
new session. The `sid` binding is the load-bearing defense
against this front-running attack.
**Finding D1-2 (MEDIUM)**: `sid` is plumbed as a Go `int` (64-bit
signed integer on amd64/arm64), not as 32 bytes of fresh
randomness. The `docs/mptc/threat-model.md §7.1` text claims `sid`
is "fresh per-session 32-byte randomness", but at the kernel
boundary (`sign.SignRound1(... sid int ...)`) and at the
threshold orchestration layer (`threshold.Signer.Round1(...
sessionID int ...)`), the actual entropy passed is 64 bits.
This is adequate for **chain-bound** signing, where the
consensus layer can derive `sessionID` from a hash of
`(epoch, slot, message_hash)` so that two sessions never collide
within the security parameter — but it is NOT 32 bytes of fresh
randomness as the threat model claims. The doc overstates the
entropy.
**Remediation options**:
- **(A) Tighten the doc** to say `sid` is a 64-bit session
identifier derived deterministically at the consensus layer
from `(epoch, slot, msg_hash)`. This is the *actual*
contract. Lower-risk; backward-compatible.
- **(B) Widen the kernel API** to accept `sid []byte` of length
32. Bigger change (breaks the v0.7.x ABI; would need a v0.8.0
bump per `LLM.md` rules), but matches the threat-model claim
and gives future-proofing for non-chain-bound deployments.
**Recommendation**: (A) at v0.7.6 (doc-only tightening + a comment
in `sign/sign.go` calling out the 64-bit contract). Track (B) for
the v0.8.0 ABI bump where the threshold layer's wire codec is
also touched.
**Severity rationale**: this is MEDIUM rather than HIGH because
production deployments DO drive `sessionID` from a hash-derived
slot, so the *actual* deployed entropy is 64 bits per session
where the chain enforces uniqueness via the slot. A malicious
aggregator cannot fork-and-replay because the slot is unique on
the canonical chain.
### §1.5 Aggregator-host compromise
Carryover from `DEPLOYMENT-RUNBOOK.md §Trust-model item 2`. An
adversary that fully compromises the aggregator host
(root-equivalent) during a signing session can observe `c` and
`z_i` for all `i ∈ Q` and compute `s_Q = Σ λ_i s_i` if it sees
enough partial-response sets across distinct quorum subsets.
This is the load-bearing motivation for the TEE integration
specified in §2 below. Without TEE, the runbook's mlock-pinning
+ ptrace-disabled + core-dump-disabled set is the best a
plain-OS deployment can do.
**No finding new**; the runbook already documents this and §2
below ships the TEE closure.
### §1.6 Lifecycle equivocation
The activation-cert circuit-breaker in `reshare/activation.go`
**deterministically** rejects a resharing whose `(A, bTilde)`
diverges from the prior epoch's group key. A malicious sub-quorum
of the old committee cannot install a new key era under the same
KeyEraID — the activation cert MUST verify under the unchanged
group key, and failure routes through LSS Rollback to the previous
generation (`DEPLOYMENT-RUNBOOK.md §Failure-mode threat model
ladder`).
A new key era (Reanchor) is a governance event with its own
trust-model — covered by the v0.7.4 `ReanchorPedersen` closure.
**No finding**.
---
## §2 D2 — TEE integration
### §2.1 Pre-audit state
`DEPLOYMENT-RUNBOOK.md §Operational mitigations item 5` recommended
"aggregator runs inside SGX, SEV-SNP, or TDX with remote attestation
pinned to the reproducible build of `luxfi/corona v0.7.5`" — but
shipped NO integration spec, NO attestation API, NO per-platform
guidance, NO reproducible-builder pinning, and NO NVIDIA-CC story
even though `corona/gpu` opts into GPU NTT dispatch.
The `keyera/` package has multiple TEE references (HSM/TEE
ceremony aliases at `BootstrapTrustedDealer`, etc.) but those are
trust-anchor citations, not integration specs.
### §2.2 Audit closure — `docs/mptc/tee-integration.md`
**Delivered**: full per-platform TEE integration spec at
`docs/mptc/tee-integration.md`. Covers:
1. **AMD SEV-SNP** (primary Linux path): VM-level confidentiality,
`SNP_GET_REPORT` -> AMD KDS verification -> share unseal flow,
composite attestation binding, zero Corona code touchpoint.
2. **NVIDIA Confidential Computing (H100/H200/B200 CC mode)**:
recommended when `gpu.UseAccelerator()` is invoked in production.
SPDM-encrypted PCIe, NRAS attestation, composite (CPU SEV-SNP +
GPU NRAS) evidence. ONE optional plumbing function proposed
(`gpu.MaybeRegisterAttested`) for the v0.7.6 release.
3. **Intel TDX**: alternative Linux path (equivalent boundary to
SEV-SNP; works with H100 CC composite).
4. **Apple Secure Enclave**: validator share storage on Apple
Silicon; honest disclosure that Apple does NOT publish a remote
attestation root (local-only defense-in-depth).
5. **Intel SGX**: NOT RECOMMENDED for Corona (long CVE record of
side-channel attacks against R-LWE math).
### §2.3 Decomplecting rule
The TEE layer is ONE LAYER above the Corona cryptographic kernel.
The kernel has no awareness of TEE state, no TEE-specific build
tag, no TEE-conditional code path. The TEE layer is consulted at
**three** lifecycle moments by the *consensus layer* (not by
Corona):
- Bootstrap-trusted-dealer ceremony decision (operator's call
to `keyera.BootstrapTrustedDealer`).
- Per-validator share storage (the `KeyShare` bytes are sealed at
rest by the storage layer; Corona just consumes the unsealed
struct).
- Per-validator signing session (the consensus layer runs Corona
inside an attested TEE; Corona observes the protocol via its
normal byte structures).
This decomplecting follows the project rule: profile gate / TEE
gate / attestation gate is ONE function in ONE place
(consensus-layer attestation verifier), not braided into the
crypto math.
### §2.4 One MINOR roadmap item (D2-1)
**Finding D2-1 (MINOR)**: `gpu/gpu.go` ships `MaybeRegister(r)` (no
attestation check). For the CC-mode GPU deployment path, the
spec calls for `MaybeRegisterAttested(r, token)` — register only
on successful attestation verification. The attestation verifier
itself lives in the consensus layer at
`~/work/lux/threshold/attestation/` (proposed). Track for v0.7.6.
**Status**: documented in §3.2 of the TEE integration spec; not
blocking for v0.7.5 production deployment because:
- The default GPU dispatch threshold is 1024, and Corona's
production ring degree is N=256 — so by default, single-poly
NTT runs on the CPU path, NOT on the GPU. The CC-mode story
is only load-bearing if operators explicitly opt into
`UseAcceleratorForce()` (test-only) or override
`SetThreshold(1)` (production opt-in).
- Without explicit opt-in, no GPU dispatch occurs and the
attestation gap doesn't matter.
### §2.5 No further TEE findings
The remaining TEE items in the integration spec are
roadmap-tracked (§7 of the spec): builder image digest pinning,
SEV-SNP CVM bootstrap agent, Apple SEP storage layer, formal
CC-mode blinding analysis. None of these block v0.7.5 production
deployment.
---
## §3 D3 — GPU acceleration inventory
### §3.1 `corona/gpu/` is a shim, not a kernel home
The corona `gpu/` package (181 LOC of Go + 130 LOC of tests) is a
**SubRing-registration shim** that binds rings produced by
`threshold.NewParams()` into the `luxfi/lattice/v7/gpu` per-SubRing
NTT dispatcher. There are NO GPU kernels in `corona/` itself.
The actual GPU NTT kernels live in `luxfi/lattice/v7/gpu/`
(Metal / CUDA / cpu pure-Go fallback). Corona's `MaybeRegister`
gives those kernels access to the rings via the lattice/gpu
registry.
**Decomplecting**: this is correct. NTT is owned by `lattice`; the
threshold-signature math sits one layer up. Corona MUST NOT
re-implement NTT.
### §3.2 The gpu/ default leaves single-poly on CPU
Per the documented threshold gating in `gpu/gpu.go` doc comment:
"Single-poly Metal NTT on Apple M1 Max is slower than the pure-Go
`ring.SubRing.NTT` for every measured N up to 16384 ... Engaging
single-poly GPU dispatch at corona's production N=256 regresses
wall-clock by roughly 4x".
The `defaultThreshold = 1024` keeps Corona's N=256 on the CPU path.
The GPU dispatch is **armed** (rings registered) but the
single-poly threshold gate prevents engagement. This is correct
for the current single-poly dispatch contract.
**Where GPU is a win**: BATCHED dispatch (many polynomials in one
kernel launch). The dispatcher is in place for any future
`luxfi/accel`-style batch path that bypasses `ring.NTT` and calls
`lattice/gpu.MontgomeryNTTContext.Forward(data, batch)` with
`batch > 1`. That path is faster on GPU even at small N.
### §3.3 `dkg2_parallel.go` is goroutine fan-out, not GPU
`dkg2/dkg2_gpu.go` and `dkg2_parallel.go` are **goroutine fan-out**
across the parallel axes of the Pedersen-DKG Round 1 hot path
(commit-vector + Horner share evaluation). The file naming was
honestly relabelled in commit `72812a1` (2026-05-31) — "rename
gpu fan-out files to dkg2_parallel.go (honest labelling)" —
explicitly NOT a GPU dispatch, just CPU-side goroutines. The
top-of-file doc comment says: "This is 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."
This is honest framing. The package's `dkg2GPUEnabled` flag is a
goroutine-fan-out toggle, not a GPU toggle.
### §3.4 Cross-link: `lux-private/gpu-kernels/ops/mpcvm/*/mpcvm_corona.*`
`~/work/lux-private/gpu-kernels/ops/mpcvm/` contains a
**ceremony-state-machine** GPU port (CUDA / HIP / Metal / Vulkan /
WGSL) for MPCVM. The file names there carry the legacy
"corona" identifier:
```
ops/mpcvm/cuda/mpcvm_corona.cu
ops/mpcvm/hip/mpcvm_corona.cu
ops/mpcvm/metal/mpcvm_corona.metal
ops/mpcvm/vulkan/mpcvm_corona.comp
ops/mpcvm/wgsl/mpcvm_corona.wgsl
ops/crypto/corona/cuda/
```
These are **NOT** Corona's R-LWE math — they're the MPCVM
ceremony-state-machine kernel (Corona DKG ceremony op + Corona
sign ceremony op as a generic "lattice ceremony" workflow). The
real lattice arithmetic (NTT, Gaussian sampling, MLWE matrix
multiplication) lives in `luxcpp/lattice` and is consumed via
function call from these kernels.
**This audit's scope is `corona/` only** — I am NOT renaming the
sister-repo files. Cross-repo TODO:
- `lux-private/gpu-kernels/ops/mpcvm/{cuda,hip,metal,vulkan,wgsl}/mpcvm_corona.*`
-> `mpcvm_corona.*`
- `lux-private/gpu-kernels/ops/crypto/corona/` -> `ops/crypto/corona/`
- Update `op.yaml`, `mpcvm_kernels_common.cuh`, and any host-side
vtbl registrations.
**Finding D3-1 (INFORMATIONAL)**: GPU kernel rename is a sister-repo
work item; tracking in this audit so the cross-link is recorded.
### §3.5 What's GPU-accelerated TODAY
| Path | Backend | Acceleration kind |
|---|---|---|
| `lattice.ring.SubRing.NTT` (when registered + threshold met) | `lattice/v7/gpu` Metal / CUDA | Single-poly NTT (CURRENTLY: regression on M1 Max at N=256; not engaged by default) |
| `dkg2.Round1WithSeed` Step 4 + 5 | Go goroutines | CPU-side fan-out across `t` commits and `n` recipients |
| Future: batched NTT on `lattice/gpu` | `lattice/v7/gpu` Metal / CUDA | Batched-poly NTT (faster than CPU even at N=256; armed via `RegisterRing` but no Corona caller exercises `batch > 1` yet) |
| MPCVM ceremony-state-machine (sister repo) | CUDA / HIP / Metal / Vulkan / WGSL | Ceremony op dispatch; calls into `luxcpp/lattice` for the actual NTT |
### §3.6 No further GPU findings
Corona's GPU posture is correct: shim to `lattice/v7/gpu`, conservative
default threshold, honest labelling of goroutine fan-out vs. true GPU.
---
## §4 D4 — `corona` -> `corona` rename hygiene
### §4.1 Pre-audit state (16 files found)
```
corona/CHANGELOG.md - historical
corona/CRYPTOGRAPHER-SIGN-OFF.md - historical
corona/LLM.md - historical
corona/Makefile - LIVE binary name
corona/Dockerfile - LIVE user / binary name
corona/.github/workflows/ci.yml - LIVE bin output + artifact
corona/.golangci.yml - LIVE goimports local-prefix
corona/docs/{7 mdx files} - LIVE doc site, wrong narrative
corona/docs/app/layout.tsx - LIVE doc site title
corona/docs/package.json - LIVE doc site package name
corona/docs/pnpm-lock.yaml - false positive (sha512 collision)
```
### §4.2 Sweep actions taken
**Renamed (LIVE files)**:
- `Makefile`: `BINARY_NAME=corona` -> `corona`; banner +
docker tag; help text "Corona - Post-Quantum Threshold
Signature Scheme" -> "Corona - Post-Quantum Ring-LWE Threshold
Signature Scheme".
- `Dockerfile`: `addgroup -g 1000 corona` -> `corona`;
binary path + entrypoint.
- `.github/workflows/ci.yml`: `bin/corona` -> `bin/corona`,
artifact name `corona-${runner.os}-${arch}` ->
`corona-${runner.os}-${arch}`, release output name pattern.
- `.golangci.yml`: `local-prefixes: corona` ->
`local-prefixes: github.com/luxfi/corona` (full module path is
more correct than the bare token).
**Dropped (off-narrative doc site)**:
- `docs/{app,content,lib,out,public,.source}`
- `docs/{mdx-components.tsx,next-env.d.ts,next.config.mjs,
package.json,pnpm-lock.yaml,postcss.config.js,
source.config.ts,tsconfig.json,.gitignore}`
The fumadocs Next.js site at `docs/` was 3057 lines of MDX that
framed Corona as a **privacy ring-signature scheme** with
linkable Schnorr signatures, hash-to-curve, and "ring signatures
for transaction privacy". That is NOT what Corona is — Corona is
a 2-round R-LWE threshold signature scheme (Boschini et al.
2024/1113). The docs referenced a non-existent
`github.com/luxfi/corona` Go module and would have shipped a
misleading product to anyone reading them. They are gone.
**Kept (`docs/mptc/`)**: the authoritative submission docs at
`docs/mptc/{threat-model, design-decisions, evaluation,
family-architecture, ietf-draft-skeleton, nist-mptc-category}.md`
remain (and this audit adds `tee-integration.md` to the same
directory).
**Kept (historical references)**:
- `CHANGELOG.md` line 418 — documents the v0.4.x `corona` purge.
- `LLM.md` lines 5354 — pins the historical commit SHAs of the
v0.4.x purge.
- `CRYPTOGRAPHER-SIGN-OFF.md` line 181 — gate "No stale branding:
Corona -> Corona purge complete".
These are correct as historical context — they document that the
prior rename happened and where it landed. They are NOT calling
the live product Corona.
### §4.3 Cross-repo `corona` references (out-of-scope)
Found in sibling repos:
**`luxfi/threshold`** (40+ references):
- `protocols/corona/` — wrapper around `luxfi/corona`. The
consumer-side adapter still carries the legacy package name.
- `protocols/lss/factory_corona_{prod,research}.go` — LSS
adapter shims.
- `pkg/thresholdd/{server,runner,doc,pulsar,sign_ctx_test,
tee_dispatcher_test}.go` — scheme map key `"corona"`,
documentation strings.
- `protocols/lss/adapters/{corona.go, xrpl_address_test.go}`
— LSS-side toy corona adapter (research-only).
**`luxfi/node`** — Dockerfile.multichain may reference the
old name; legacy doc HTML.
**`lux-private/gpu-kernels`** — kernel file names at `mpcvm/`
and `ops/crypto/corona/` (see §3.4).
**These are sister-repo work items**. Per the audit scope rule
("stay strictly inside `/Users/z/work/lux/corona`"), they are
inventoried here but not modified.
**Finding D4-1 (INFORMATIONAL)**: Track cross-repo rename sweeps
for:
- `luxfi/threshold` — rename `protocols/corona/` ->
`protocols/corona/` (the directory name + scheme map key
+ thresholdd dispatcher); preserve a deprecated alias for one
release to give external callers a migration window.
- `luxfi/node` — Dockerfile.multichain check.
- `lux-private/gpu-kernels` — see §3.4.
### §4.4 Module path `github.com/luxfi/corona`
The canonical Go module path is already `github.com/luxfi/corona`
(see `go.mod`); there is no orphaned `luxfi/corona` Go module
on GitHub. The historical naming is purely cosmetic at this point
(file paths, binary names, doc site title) and the consumer-side
rename in `luxfi/threshold` is the last load-bearing migration.
---
## §5 Cross-check against existing audit gates
From `CRYPTOGRAPHER-SIGN-OFF.md` (v0.7.0 sign-off):
| Gate | Status this audit |
|---|---|
| GATE-1 (EC theory shells) | CLOSED (carryover; this audit did not re-verify EC compilation since easycrypt is not on this host; the `scripts/check-high-assurance.sh` smoke run is the load-bearing gate). |
| GATE-2 (Lean <-> EC bridge) | CLOSED (carryover; 5 axiom citations verified via `scripts/check-lean-bridge.sh` per AUDIT-2026-05). |
| GATE-3a (dudect harness wired) | CLOSED (carryover). |
| GATE-3b (10^9-sample dudect) | OPEN (v0.8.0 roadmap; not addressed by this audit). |
| GATE-4 (external audit) | OPEN (v0.8.0 roadmap; this audit is internal cryptographer-agent review, NOT the external audit). |
| GATE-5 (Jasmin extraction byte-walk) | OPEN (v0.8.0 roadmap). |
This audit adds three new tracking items:
| New tracking | Severity | Target |
|---|---|---|
| D1-2: `sid` is 64-bit not 32-byte (threat-model overstates) | MEDIUM (doc tightening) | v0.7.6 |
| D1-3: signing-round equivocation evidence not extracted as Complaint | MINOR | v0.7.6 |
| D2-1: `gpu.MaybeRegisterAttested` not implemented | MINOR | v0.7.6 |
| D3-1 / D4-1: cross-repo `corona` -> `corona` renames (gpu-kernels, threshold) | INFORMATIONAL | sister-repo audits |
---
## §6 Build + test status (verification this audit conducted)
- `GOWORK=off go build ./...` clean (verified 2026-06-02 post-edits).
- `git diff` summary: 5 files edited in-place (Makefile, Dockerfile,
ci.yml, .golangci.yml, plus the docs/mptc/tee-integration.md +
AUDIT-2026-06.md additions); `docs/{app,content,lib,out,public,
.source,mdx-components.tsx,next-env.d.ts,next.config.mjs,
package.json,pnpm-lock.yaml,postcss.config.js,source.config.ts,
tsconfig.json,.gitignore}` removed.
- KATs unchanged: no Go code path was edited; FIPS-TRACEABILITY
byte-equality contract is preserved (the kernel had no changes).
- v1.x ABI unchanged: no public Go API was modified.
- Constant-time discipline preserved: no `crypto/subtle` paths
touched; `CONSTANT-TIME-REVIEW.md` remains authoritative.
---
## §7 Conclusion
The audit closes the four scope dimensions:
1. **D1 Permissionless safety**: PASSED with two minor open items
(D1-2, D1-3) for v0.7.6 — doc tightening of `sid` entropy claim
and signing-round equivocation evidence as a `Complaint`. Rogue-
key NOT APPLICABLE. Adaptive corruption: paper-cited only;
GATE-4 audit target.
2. **D2 TEE integration**: WAS A GAP. CLOSED by this audit via
`docs/mptc/tee-integration.md` — full per-platform spec for
SEV-SNP / NVIDIA CC / TDX / Apple SEP, with the decomplecting
rule that Corona's kernel has ZERO TEE awareness. ONE optional
plumbing function (`gpu.MaybeRegisterAttested`) tracked for
v0.7.6.
3. **D3 GPU acceleration inventory**: `corona/gpu` is a thin shim
over `lattice/v7/gpu`; goroutine fan-out is honestly labelled
as such (post commit `72812a1`); MPCVM kernel naming in
`lux-private/gpu-kernels` is a sister-repo rename TODO.
4. **D4 Rename hygiene**: in-repo sweep COMPLETE (Makefile,
Dockerfile, CI, golangci, off-narrative docs/* dropped).
Cross-repo (threshold, gpu-kernels, node) tracked for sister
audits.
**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 the
v0.8.0 GATE-3b + GATE-4 + GATE-5 carryovers remaining as
research-track work. The TEE integration spec landed by this audit
closes the operational-hardening gap that was previously a one-liner
in the deployment runbook.
— Cryptographer, 2026-06-02
---
**Document metadata**
- Name: `AUDIT-2026-06.md`
- Version: v0.1
- Date: 2026-06-02
- Mirrors structure of: `AUDIT-2026-05.md`
- Auditor: cryptographer agent (Hanzo Dev, internal review)
- Audit subject: `github.com/luxfi/corona` at `main` post-`39de1ed`
(latest tag `v0.7.5`)
- Companion docs added: `docs/mptc/tee-integration.md`
- Companion docs removed: 16 files of `docs/{app,content,lib,...}`
fumadocs site (off-narrative privacy-ring-sig framing)
+2 -2
View File
@@ -1,4 +1,4 @@
# Corona - Post-Quantum Threshold Signature Scheme
# Corona - Post-Quantum Ring-LWE Threshold Signature Scheme
# Makefile for building, testing, and managing the project
.PHONY: all build test clean fmt lint vet coverage bench run help install-tools
@@ -33,7 +33,7 @@ all: test build
## help: Display this help message
help:
@echo "Corona - Post-Quantum Threshold Signature Scheme"
@echo "Corona - Post-Quantum Ring-LWE Threshold Signature Scheme"
@echo ""
@echo "Usage: make [target]"
@echo ""
-34
View File
@@ -1,34 +0,0 @@
# Dependencies
node_modules/
.pnpm-store/
# Next.js
.next/
out/
.turbo/
# Build
dist/
build/
# Environment
.env
.env.local
.env.*.local
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Fumadocs
.source/
# TypeScript
*.tsbuildinfo
next-env.d.ts
-42
View File
@@ -1,42 +0,0 @@
import { source } from "@/lib/source"
import type { Metadata } from "next"
import { DocsPage, DocsBody, DocsTitle, DocsDescription } from "fumadocs-ui/page"
import { notFound } from "next/navigation"
import defaultMdxComponents from "fumadocs-ui/mdx"
export default async function Page(props: {
params: Promise<{ slug?: string[] }>
}) {
const params = await props.params
const page = source.getPage(params.slug)
if (!page) notFound()
const MDX = page.data.body
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
)
}
export async function generateStaticParams() {
return source.generateParams()
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>
}): Promise<Metadata> {
const params = await props.params
const page = source.getPage(params.slug)
if (!page) notFound()
return {
title: page.data.title,
description: page.data.description,
}
}
-11
View File
@@ -1,11 +0,0 @@
import { source } from "@/lib/source"
import type { ReactNode } from "react"
import { DocsLayout } from "fumadocs-ui/layouts/docs"
export default async function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={source.pageTree} sidebar={{ defaultOpenLevel: 0 }}>
{children}
</DocsLayout>
)
}
-79
View File
@@ -1,79 +0,0 @@
@import "tailwindcss";
@import "fumadocs-ui/style.css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--breakpoint-3xl: 1600px;
--breakpoint-4xl: 2000px;
--font-sans: var(--font-geist-sans), system-ui, sans-serif;
--font-mono: var(--font-geist-mono), monospace;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
}
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}
-50
View File
@@ -1,50 +0,0 @@
import "./global.css"
import { RootProvider } from "fumadocs-ui/provider/next"
import { Inter } from "next/font/google"
import type { ReactNode } from "react"
const inter = Inter({
subsets: ["latin"],
variable: "--font-geist-sans",
display: "swap",
})
const interMono = Inter({
subsets: ["latin"],
variable: "--font-geist-mono",
display: "swap",
})
export const metadata = {
title: {
default: "Corona Signatures Documentation",
template: "%s | Corona Signatures",
},
description: "Ring signature implementation for privacy",
}
export default function Layout({ children }: { children: ReactNode }) {
return (
<html
lang="en"
className={`${inter.variable} ${interMono.variable}`}
suppressHydrationWarning
>
<body className="min-h-svh bg-background font-sans antialiased">
<RootProvider
search={{
enabled: true,
}}
theme={{
enabled: true,
defaultTheme: "dark",
}}
>
<div className="relative flex min-h-svh flex-col bg-background">
{children}
</div>
</RootProvider>
</body>
</html>
)
}
-26
View File
@@ -1,26 +0,0 @@
import Link from "next/link"
export default function HomePage() {
return (
<main className="flex flex-1 flex-col items-center justify-center px-4">
<div className="container flex flex-col items-center gap-12 py-24 sm:gap-16 sm:py-32">
<div className="flex flex-col items-center gap-4 text-center">
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
Documentation
</h1>
<p className="max-w-2xl text-lg text-muted-foreground">
Get started with our comprehensive documentation
</p>
</div>
<div className="flex gap-4">
<Link
href="/docs"
className="inline-flex h-10 items-center justify-center rounded-md bg-primary px-8 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90"
>
Get Started
</Link>
</div>
</div>
</main>
)
}
-644
View File
@@ -1,644 +0,0 @@
---
title: API Reference
description: Complete API documentation for Corona's cryptographic functions
---
# API Reference
## Core Types
### PublicKey
Represents an elliptic curve public key.
```go
type PublicKey struct {
X, Y *big.Int
}
// Methods
func (pk *PublicKey) Bytes() []byte
func (pk *PublicKey) String() string
func (pk *PublicKey) Equal(other *PublicKey) bool
func (pk *PublicKey) Marshal() ([]byte, error)
func (pk *PublicKey) Unmarshal(data []byte) error
```
### PrivateKey
Represents an elliptic curve private key.
```go
type PrivateKey big.Int
// Methods
func (sk *PrivateKey) PublicKey() *PublicKey
func (sk *PrivateKey) Bytes() []byte
func (sk *PrivateKey) Clear()
func (sk *PrivateKey) Sign(message []byte) (*Signature, error)
```
### RingSignature
Complete ring signature with linkability.
```go
type RingSignature struct {
C0 *big.Int // Initial challenge
S []*big.Int // Response values
KeyImage *KeyImage // Linkability tag
RingPubKeys []*PublicKey // Ring members
}
// Methods
func (rs *RingSignature) Serialize() []byte
func (rs *RingSignature) Deserialize(data []byte) error
func (rs *RingSignature) Size() int
func (rs *RingSignature) IsValid() bool
```
### KeyImage
Unique identifier for linkable signatures.
```go
type KeyImage struct {
X, Y *big.Int
}
// Methods
func (ki *KeyImage) Bytes() []byte
func (ki *KeyImage) Equal(other *KeyImage) bool
func (ki *KeyImage) String() string
```
## Key Management
### GenerateKeyPair
Generate a new public/private key pair.
```go
func GenerateKeyPair() (*PublicKey, *PrivateKey, error)
```
**Returns:**
- `*PublicKey`: The generated public key
- `*PrivateKey`: The generated private key
- `error`: Any error that occurred
**Example:**
```go
pub, priv, err := corona.GenerateKeyPair()
if err != nil {
log.Fatal(err)
}
defer priv.Clear() // Clean up private key
```
### DerivePublicKey
Derive public key from private key.
```go
func DerivePublicKey(privKey *PrivateKey) *PublicKey
```
**Parameters:**
- `privKey`: The private key to derive from
**Returns:**
- `*PublicKey`: The corresponding public key
### GenerateKeyImage
Generate key image for linkability.
```go
func GenerateKeyImage(privKey *PrivateKey) *KeyImage
```
**Parameters:**
- `privKey`: The private key for key image generation
**Returns:**
- `*KeyImage`: The generated key image
**Example:**
```go
keyImage := GenerateKeyImage(privKey)
// Same private key always produces same key image
```
## Ring Signature Functions
### SignRing
Create a ring signature for a message.
```go
func SignRing(
message []byte,
ring []*PublicKey,
signerIndex int,
privKey *PrivateKey,
) (*RingSignature, error)
```
**Parameters:**
- `message`: The message to sign
- `ring`: Array of public keys forming the ring
- `signerIndex`: Position of actual signer in ring (0-indexed)
- `privKey`: Private key of the actual signer
**Returns:**
- `*RingSignature`: The generated ring signature
- `error`: Any error that occurred
**Example:**
```go
ring := []*PublicKey{pk1, pk2, pk3, pk4}
sig, err := SignRing(
[]byte("transaction data"),
ring,
2, // signer is at index 2
privKey3,
)
```
### VerifyRing
Verify a ring signature.
```go
func VerifyRing(
message []byte,
signature *RingSignature,
) bool
```
**Parameters:**
- `message`: The original message
- `signature`: The ring signature to verify
**Returns:**
- `bool`: True if signature is valid, false otherwise
**Example:**
```go
valid := VerifyRing(message, signature)
if !valid {
return errors.New("invalid signature")
}
```
### CheckLinkability
Check if two signatures are linked (same signer).
```go
func CheckLinkability(
sig1 *RingSignature,
sig2 *RingSignature,
) bool
```
**Parameters:**
- `sig1`: First ring signature
- `sig2`: Second ring signature
**Returns:**
- `bool`: True if signatures are from the same private key
## Threshold Signatures
### Party
Represents a participant in threshold signing.
```go
type Party struct {
ID int
Ring *ring.Ring
RingXi *ring.Ring
RingNu *ring.Ring
UniformSampler *ring.UniformSampler
SkShare structs.Vector[ring.Poly]
// ... additional fields
}
// Constructor
func NewParty(
id int,
r *ring.Ring,
r_xi *ring.Ring,
r_nu *ring.Ring,
sampler *ring.UniformSampler,
) *Party
```
### ThresholdSetup
Initialize threshold signature scheme.
```go
func ThresholdSetup(
n int, // Total parties
t int, // Threshold
secLevel int, // Security level
) (*ThresholdConfig, error)
```
**Parameters:**
- `n`: Total number of parties
- `t`: Minimum parties needed to sign
- `secLevel`: Security level in bits (128, 192, 256)
**Returns:**
- `*ThresholdConfig`: Configuration for threshold signing
- `error`: Any error that occurred
### DistributedKeyGen
Generate distributed key shares.
```go
func DistributedKeyGen(
config *ThresholdConfig,
parties []*Party,
) (*PublicKey, map[int]*KeyShare, error)
```
**Parameters:**
- `config`: Threshold configuration
- `parties`: Array of participating parties
**Returns:**
- `*PublicKey`: The group public key
- `map[int]*KeyShare`: Map of party ID to key share
- `error`: Any error that occurred
### ThresholdSign
Perform threshold signing with t-of-n parties.
```go
func ThresholdSign(
message []byte,
signers []*Party,
threshold int,
) (*ThresholdSignature, error)
```
**Parameters:**
- `message`: Message to sign
- `signers`: Participating signers (must be >= threshold)
- `threshold`: Minimum signers required
**Returns:**
- `*ThresholdSignature`: The threshold signature
- `error`: Any error that occurred
## Lattice Operations
### Polynomial Ring Functions
```go
// Create new polynomial ring
func NewRing(n int, q *big.Int) *ring.Ring
// Sample random polynomial
func SamplePoly(r *ring.Ring) ring.Poly
// Polynomial multiplication
func PolyMul(
r *ring.Ring,
a, b ring.Poly,
) ring.Poly
// NTT transformation
func NTT(r *ring.Ring, poly ring.Poly) ring.Poly
// Inverse NTT
func INTT(r *ring.Ring, poly ring.Poly) ring.Poly
```
### Matrix Operations
```go
// Matrix-vector multiplication
func MatrixVectorMul(
r *ring.Ring,
M structs.Matrix[ring.Poly],
v structs.Vector[ring.Poly],
) structs.Vector[ring.Poly]
// Matrix addition
func MatrixAdd(
r *ring.Ring,
A, B structs.Matrix[ring.Poly],
) structs.Matrix[ring.Poly]
// Sample random matrix
func SamplePolyMatrix(
r *ring.Ring,
rows, cols int,
sampler Sampler,
) structs.Matrix[ring.Poly]
```
## Utility Functions
### Hash Functions
```go
// Hash to elliptic curve point
func HashToPoint(data []byte) *Point
// Generate challenge hash
func ChallengeHash(components ...[]byte) *big.Int
// SHA-256 hash
func Hash256(data []byte) []byte
// Keccak-256 hash
func Keccak256(data []byte) []byte
```
### Serialization
```go
// Serialize ring signature
func SerializeRingSignature(sig *RingSignature) []byte
// Deserialize ring signature
func DeserializeRingSignature(data []byte) (*RingSignature, error)
// Encode to base64
func EncodeBase64(data []byte) string
// Decode from base64
func DecodeBase64(s string) ([]byte, error)
```
### Random Generation
```go
// Generate secure random bytes
func GenerateRandom(size int) ([]byte, error)
// Generate random scalar
func RandomScalar() (*big.Int, error)
// Generate random point
func RandomPoint() (*Point, error)
// Deterministic random from seed
func DeterministicRandom(seed []byte, size int) []byte
```
## Network Protocol
### Message Types
```go
const (
MsgKeyGenInit MessageType = 0x01
MsgKeyGenShare MessageType = 0x02
MsgSignRound1 MessageType = 0x10
MsgSignRound2 MessageType = 0x11
MsgSignRound3 MessageType = 0x12
MsgAbort MessageType = 0xFF
)
```
### Network Functions
```go
// Send message to party
func SendMessage(
party int,
msg NetworkMessage,
) error
// Broadcast to all parties
func Broadcast(
msg NetworkMessage,
) error
// Receive messages
func ReceiveMessages(
expectedType MessageType,
timeout time.Duration,
) ([]NetworkMessage, error)
// Establish secure channel
func EstablishChannel(
partyID int,
publicKey *PublicKey,
) (*SecureChannel, error)
```
## Configuration
### Config Structure
```go
type Config struct {
// Ring signature parameters
RingSize int
Algorithm Algorithm
HashFunction hash.Hash
// Lattice parameters
PolyDegree int
Modulus *big.Int
ErrorStdDev float64
// Threshold parameters
TotalParties int
Threshold int
// Security
SecurityLevel int
QuantumSafe bool
// Performance
ParallelOps bool
CacheSize int
}
```
### Default Configurations
```go
// Default configuration
func DefaultConfig() *Config
// High security configuration
func HighSecurityConfig() *Config
// Performance optimized configuration
func PerformanceConfig() *Config
// Quantum-safe configuration
func QuantumSafeConfig() *Config
```
## Error Types
```go
var (
// Key errors
ErrInvalidPrivateKey = errors.New("invalid private key")
ErrInvalidPublicKey = errors.New("invalid public key")
ErrKeyMismatch = errors.New("key mismatch")
// Signature errors
ErrInvalidSignature = errors.New("invalid signature")
ErrInvalidRingSize = errors.New("invalid ring size")
ErrSignerNotInRing = errors.New("signer not in ring")
// Threshold errors
ErrInsufficientSigners = errors.New("insufficient signers")
ErrInvalidThreshold = errors.New("invalid threshold")
ErrDuplicateSigner = errors.New("duplicate signer")
// Protocol errors
ErrProtocolAborted = errors.New("protocol aborted")
ErrTimeout = errors.New("operation timeout")
ErrInvalidMAC = errors.New("invalid MAC")
// Lattice errors
ErrInvalidPolynomial = errors.New("invalid polynomial")
ErrNTTFailed = errors.New("NTT operation failed")
)
```
## Constants
```go
const (
// Default values
DefaultRingSize = 16
DefaultPolyDegree = 4096
DefaultSecurityLevel = 128
// Size constants
PublicKeySize = 64 // bytes
PrivateKeySize = 32 // bytes
KeyImageSize = 64 // bytes
SignatureBase = 96 // bytes (before ring members)
// Algorithm identifiers
AlgoSchnorr Algorithm = 0x01
AlgoLattice Algorithm = 0x02
AlgoHybrid Algorithm = 0x03
// Security levels
SecurityLow = 80
SecurityMedium = 128
SecurityHigh = 192
SecurityMax = 256
)
```
## Example Usage
### Basic Ring Signature
```go
package main
import (
"fmt"
"log"
"github.com/luxfi/corona"
)
func main() {
// Generate keys for ring members
keys := make([]*corona.PublicKey, 16)
var signerKey *corona.PrivateKey
signerIndex := 7
for i := 0; i < 16; i++ {
pub, priv, err := corona.GenerateKeyPair()
if err != nil {
log.Fatal(err)
}
keys[i] = pub
if i == signerIndex {
signerKey = priv
} else {
priv.Clear() // Clean up other private keys
}
}
// Create and verify signature
message := []byte("confidential transaction")
sig, err := corona.SignRing(
message,
keys,
signerIndex,
signerKey,
)
if err != nil {
log.Fatal(err)
}
valid := corona.VerifyRing(message, sig)
fmt.Printf("Signature valid: %v\n", valid)
// Check linkability
sig2, _ := corona.SignRing(
[]byte("another message"),
keys,
signerIndex,
signerKey,
)
linked := corona.CheckLinkability(sig, sig2)
fmt.Printf("Signatures linked: %v\n", linked)
}
```
### Threshold Signature
```go
// Setup threshold scheme (3-of-5)
config, err := corona.ThresholdSetup(5, 3, 128)
if err != nil {
log.Fatal(err)
}
// Initialize parties
parties := make([]*corona.Party, 5)
for i := 0; i < 5; i++ {
parties[i] = corona.NewParty(i, config)
}
// Generate distributed key
pubKey, shares, err := corona.DistributedKeyGen(config, parties)
if err != nil {
log.Fatal(err)
}
// Sign with subset of parties (3 out of 5)
signers := []*corona.Party{parties[0], parties[2], parties[4]}
sig, err := corona.ThresholdSign(
[]byte("message"),
signers,
3,
)
if err != nil {
log.Fatal(err)
}
// Verify threshold signature
valid := corona.VerifyThreshold([]byte("message"), sig, pubKey)
fmt.Printf("Threshold signature valid: %v\n", valid)
```
-702
View File
@@ -1,702 +0,0 @@
---
title: Implementation Guide
description: Detailed implementation architecture and code structure of Corona
---
# Implementation Guide
## Architecture Overview
Corona is structured as a modular cryptographic library with clear separation of concerns:
```
corona/
├── primitives/ # Core cryptographic primitives
├── sign/ # Signature generation and verification
├── utils/ # Utility functions and helpers
├── networking/ # Network communication layer
└── docs/ # Documentation
```
## Core Components
### Primitives Package
The `primitives` package provides fundamental cryptographic operations:
```go
// primitives/shamir.go
package primitives
import (
"github.com/luxfi/lattice/v6/ring"
"github.com/luxfi/lattice/v6/utils/structs"
)
// ShamirSecretSharing implements (t,n) threshold secret sharing
func ShamirSecretSharing(
r *ring.Ring,
secret structs.Vector[ring.Poly],
threshold int,
lagrangeCoeffs structs.Vector[ring.Poly],
) map[int]structs.Vector[ring.Poly] {
// Generate random polynomial of degree threshold-1
// Evaluate at n points to create shares
// Return map of party ID to share
}
```
#### Hash Functions
Secure hash operations for ring signatures:
```go
// primitives/hash.go
func HashToPoint(data []byte) *Point {
// Hash arbitrary data to elliptic curve point
// Used for key image generation
h := sha256.Sum256(data)
// Map hash to curve point deterministically
return mapToCurve(h[:])
}
func ChallengeHash(components ...[]byte) *big.Int {
// Generate challenge for ring signature
h := sha256.New()
for _, c := range components {
h.Write(c)
}
return new(big.Int).SetBytes(h.Sum(nil))
}
```
### Signature Package
The `sign` package implements the ring signature protocol:
```go
// sign/sign.go
package sign
type Party struct {
ID int
Ring *ring.Ring // Polynomial ring
RingXi *ring.Ring // Rounding ring
RingNu *ring.Ring // Verification ring
UniformSampler *ring.UniformSampler
SkShare structs.Vector[ring.Poly] // Secret key share
Seed map[int][][]byte // PRG seeds
R structs.Matrix[ring.Poly]
C ring.Poly // Challenge
H structs.Vector[ring.Poly]
Lambda ring.Poly // Lagrange coefficient
D structs.Matrix[ring.Poly]
MACKeys map[int][]byte // MAC keys for parties
MACs map[int][]byte // Message auth codes
}
```
#### Signature Generation Phases
**Phase 1: Setup**
```go
func (party *Party) SignRound1(
A structs.Matrix[ring.Poly],
sid int,
PRFKey []byte,
T []int,
) (structs.Matrix[ring.Poly], map[int][]byte) {
// Generate randomness using PRF
// Compute first round messages
// Create MAC tags for other parties
}
```
**Phase 2: Challenge Computation**
```go
func (party *Party) SignRound2(
hintShares map[int]structs.Vector[uint8],
) (structs.Vector[uint8], []byte) {
// Aggregate hint shares
// Compute ring signature challenge
// Prepare response
}
```
**Phase 3: Response Generation**
```go
func (party *Party) SignRound3(
dShares map[int]structs.Matrix[ring.Poly],
) structs.Matrix[ring.Poly] {
// Combine shares from all parties
// Generate final signature response
// Clean up sensitive data
}
```
### Utilities Package
Helper functions for efficient operations:
```go
// utils/utils.go
package utils
// Matrix-vector multiplication in polynomial ring
func MatrixVectorMul(
r *ring.Ring,
M structs.Matrix[ring.Poly],
v structs.Vector[ring.Poly],
result structs.Vector[ring.Poly],
) {
for i := 0; i < len(M); i++ {
r.MulCoeffsMontgomery(M[i][0], v[0], result[i])
for j := 1; j < len(v); j++ {
r.MulCoeffsMontgomeryThenAdd(M[i][j], v[j], result[i])
}
}
}
// NTT conversions for efficiency
func ConvertVectorToNTT(r *ring.Ring, v structs.Vector[ring.Poly]) {
for i := range v {
r.NTT(v[i], v[i])
}
}
```
## Ring Signature Implementation
### Key Generation
```go
type RingMember struct {
PublicKey *PublicKey
PrivateKey *PrivateKey // nil for decoy members
Index int // position in ring
}
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
// Generate random private key
priv, err := rand.Int(rand.Reader, curve.N)
if err != nil {
return nil, nil, err
}
// Compute public key: P = x*G
pub := new(PublicKey)
pub.X, pub.Y = curve.ScalarBaseMult(priv.Bytes())
return pub, (*PrivateKey)(priv), nil
}
```
### Key Image Generation
```go
func GenerateKeyImage(privKey *PrivateKey) *KeyImage {
// Compute I = x * H(P)
pubKey := privKey.PublicKey()
hashPoint := HashToPoint(pubKey.Bytes())
keyImage := new(KeyImage)
keyImage.X, keyImage.Y = curve.ScalarMult(
hashPoint.X, hashPoint.Y,
privKey.Bytes(),
)
return keyImage
}
```
### Ring Construction
```go
func ConstructRing(
signerIndex int,
signerPubKey *PublicKey,
decoyKeys []*PublicKey,
) []*PublicKey {
ringSize := len(decoyKeys) + 1
ring := make([]*PublicKey, ringSize)
// Place signer at specified index
ring[signerIndex] = signerPubKey
// Fill remaining positions with decoys
decoyIdx := 0
for i := 0; i < ringSize; i++ {
if i != signerIndex {
ring[i] = decoyKeys[decoyIdx]
decoyIdx++
}
}
return ring
}
```
### Signature Algorithm
```go
func SignRing(
message []byte,
ring []*PublicKey,
signerIndex int,
privKey *PrivateKey,
) (*RingSignature, error) {
n := len(ring)
sig := &RingSignature{
KeyImage: GenerateKeyImage(privKey),
RingPubKeys: ring,
S: make([]*big.Int, n),
}
// Generate random responses for non-signers
c := make([]*big.Int, n)
for i := 0; i < n; i++ {
if i != signerIndex {
sig.S[i], _ = rand.Int(rand.Reader, curve.N)
c[i], _ = rand.Int(rand.Reader, curve.N)
}
}
// Generate commitment
alpha, _ := rand.Int(rand.Reader, curve.N)
L := make([]*Point, n)
R := make([]*Point, n)
// Compute L_i, R_i for signer
L[signerIndex] = new(Point)
L[signerIndex].X, L[signerIndex].Y = curve.ScalarBaseMult(alpha.Bytes())
R[signerIndex] = new(Point)
hP := HashToPoint(ring[signerIndex].Bytes())
R[signerIndex].X, R[signerIndex].Y = curve.ScalarMult(
hP.X, hP.Y, alpha.Bytes(),
)
// Compute L_j, R_j for non-signers
for i := (signerIndex + 1) % n; i != signerIndex; i = (i + 1) % n {
// L_j = s_j*G + c_j*P_j
L[i] = computeL(sig.S[i], c[i], ring[i])
// R_j = s_j*H(P_j) + c_j*I
R[i] = computeR(sig.S[i], c[i], ring[i], sig.KeyImage)
}
// Compute challenge
sig.C0 = ChallengeHash(message, L, R)
// Close the ring
cSum := new(big.Int)
for i := 0; i < n; i++ {
if i != signerIndex {
cSum.Add(cSum, c[i])
}
}
c[signerIndex] = new(big.Int).Sub(sig.C0, cSum)
c[signerIndex].Mod(c[signerIndex], curve.N)
// Compute signer's response
sig.S[signerIndex] = new(big.Int).Mul(c[signerIndex], privKey.Big())
sig.S[signerIndex].Sub(alpha, sig.S[signerIndex])
sig.S[signerIndex].Mod(sig.S[signerIndex], curve.N)
return sig, nil
}
```
### Verification Algorithm
```go
func VerifyRing(
message []byte,
sig *RingSignature,
) bool {
n := len(sig.RingPubKeys)
L := make([]*Point, n)
R := make([]*Point, n)
c := make([]*big.Int, n)
c[0] = sig.C0
for i := 0; i < n; i++ {
// Compute L_i = s_i*G + c_i*P_i
L[i] = computeL(sig.S[i], c[i], sig.RingPubKeys[i])
// Compute R_i = s_i*H(P_i) + c_i*I
R[i] = computeR(sig.S[i], c[i], sig.RingPubKeys[i], sig.KeyImage)
// Compute next challenge
if i < n-1 {
c[i+1] = ChallengeHash(message, L[i], R[i])
}
}
// Verify ring closure
finalChallenge := ChallengeHash(message, L, R)
return finalChallenge.Cmp(sig.C0) == 0
}
```
## Lattice Integration
### Polynomial Operations
```go
// Efficient polynomial multiplication using NTT
func PolyMul(r *ring.Ring, a, b, c ring.Poly) {
// Convert to NTT domain
r.NTT(a, a)
r.NTT(b, b)
// Component-wise multiplication
r.MulCoeffsMontgomery(a, b, c)
// Convert back from NTT
r.INTT(c, c)
}
// Gaussian sampling for error polynomials
func SampleGaussianPoly(r *ring.Ring, sigma float64) ring.Poly {
sampler := ring.NewGaussianSampler(
prng, r,
ring.DiscreteGaussian{Sigma: sigma},
false,
)
poly := r.NewPoly()
sampler.Read(poly)
return poly
}
```
### Threshold Signatures
```go
// Distributed signature generation
type ThresholdSigner struct {
Parties []*Party
Threshold int
PublicKey structs.Vector[ring.Poly]
}
func (ts *ThresholdSigner) Sign(
message []byte,
signers []int,
) (*ThresholdSignature, error) {
if len(signers) < ts.Threshold {
return nil, ErrInsufficientSigners
}
// Round 1: Generate partial signatures
round1Data := make(map[int]Round1Message)
for _, id := range signers {
data := ts.Parties[id].SignRound1(...)
round1Data[id] = data
}
// Round 2: Aggregate and compute challenge
round2Data := make(map[int]Round2Message)
for _, id := range signers {
data := ts.Parties[id].SignRound2(round1Data)
round2Data[id] = data
}
// Round 3: Generate final signature
signature := ts.combinePartialSignatures(round2Data)
return signature, nil
}
```
## Network Protocol
### Message Types
```go
type MessageType uint8
const (
MsgKeyGenInit MessageType = iota
MsgKeyGenShare
MsgSignRound1
MsgSignRound2
MsgSignRound3
MsgAbort
)
type NetworkMessage struct {
Type MessageType
SessionID []byte
PartyID int
Data []byte
MAC []byte
}
```
### Communication Layer
```go
func (p *Party) Broadcast(msg NetworkMessage) error {
// Add MAC for authentication
msg.MAC = p.computeMAC(msg)
// Send to all parties
for _, peer := range p.peers {
if err := peer.Send(msg); err != nil {
return err
}
}
return nil
}
func (p *Party) ReceiveMessages(
expectedType MessageType,
timeout time.Duration,
) (map[int]NetworkMessage, error) {
messages := make(map[int]NetworkMessage)
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
msg, err := p.network.Receive()
if err != nil {
continue
}
// Verify MAC
if !p.verifyMAC(msg) {
return nil, ErrInvalidMAC
}
// Check message type
if msg.Type != expectedType {
return nil, ErrUnexpectedMessage
}
messages[msg.PartyID] = msg
if len(messages) == len(p.peers) {
break
}
}
return messages, nil
}
```
## Security Considerations
### Memory Security
```go
// Secure memory handling
func (p *PrivateKey) Clear() {
// Overwrite private key material
bytes := p.Bytes()
for i := range bytes {
bytes[i] = 0
}
runtime.SetFinalizer(p, nil)
}
// Use defer for cleanup
func sensitiveOperation() {
privKey := generatePrivateKey()
defer privKey.Clear()
// Perform operations
// ...
}
```
### Randomness Quality
```go
// Always use crypto/rand for security-critical operations
import "crypto/rand"
func generateSecureRandom(size int) ([]byte, error) {
bytes := make([]byte, size)
_, err := rand.Read(bytes)
if err != nil {
return nil, fmt.Errorf("random generation failed: %w", err)
}
return bytes, nil
}
// Deterministic randomness for reproducibility
func deterministicRandom(seed []byte) *DeterministicRNG {
h := sha256.Sum256(seed)
return &DeterministicRNG{
state: h[:],
}
}
```
### Constant-Time Operations
```go
// Avoid timing leaks
func constantTimeCompare(a, b []byte) bool {
if len(a) != len(b) {
return false
}
var result byte
for i := 0; i < len(a); i++ {
result |= a[i] ^ b[i]
}
return result == 0
}
// Constant-time selection
func constantTimeSelect(v, a, b *big.Int) *big.Int {
// v = 1 -> return a
// v = 0 -> return b
mask := new(big.Int).Sub(v, big.NewInt(1))
mask.Not(mask)
result := new(big.Int)
result.And(a, mask)
tmp := new(big.Int)
tmp.Not(mask)
tmp.And(b, tmp)
result.Or(result, tmp)
return result
}
```
## Testing Strategy
### Unit Tests
```go
func TestRingSignature(t *testing.T) {
// Setup
ringSize := 16
ring := generateTestRing(ringSize)
signerIndex := 7
privKey := testPrivateKeys[signerIndex]
message := []byte("test message")
// Sign
sig, err := SignRing(message, ring, signerIndex, privKey)
require.NoError(t, err)
// Verify
valid := VerifyRing(message, sig)
assert.True(t, valid)
// Test linkability
sig2, _ := SignRing([]byte("another message"), ring, signerIndex, privKey)
assert.Equal(t, sig.KeyImage, sig2.KeyImage)
}
```
### Benchmarks
```go
func BenchmarkRingSign16(b *testing.B) {
ring := generateTestRing(16)
privKey := generatePrivateKey()
message := []byte("benchmark message")
b.ResetTimer()
for i := 0; i < b.N; i++ {
SignRing(message, ring, 0, privKey)
}
}
func BenchmarkRingVerify16(b *testing.B) {
ring := generateTestRing(16)
sig := generateTestSignature(ring)
message := []byte("benchmark message")
b.ResetTimer()
for i := 0; i < b.N; i++ {
VerifyRing(message, sig)
}
}
```
### Integration Tests
```go
func TestThresholdSigningProtocol(t *testing.T) {
// Setup threshold parameters
n := 7 // total parties
threshold := 5 // threshold
// Initialize parties
parties := setupParties(n, threshold)
// Select signers (must be >= threshold)
signers := []int{0, 2, 3, 4, 6}
// Run protocol
message := []byte("threshold test")
sig, err := runThresholdProtocol(parties, signers, message)
require.NoError(t, err)
assert.True(t, verifyThresholdSignature(message, sig))
}
```
## Deployment
### Library Usage
```go
import "github.com/luxfi/corona"
// Initialize Corona
rt := corona.New(corona.Config{
RingSize: 16,
Algorithm: corona.AlgoSchnorr,
HashFunction: sha256.New,
Curve: elliptic.P256(),
})
// Generate keys
pub, priv, _ := rt.GenerateKeyPair()
// Create ring signature
ring := rt.SelectRing(pub, 15) // 15 decoys + 1 signer
sig, _ := rt.Sign(message, ring, priv)
// Verify signature
valid := rt.Verify(message, sig)
```
### Configuration Options
```go
type Config struct {
// Ring parameters
RingSize int // Number of ring members
Algorithm Algorithm // Signature algorithm
// Lattice parameters
PolyDegree int // Polynomial degree (power of 2)
Modulus *big.Int // Ring modulus
ErrorStdDev float64 // Gaussian error standard deviation
// Threshold parameters
TotalParties int // Total number of parties
Threshold int // Signing threshold
// Security parameters
SecurityLevel int // Target security level in bits
QuantumSafe bool // Enable post-quantum features
// Performance tuning
ParallelOps bool // Enable parallel operations
CacheSize int // Signature cache size
BatchSize int // Batch verification size
}
```
-39
View File
@@ -1,39 +0,0 @@
---
title: Introduction
description: Corona - Privacy-preserving ring signatures for the Lux blockchain
---
# Corona
## Features
- High performance
- Production ready
- Well tested
- Comprehensive API
## Quick Start
```go
package main
import (
"github.com/luxfi/corona"
)
func main() {
// Your code here
}
```
## Documentation
Coming soon. For now, see the [source code](https://github.com/luxfi/corona).
## Next Steps
- API Reference
- Examples
- Configuration Guide
-440
View File
@@ -1,440 +0,0 @@
---
title: Privacy Guarantees
description: Understanding the privacy properties and security guarantees of Corona
---
# Privacy Guarantees
## Overview
Corona provides strong privacy guarantees through a combination of cryptographic techniques. This document details the privacy properties, threat models, and security guarantees provided by the system.
## Privacy Properties
### 1. Signer Anonymity
**Property**: The identity of the actual signer is hidden among a set of potential signers (the ring).
**Guarantee Level**: **Unconditional**
- Information-theoretic hiding
- No computational assumptions required
- Perfect indistinguishability among ring members
**Mathematical Foundation**:
```
For a ring R = {PK₁, PK₂, ..., PKₙ} and signature σ:
Pr[Signer = PKᵢ | σ] = 1/n for all i ∈ [1,n]
```
### 2. Unlinkability (Within Different Rings)
**Property**: Signatures using different rings cannot be linked to the same signer.
**Guarantee Level**: **Computational**
- Based on Decisional Diffie-Hellman (DDH) assumption
- Quantum resistance through lattice-based alternatives
**Formal Definition**:
```
For signatures σ₁ on ring R₁ and σ₂ on ring R₂:
If R₁ ≠ R₂, then σ₁ and σ₂ are unlinkable
```
### 3. Linkability (Within Same Context)
**Property**: Signatures from the same private key can be detected (preventing double-spending).
**Guarantee Level**: **Cryptographic**
- Deterministic key image generation
- Collision-resistant hash functions
**Key Image Property**:
```
KeyImage = x · H(P) where:
- x is the private key
- P is the public key
- H is a hash-to-point function
Same x → Same KeyImage (always)
Different x → Different KeyImage (with overwhelming probability)
```
### 4. Non-frameability
**Property**: No party can forge a signature that appears to come from another party's private key.
**Guarantee Level**: **Computational**
- Based on discrete logarithm assumption
- Existential unforgeability under chosen message attack (EUF-CMA)
### 5. Forward Privacy
**Property**: Past signatures remain anonymous even if private keys are later compromised.
**Guarantee Level**: **Perfect**
- Historical signatures cannot be de-anonymized
- Key compromise doesn't reveal past signing patterns
## Threat Models
### Passive Adversary
**Capabilities**:
- Observe all signatures on the blockchain
- Analyze transaction patterns
- Collect timing information
**Protection**:
- Ring signatures hide signer identity
- Key images prevent correlation attacks
- Constant-time implementations prevent timing attacks
**Limitations**:
- Cannot determine actual signer from signature
- Cannot link signatures across different rings
- Cannot predict future signatures
### Active Adversary
**Capabilities**:
- Create malicious signatures
- Attempt to forge signatures
- Control some ring members
**Protection**:
- Cryptographic unforgeability
- Threshold requirements for multi-party signatures
- MAC authentication in network protocols
**Attack Scenarios Protected Against**:
1. **Sybil Attacks**: Controlling multiple ring members doesn't break anonymity
2. **Chosen Ring Attacks**: Adversary cannot force specific ring compositions
3. **Replay Attacks**: Unique challenges prevent signature replay
### Quantum Adversary
**Capabilities**:
- Shor's algorithm for discrete logarithm
- Grover's algorithm for search problems
- Unlimited classical computation
**Protection (Hybrid Mode)**:
- Lattice-based signatures (quantum-resistant)
- Increased key sizes for Grover resistance
- Post-quantum key exchange protocols
**Security Levels**:
| Component | Classical | Post-Quantum |
|-----------|-----------|--------------|
| Ring Signatures | 128-bit | 64-bit |
| Lattice Signatures | 256-bit | 128-bit |
| Hybrid Mode | 256-bit | 128-bit |
## Anonymity Set Analysis
### Ring Size Impact
The anonymity set size directly affects privacy:
| Ring Size | Anonymity | Storage Overhead | Verification Time |
|-----------|-----------|------------------|-------------------|
| 4 | 2 bits | 128 bytes | 0.5 ms |
| 8 | 3 bits | 256 bytes | 1.0 ms |
| 16 | 4 bits | 512 bytes | 1.8 ms |
| 32 | 5 bits | 1024 bytes | 3.5 ms |
| 64 | 6 bits | 2048 bytes | 7.0 ms |
**Recommended Minimum**: 16 members (4 bits of anonymity)
### Temporal Analysis Resistance
**Ring Member Selection Strategy**:
```go
func SelectRingMembers(currentHeight int) []*PublicKey {
// Recent outputs (50%)
recent := selectFromRange(currentHeight-100, currentHeight)
// Medium-term outputs (30%)
medium := selectFromRange(currentHeight-1000, currentHeight-100)
// Historical outputs (20%)
historical := selectFromRange(0, currentHeight-1000)
return shuffle(append(recent, medium, historical...))
}
```
### Statistical Disclosure Attacks
**Protection Measures**:
1. **Uniform Distribution**: Ring members selected uniformly
2. **Output Mixing**: Include outputs from different time periods
3. **Value Similarity**: Select outputs with similar values
4. **Churn Prevention**: Limit frequency of ring reuse
## Information Leakage Analysis
### What IS Revealed
1. **Key Image**: Unique identifier for double-spend detection
2. **Ring Members**: The set of possible signers
3. **Message**: The signed data (unless encrypted)
4. **Timestamp**: When signature was created
### What is NOT Revealed
1. **Actual Signer**: Hidden among ring members
2. **Private Key**: No information about secret key
3. **Link Between Different Rings**: Unlinkable across contexts
4. **Transaction Amount**: Can be hidden with Pedersen commitments
### Metadata Protection
**Network-Level Privacy**:
```go
// Use Tor/I2P for transaction broadcast
func BroadcastTransaction(tx *Transaction) error {
// Route through privacy network
if config.UseTor {
return broadcastViaTor(tx)
}
// Add timing jitter
delay := randomDelay(0, 60*time.Second)
time.Sleep(delay)
// Broadcast to random subset of peers
peers := selectRandomPeers(8)
return broadcastToPeers(tx, peers)
}
```
## Cryptographic Assumptions
### Classical Security
**Discrete Logarithm Problem (DLP)**:
- Given: `P = xG`
- Find: `x`
- Hardness: 2^128 operations for 256-bit curves
**Decisional Diffie-Hellman (DDH)**:
- Distinguish: `(G, xG, yG, xyG)` from `(G, xG, yG, zG)`
- Required for: Unlinkability between rings
**Hash Function Properties**:
- Collision resistance: Cannot find `x ≠ y` where `H(x) = H(y)`
- Preimage resistance: Given `h`, cannot find `x` where `H(x) = h`
- Second preimage resistance: Given `x`, cannot find `y ≠ x` where `H(x) = H(y)`
### Post-Quantum Security
**Ring Learning With Errors (Ring-LWE)**:
- Given: `(A, As + e)` for small error `e`
- Find: Secret `s`
- Hardness: Exponential in lattice dimension
**Module Learning With Errors (Module-LWE)**:
- Generalization of Ring-LWE
- Used in NIST standardized algorithms
- Security parameter: 128-256 bits post-quantum
## Implementation Security
### Side-Channel Resistance
**Timing Attack Prevention**:
```go
// Constant-time scalar multiplication
func constantTimeScalarMult(k *big.Int, P *Point) *Point {
// Always perform same number of operations
result := IdentityPoint()
temp := P.Copy()
for i := 0; i < 256; i++ {
bit := k.Bit(i)
// Conditional move, not branch
result = conditionalAdd(result, temp, bit)
temp = temp.Double()
}
return result
}
```
**Power Analysis Protection**:
```go
// Randomized computation order
func protectedSign(msg []byte, ring []*PublicKey) *Signature {
// Random blinding factor
blind := randomScalar()
// Shuffle computation order
order := randomPermutation(len(ring))
// Process in random order with blinding
for _, idx := range order {
processRingMember(idx, blind)
}
// Remove blinding
return unblind(signature, blind)
}
```
### Memory Security
**Secure Cleanup**:
```go
// Zero memory after use
func (sk *PrivateKey) Clear() {
bytes := sk.Bytes()
// Overwrite multiple times
for i := 0; i < 3; i++ {
for j := range bytes {
bytes[j] = byte(rand.Intn(256))
}
}
// Final zero
for j := range bytes {
bytes[j] = 0
}
// Prevent compiler optimization
runtime.KeepAlive(bytes)
}
```
## Privacy Best Practices
### For Developers
1. **Ring Selection**:
- Use sufficient ring size (≥16)
- Randomize ring member selection
- Include diverse temporal distribution
2. **Key Management**:
- Generate fresh keys for each identity
- Secure key storage (HSM/TEE)
- Regular key rotation
3. **Network Privacy**:
- Use Tor/I2P for broadcast
- Add random delays
- Avoid correlatable patterns
### For Users
1. **Operational Security**:
- Don't reuse addresses
- Avoid temporal patterns
- Use privacy-preserving networks
2. **Ring Hygiene**:
- Verify ring size before signing
- Check ring member diversity
- Avoid rings with compromised keys
3. **Metadata Protection**:
- Randomize transaction timing
- Use different nodes for broadcast
- Avoid correlatable amounts
## Formal Security Proofs
### Theorem 1: Signer Anonymity
**Statement**: For any PPT adversary A, the advantage in identifying the actual signer is negligible.
**Proof Sketch**:
1. Ring signature is perfectly hiding
2. All ring members equally likely
3. Information-theoretic indistinguishability
### Theorem 2: Unforgeability
**Statement**: Under the DL assumption, no PPT adversary can forge a valid signature without the private key.
**Proof Sketch**:
1. Reduction to discrete logarithm problem
2. Forger implies DL solver
3. Contradiction with DL hardness
### Theorem 3: Linkability
**Statement**: Signatures from the same key produce identical key images with probability 1.
**Proof**:
1. Key image `I = xH(P)` is deterministic
2. Same `x` always produces same `I`
3. Different `x` produces different `I` (except negligible probability)
## Audit and Compliance
### Selective Disclosure
**View Keys**:
```go
type ViewKey struct {
KeyImage *KeyImage
PrivateView *big.Int
}
// Allow auditor to verify specific signatures
func (vk *ViewKey) VerifyOwnership(sig *RingSignature) bool {
return vk.KeyImage.Equal(sig.KeyImage)
}
```
### Compliance Features
1. **Time-locked Disclosure**: Keys revealed after specific time
2. **Threshold Disclosure**: Multiple parties required for reveal
3. **Regulatory Reporting**: Aggregate statistics without individual privacy loss
### Zero-Knowledge Proofs
**Proof of Assets**:
```go
// Prove ownership without revealing which signatures
func ProveAssets(amount *big.Int, signatures []*RingSignature) *ZKProof {
// Generate commitment to total
commitment := PedersenCommit(amount)
// Create range proof
rangeProof := BulletproofRange(amount)
// Link to signatures without revealing which
ownershipProof := ProveRingOwnership(signatures)
return &ZKProof{
Commitment: commitment,
Range: rangeProof,
Ownership: ownershipProof,
}
}
```
## Future Enhancements
### Advanced Privacy Features
1. **Stealth Addresses**: One-time addresses for receivers
2. **Confidential Assets**: Hide asset types
3. **Private Smart Contracts**: Compute on encrypted data
4. **Coinjoin Integration**: Additional mixing layer
### Research Directions
1. **Sublinear Ring Signatures**: O(log n) verification
2. **Updatable Anonymity Sets**: Dynamic ring membership
3. **Cross-Chain Privacy**: Universal anonymity pools
4. **Quantum-Safe Variants**: Full post-quantum security
## Conclusion
Corona provides strong, provable privacy guarantees suitable for financial privacy applications. The combination of ring signatures for anonymity, key images for double-spend prevention, and lattice cryptography for post-quantum security creates a robust privacy system resistant to current and future threats.
-209
View File
@@ -1,209 +0,0 @@
---
title: Ring Signatures
description: Understanding Corona's ring signature implementation for privacy-preserving transactions
---
# Ring Signatures in Corona
## Overview
Corona implements **linkable ring signatures** based on the Schnorr signature scheme, providing transaction privacy while maintaining the ability to detect double-spending. This cryptographic primitive enables users to prove membership in a group without revealing their specific identity.
## Key Concepts
### Anonymity Set
The **ring** consists of multiple public keys where:
- One key belongs to the actual signer
- Other keys are "decoys" that provide plausible deniability
- Default ring size: 16 members (configurable)
- Larger rings provide stronger privacy but increase computational cost
### Linkability
While ring signatures hide the signer's identity, linkability ensures:
- The same private key cannot sign twice for the same context
- Double-spending prevention in cryptocurrency applications
- Key images uniquely identify signatures from the same private key
## Mathematical Foundation
### Core Components
```go
// Ring signature structure
type RingSignature struct {
C0 *big.Int // Initial challenge
S []*big.Int // Response values for each ring member
KeyImage *Point // Unique identifier preventing double-signing
RingPubKeys []*PublicKey // Public keys forming the ring
}
```
### Algorithm Overview
1. **Key Image Generation**: `I = x * H(P)` where:
- `x` is the signer's private key
- `P` is the signer's public key
- `H()` is a hash-to-curve function
2. **Ring Construction**: Aggregate public keys from:
- The actual signer
- Randomly selected decoy keys
- Order is randomized to hide signer position
3. **Signature Generation**:
- Generate random nonces for each ring member
- Compute challenges using hash chain
- Create responses that satisfy verification equation
4. **Verification**: Check that:
- All responses are valid
- Challenge chain closes properly
- Key image is well-formed
## Implementation Details
### Signature Size
- Base size: 32 bytes × ring size
- Default (16 members): 512 bytes
- Scales linearly with ring size
### Performance Characteristics
- **Sign time**: O(n) where n = ring size
- **Verify time**: O(n)
- **Space complexity**: O(n)
### Security Parameters
- **Curve**: secp256k1 or Ed25519
- **Hash function**: SHA-256
- **Challenge size**: 256 bits
- **Response size**: 256 bits per ring member
## Integration with Lattice Cryptography
Corona uniquely combines ring signatures with lattice-based cryptography from the Lattice library:
```go
import (
"github.com/luxfi/lattice/v6/ring"
"github.com/luxfi/lattice/v6/utils/sampling"
)
```
### Polynomial Ring Operations
- Uses polynomial rings for efficient modular arithmetic
- NTT (Number Theoretic Transform) for fast polynomial multiplication
- Gaussian sampling for noise generation
### Threshold Signatures
- Shamir secret sharing for distributed key generation
- `t-of-n` threshold schemes supported
- Secure multiparty computation protocols
## Privacy Guarantees
### Unconditional Anonymity
- **Signer ambiguity**: Equal probability for each ring member
- **Perfect hiding**: No information theoretic advantage
- **Forward secrecy**: Past signatures remain anonymous even if keys are compromised
### Resistance to Analysis
- **Timing attacks**: Constant-time implementations
- **Side channels**: Protected against power analysis
- **Blockchain analysis**: Ring signatures break transaction graph analysis
## Use Cases in Q-Chain
### Private Transactions
- Hide sender identity in value transfers
- Preserve receiver privacy through stealth addresses
- Maintain public verifiability of transaction validity
### Confidential Smart Contracts
- Anonymous voting systems
- Private auctions
- Confidential DeFi operations
### Regulatory Compliance
- Optional view keys for authorized auditing
- Selective disclosure capabilities
- Compliance with privacy regulations
## Best Practices
### Ring Selection
1. **Size considerations**:
- Minimum: 8 members for basic privacy
- Recommended: 16 members for strong privacy
- Maximum: 64 members (performance consideration)
2. **Member selection**:
- Random sampling from active users
- Temporal proximity (recent transactions)
- Value similarity (similar transaction amounts)
### Key Management
- Secure storage of private keys
- Regular key rotation for long-term privacy
- Backup strategies for key recovery
### Performance Optimization
- Cache polynomial operations
- Batch verification for multiple signatures
- Parallel signature generation
## Security Considerations
### Known Attacks and Mitigations
1. **Key image analysis**: Use deterministic key image generation
2. **Ring intersection attacks**: Ensure sufficient ring overlap
3. **Timing analysis**: Implement constant-time operations
4. **Quantum threats**: Combined with post-quantum signatures
### Audit Trail
- Key images provide spending auditability
- Optional transaction memos for compliance
- Cryptographic proofs of non-double-spending
## Code Example
```go
// Generate a ring signature
func SignWithRing(message []byte, privateKey *PrivateKey, ring []*PublicKey) (*RingSignature, error) {
// Generate key image
keyImage := GenerateKeyImage(privateKey)
// Create signature components
sig := &RingSignature{
KeyImage: keyImage,
RingPubKeys: ring,
S: make([]*big.Int, len(ring)),
}
// Generate signature using Schnorr-based ring signature
// ... (implementation details)
return sig, nil
}
// Verify a ring signature
func VerifyRingSignature(message []byte, sig *RingSignature) bool {
// Verify challenge chain
// Check key image validity
// Validate responses
return valid
}
```
## Future Enhancements
### Planned Features
- **Bulletproofs integration**: Confidential amounts with ring signatures
- **Sublinear ring signatures**: Logarithmic signature size
- **Multi-ring signatures**: Cross-chain privacy
- **Zero-knowledge range proofs**: Amount privacy
### Research Directions
- Post-quantum ring signatures
- Efficient batch verification
- Ring signature aggregation
- Threshold ring signatures
-299
View File
@@ -1,299 +0,0 @@
---
title: Cryptographic Theory
description: Mathematical foundations of linkable ring signatures and lattice-based cryptography
---
# Cryptographic Theory
## Overview
Corona combines two powerful cryptographic primitives: **linkable ring signatures** for privacy and **lattice-based cryptography** for post-quantum security. This unique combination provides both strong privacy guarantees and resistance to quantum attacks.
## Ring Signature Mathematics
### Schnorr Signature Foundation
Corona's ring signatures are based on the Schnorr signature scheme, which provides:
- **Simplicity**: Elegant mathematical structure
- **Security**: Proven secure under discrete logarithm assumption
- **Efficiency**: Fast signature generation and verification
#### Standard Schnorr Signature
For a message `m`, private key `x`, and public key `P = xG`:
1. **Sign**:
- Choose random `k ∈ Zq`
- Compute `R = kG`
- Compute `e = H(R || P || m)`
- Compute `s = k + ex`
- Signature: `σ = (R, s)`
2. **Verify**:
- Check: `sG = R + eP`
### Extension to Ring Signatures
Ring signatures extend Schnorr signatures to hide the signer among a ring of public keys.
#### Ring Signature Generation
Given:
- Message `m`
- Private key `xi` with public key `Pi`
- Ring of public keys `{P0, P1, ..., Pn-1}`
Algorithm:
```
1. Generate key image: I = xi * H(Pi)
2. For j ≠ i:
- Choose random sj ∈ Zq
- Choose random cj ∈ Zq
3. Choose random α ∈ Zq
4. Compute:
- Li = αG
- Ri = α * H(Pi)
5. For j = i+1, ..., n-1, 0, ..., i-1:
- Lj = sjG + cjPj
- Rj = sjH(Pj) + cjI
6. Compute challenge:
c0 = H(m || L0 || ... || Ln-1 || R0 || ... || Rn-1)
7. Close the ring:
ci = c0 - Σ(j≠i) cj
si = α - cixi
8. Output: σ = (I, c0, s0, ..., sn-1)
```
#### Ring Signature Verification
Given signature `σ = (I, c0, s0, ..., sn-1)`:
```
1. For j = 0, ..., n-1:
- Compute Lj' = sjG + cjPj
- Compute Rj' = sjH(Pj) + cjI
2. Verify: c0 = H(m || L0' || ... || Ln-1' || R0' || ... || Rn-1')
```
### Linkability Property
The **key image** `I = x * H(P)` ensures linkability:
- Deterministic for a given private key
- Cannot be forged without knowledge of `x`
- Two signatures with the same `I` indicate double-signing
This property is crucial for preventing double-spending in cryptocurrency applications.
## Lattice-Based Cryptography
### Polynomial Rings
Corona leverages polynomial ring operations from the Lattice library:
#### Ring Definition
The ring `R = Z[X]/(X^n + 1)` where:
- `n` is a power of 2 (typically 2^12 to 2^14)
- Operations are performed modulo `q` (prime or power of prime)
#### Why Polynomial Rings?
1. **Efficiency**: NTT enables O(n log n) multiplication
2. **Security**: Based on Ring-LWE problem
3. **Structure**: Natural for threshold cryptography
### Number Theoretic Transform (NTT)
NTT is the finite field analogue of FFT:
```
Forward NTT: a(X) → â = NTT(a)
Inverse NTT: â → a(X) = NTT^(-1)(â)
Multiplication: a(X) * b(X) = NTT^(-1)(NTT(a) ⊙ NTT(b))
```
Where `⊙` denotes component-wise multiplication.
### Ring-LWE Problem
The security of lattice operations is based on the Ring Learning With Errors (Ring-LWE) problem:
Given `(A, b = As + e)` where:
- `A` is a random polynomial matrix
- `s` is a secret polynomial vector
- `e` is a small error polynomial
It is computationally hard to recover `s`.
## Threshold Signatures
### Shamir Secret Sharing
Corona implements `(t, n)` threshold signatures using polynomial secret sharing:
#### Share Generation
For secret `s` and threshold `t`:
1. Generate random polynomial `f(x) = s + a₁x + ... + aₜ₋₁x^(t-1)`
2. Compute shares: `sᵢ = f(i)` for `i = 1, ..., n`
3. Any `t` shares can reconstruct `s` using Lagrange interpolation
#### Lagrange Interpolation
```
s = Σᵢ sᵢ * λᵢ
where λᵢ = Π(j≠i) (j/(j-i))
```
### Distributed Key Generation (DKG)
Corona supports distributed key generation without trusted dealer:
1. **Share Distribution**: Each party generates and distributes shares
2. **Verification**: Feldman VSS ensures share validity
3. **Aggregation**: Combine shares to form distributed key
4. **Threshold Signing**: `t` parties collaborate to sign
## Security Analysis
### Privacy Guarantees
#### Unconditional Anonymity
- **Information-theoretic hiding**: Even computationally unbounded adversary cannot identify signer
- **Perfect indistinguishability**: All ring members equally likely
- **Statistical distance**: Negligible between different signers
#### Unforgeability
Under the discrete logarithm assumption:
- Cannot forge signature without private key
- Cannot create valid key image without secret
- Collision-resistant hash functions prevent replay
### Quantum Resistance
#### Post-Quantum Security Levels
| Algorithm | Classical Security | Quantum Security |
|-----------|-------------------|------------------|
| Ring Signatures (256-bit) | 128 bits | 64 bits |
| Ring-LWE (n=4096, q≈2^128) | 256 bits | 128 bits |
| Hybrid Mode | 256 bits | 128 bits |
#### Quantum Attack Vectors
1. **Grover's Algorithm**: √n speedup for discrete log
- Mitigation: Double key sizes
2. **Shor's Algorithm**: Polynomial time for discrete log
- Mitigation: Lattice-based alternatives
3. **Hybrid Approach**: Combine classical and post-quantum
- Current signatures remain valid
- Future signatures gain quantum resistance
## Performance Analysis
### Computational Complexity
| Operation | Ring Signature | Lattice Operations |
|-----------|---------------|-------------------|
| Key Generation | O(1) | O(n log n) |
| Sign | O(r) | O(n log n) |
| Verify | O(r) | O(n log n) |
| Key Image | O(1) | N/A |
Where:
- `r` = ring size
- `n` = polynomial degree
### Space Complexity
| Component | Size |
|-----------|------|
| Public Key | 32 bytes |
| Private Key | 32 bytes |
| Ring Signature | 32 + 32r bytes |
| Lattice Signature | ~10 KB |
| Key Image | 32 bytes |
### Benchmark Results
On Apple M1 Pro:
- **Ring Sign (16 members)**: 2.1 ms
- **Ring Verify (16 members)**: 1.8 ms
- **Lattice Sign**: 0.8 ms
- **Lattice Verify**: 0.4 ms
- **Hybrid Sign**: 2.9 ms
- **Hybrid Verify**: 2.2 ms
## Advanced Topics
### Bulletproofs Integration
Corona can be extended with Bulletproofs for:
- **Confidential amounts**: Hide transaction values
- **Range proofs**: Prove values are in valid range
- **Aggregation**: Combine multiple proofs
### Sublinear Ring Signatures
Future enhancement using techniques like:
- **LSAG**: Linkable Spontaneous Anonymous Group signatures
- **Omniring**: O(log n) verification time
- **RingCT 3.0**: Logarithmic size signatures
### Cross-Chain Privacy
Multi-ring signatures for:
- **Atomic swaps**: Private cross-chain exchanges
- **Bridge privacy**: Hide cross-chain transfers
- **Universal anonymity**: Single anonymity set across chains
## Implementation Considerations
### Side-Channel Protection
1. **Constant-time operations**: No data-dependent branches
2. **Memory access patterns**: Independent of secret data
3. **Power analysis**: Randomized computation order
4. **Cache timing**: Preload all ring members
### Random Number Generation
Critical for security:
```go
// Use crypto/rand for security-critical randomness
import "crypto/rand"
func generateNonce() (*big.Int, error) {
max := new(big.Int).Sub(curve.N, big.NewInt(1))
k, err := rand.Int(rand.Reader, max)
if err != nil {
return nil, err
}
return k, nil
}
```
### Error Handling
Proper error propagation without leaking information:
- Generic error messages
- Timing-independent error paths
- No correlation between errors and secrets
## References
### Academic Papers
1. "Linkable Spontaneous Anonymous Group Signature" - Liu et al.
2. "Ring Confidential Transactions" - Noether et al.
3. "Lattice-based Zero-Knowledge Proofs" - Lyubashevsky
4. "Post-Quantum Ring Signatures" - Torres et al.
### Standards
- NIST Post-Quantum Cryptography
- IETF Ring Signatures Draft
- ISO/IEC 29192-7 Lightweight Cryptography
### Implementation Resources
- Monero RingCT
- Lattice Cryptography Library
- Stanford IBE Implementation
- Microsoft SEAL Library
-730
View File
@@ -1,730 +0,0 @@
---
title: Usage Examples
description: Practical examples and code samples for using Corona
---
# Usage Examples
## Getting Started
### Installation
```bash
# Install Corona library
go get github.com/luxfi/corona
# Install dependencies
go get github.com/luxfi/lattice/v6
```
### Basic Setup
```go
package main
import (
"fmt"
"log"
"github.com/luxfi/corona"
"github.com/luxfi/corona/primitives"
"github.com/luxfi/corona/sign"
)
func main() {
// Initialize Corona with default configuration
rt := corona.New(corona.DefaultConfig())
fmt.Println("Corona initialized successfully")
}
```
## Simple Ring Signature
### Generate Keys and Create Signature
```go
package main
import (
"crypto/rand"
"fmt"
"log"
"math/big"
)
func simpleRingSignatureExample() {
// 1. Generate key pairs for ring members
ringSize := 16
publicKeys := make([]*PublicKey, ringSize)
// Generate keys for all ring members
var signerPrivKey *PrivateKey
signerIndex := 7 // The actual signer will be at index 7
for i := 0; i < ringSize; i++ {
pub, priv, err := GenerateKeyPair()
if err != nil {
log.Fatal(err)
}
publicKeys[i] = pub
// Keep the private key for the actual signer
if i == signerIndex {
signerPrivKey = priv
}
}
// 2. Create a message to sign
message := []byte("Transfer 100 LUX to address xyz")
// 3. Generate the ring signature
signature, err := SignRing(
message,
publicKeys,
signerIndex,
signerPrivKey,
)
if err != nil {
log.Fatal(err)
}
// 4. Verify the signature
valid := VerifyRing(message, signature)
fmt.Printf("Signature valid: %v\n", valid)
// 5. Check signature size
sigSize := signature.Size()
fmt.Printf("Signature size: %d bytes\n", sigSize)
// 6. Demonstrate linkability
message2 := []byte("Another transaction")
signature2, _ := SignRing(message2, publicKeys, signerIndex, signerPrivKey)
// Both signatures have the same key image
if signature.KeyImage.Equal(signature2.KeyImage) {
fmt.Println("Signatures are linkable (same signer)")
}
}
```
## Privacy-Preserving Transaction
### Confidential Transfer with Ring Signatures
```go
type ConfidentialTransaction struct {
Inputs []TxInput
Outputs []TxOutput
RingSize int
Signatures []*RingSignature
}
type TxInput struct {
Amount uint64
KeyImage *KeyImage
RingMembers []*PublicKey
}
type TxOutput struct {
Amount uint64
Recipient *PublicKey
Ephemeral *PublicKey // For stealth address
}
func createConfidentialTransaction() (*ConfidentialTransaction, error) {
// 1. Select inputs (previous outputs we own)
inputs := []TxInput{
{
Amount: 100,
KeyImage: nil, // Will be set by signature
},
}
// 2. Create ring for input
ringSize := 16
ring := selectRingMembers(ringSize)
inputs[0].RingMembers = ring
// 3. Create outputs with stealth addresses
outputs := []TxOutput{
{
Amount: 90, // Send 90
Recipient: generateStealthAddress(recipientPubKey),
},
{
Amount: 10, // Change
Recipient: generateStealthAddress(ownPubKey),
},
}
// 4. Create transaction
tx := &ConfidentialTransaction{
Inputs: inputs,
Outputs: outputs,
RingSize: ringSize,
}
// 5. Sign each input with ring signature
for i, input := range tx.Inputs {
message := tx.HashForSigning(i)
sig, err := SignRing(
message,
input.RingMembers,
myIndex,
myPrivKey,
)
if err != nil {
return nil, err
}
tx.Signatures = append(tx.Signatures, sig)
tx.Inputs[i].KeyImage = sig.KeyImage
}
return tx, nil
}
func (tx *ConfidentialTransaction) Verify() bool {
// 1. Check balance (inputs = outputs)
if !tx.checkBalance() {
return false
}
// 2. Verify each ring signature
for i, sig := range tx.Signatures {
message := tx.HashForSigning(i)
if !VerifyRing(message, sig) {
return false
}
}
// 3. Check for double-spending (unique key images)
keyImages := make(map[string]bool)
for _, input := range tx.Inputs {
ki := input.KeyImage.String()
if keyImages[ki] {
return false // Double spend detected
}
keyImages[ki] = true
}
return true
}
```
## Threshold Ring Signatures
### Multi-Party Signing Protocol
```go
func thresholdRingSignatureExample() error {
// Setup: 3-of-5 threshold with 16-member ring
n := 5 // Total parties
threshold := 3 // Minimum signers
ringSize := 16
// 1. Initialize parties
parties := make([]*sign.Party, n)
for i := 0; i < n; i++ {
parties[i] = initializeParty(i)
}
// 2. Distributed Key Generation (DKG)
fmt.Println("Starting DKG protocol...")
publicKey, keyShares, err := runDKG(parties)
if err != nil {
return fmt.Errorf("DKG failed: %w", err)
}
fmt.Printf("Group public key generated: %x\n", publicKey.Bytes())
// 3. Distribute key shares to parties
for i, party := range parties {
party.SetKeyShare(keyShares[i])
}
// 4. Select subset of parties for signing (must be >= threshold)
signers := []int{0, 2, 4} // Parties 0, 2, and 4 will sign
// 5. Create ring with decoy members
ring := make([]*PublicKey, ringSize)
ring[0] = publicKey // Group's public key
for i := 1; i < ringSize; i++ {
ring[i] = generateDecoyKey()
}
// 6. Execute threshold signing protocol
message := []byte("Threshold signed message")
signature, err := executeThresholdSigning(
message,
ring,
parties,
signers,
threshold,
)
if err != nil {
return fmt.Errorf("threshold signing failed: %w", err)
}
// 7. Verify the threshold ring signature
valid := VerifyThresholdRingSignature(message, signature)
fmt.Printf("Threshold ring signature valid: %v\n", valid)
return nil
}
func executeThresholdSigning(
message []byte,
ring []*PublicKey,
parties []*sign.Party,
signers []int,
threshold int,
) (*ThresholdRingSignature, error) {
// Round 1: Generate commitments
round1Messages := make(map[int]Round1Message)
for _, id := range signers {
msg, err := parties[id].SignRound1(message, ring)
if err != nil {
return nil, err
}
round1Messages[id] = msg
}
// Broadcast round 1 messages
for _, id := range signers {
parties[id].ReceiveRound1(round1Messages)
}
// Round 2: Generate shares
round2Messages := make(map[int]Round2Message)
for _, id := range signers {
msg, err := parties[id].SignRound2()
if err != nil {
return nil, err
}
round2Messages[id] = msg
}
// Broadcast round 2 messages
for _, id := range signers {
parties[id].ReceiveRound2(round2Messages)
}
// Round 3: Combine shares
var signature *ThresholdRingSignature
for _, id := range signers {
sig, err := parties[id].SignRound3()
if err != nil {
return nil, err
}
if signature == nil {
signature = sig
}
}
return signature, nil
}
```
## Lattice-Based Ring Signatures
### Post-Quantum Secure Implementation
```go
import (
"github.com/luxfi/lattice/v6/ring"
"github.com/luxfi/lattice/v6/utils/sampling"
)
func latticeRingSignatureExample() {
// 1. Setup lattice parameters
n := 4096 // Polynomial degree
q := big.NewInt(0).SetBit(big.NewInt(0), 119, 1) // Prime modulus
r, err := ring.NewRing(n, q)
if err != nil {
log.Fatal(err)
}
// 2. Setup samplers
prng, _ := sampling.NewKeyedPRNG([]byte("seed"))
uniformSampler := ring.NewUniformSampler(prng, r)
gaussianSampler := ring.NewGaussianSampler(
prng, r,
ring.DiscreteGaussian{Sigma: 3.2},
false,
)
// 3. Generate lattice-based keys
ringSize := 8 // Smaller ring due to larger signatures
publicKeys := make([]LatticePublicKey, ringSize)
var signerSecret LatticeSecretKey
signerIndex := 3
for i := 0; i < ringSize; i++ {
pk, sk := generateLatticeKeyPair(r, uniformSampler, gaussianSampler)
publicKeys[i] = pk
if i == signerIndex {
signerSecret = sk
}
}
// 4. Create lattice ring signature
message := []byte("Post-quantum secure message")
sig := createLatticeRingSignature(
r,
message,
publicKeys,
signerIndex,
signerSecret,
)
// 5. Verify signature
valid := verifyLatticeRingSignature(r, message, sig, publicKeys)
fmt.Printf("Lattice ring signature valid: %v\n", valid)
// 6. Check signature size
sigBytes := sig.Serialize()
fmt.Printf("Lattice signature size: %d KB\n", len(sigBytes)/1024)
}
type LatticePublicKey struct {
A structs.Matrix[ring.Poly]
B structs.Vector[ring.Poly]
}
type LatticeSecretKey struct {
S structs.Vector[ring.Poly]
}
func generateLatticeKeyPair(
r *ring.Ring,
uniformSampler *ring.UniformSampler,
gaussianSampler *ring.GaussianSampler,
) (LatticePublicKey, LatticeSecretKey) {
// Generate random matrix A
m, n := 8, 8
A := utils.SamplePolyMatrix(r, m, n, uniformSampler, true, true)
// Generate secret vector s
s := utils.SamplePolyVector(r, n, gaussianSampler, false, false)
// Generate error vector e
e := utils.SamplePolyVector(r, m, gaussianSampler, false, false)
// Compute public key b = As + e
b := utils.InitializeVector(r, m)
utils.MatrixVectorMul(r, A, s, b)
utils.VectorAdd(r, b, e, b)
return LatticePublicKey{A: A, B: b}, LatticeSecretKey{S: s}
}
```
## Integration with Q-Chain
### Using Corona in QuantumVM
```go
// Smart contract using ring signatures
contract PrivacyPool {
mapping(bytes32 => bool) public keyImages;
uint256 public poolBalance;
event Deposit(bytes32 commitment);
event Withdrawal(bytes32 keyImage);
function deposit() external payable {
require(msg.value > 0, "Invalid amount");
// Generate commitment for depositor
bytes32 commitment = keccak256(
abi.encodePacked(msg.sender, msg.value, block.timestamp)
);
poolBalance += msg.value;
emit Deposit(commitment);
}
function withdrawWithRingSignature(
uint256 amount,
bytes memory ringSignature,
bytes32[] memory ringCommitments
) external {
// 1. Verify ring signature
require(
verifyRingSignature(
abi.encode(amount, msg.sender),
ringSignature,
ringCommitments
),
"Invalid ring signature"
);
// 2. Extract and check key image
bytes32 keyImage = extractKeyImage(ringSignature);
require(!keyImages[keyImage], "Double spending");
// 3. Mark key image as used
keyImages[keyImage] = true;
// 4. Transfer funds
require(poolBalance >= amount, "Insufficient pool balance");
poolBalance -= amount;
payable(msg.sender).transfer(amount);
emit Withdrawal(keyImage);
}
function verifyRingSignature(
bytes memory message,
bytes memory signature,
bytes32[] memory ring
) internal pure returns (bool) {
// Call Corona precompile for verification
return ICoronaVerifier(CORONA_PRECOMPILE).verify(
message,
signature,
ring
);
}
}
```
## Performance Benchmarks
### Benchmark Different Configurations
```go
func runBenchmarks() {
configs := []struct {
name string
ringSize int
algorithm string
}{
{"Small Ring (8)", 8, "schnorr"},
{"Medium Ring (16)", 16, "schnorr"},
{"Large Ring (32)", 32, "schnorr"},
{"Lattice Small (4)", 4, "lattice"},
{"Lattice Medium (8)", 8, "lattice"},
{"Hybrid (16)", 16, "hybrid"},
}
for _, cfg := range configs {
fmt.Printf("\n=== %s ===\n", cfg.name)
// Setup
ring := generateRing(cfg.ringSize)
message := []byte("benchmark message")
// Measure signing time
start := time.Now()
sig := sign(message, ring, cfg.algorithm)
signTime := time.Since(start)
// Measure verification time
start = time.Now()
verify(message, sig, cfg.algorithm)
verifyTime := time.Since(start)
// Measure signature size
sigSize := len(sig.Serialize())
fmt.Printf("Sign time: %v\n", signTime)
fmt.Printf("Verify time: %v\n", verifyTime)
fmt.Printf("Signature size: %d bytes\n", sigSize)
fmt.Printf("Size per ring member: %d bytes\n", sigSize/cfg.ringSize)
}
}
// Expected output:
// === Small Ring (8) ===
// Sign time: 1.2ms
// Verify time: 0.9ms
// Signature size: 288 bytes
// Size per ring member: 36 bytes
//
// === Medium Ring (16) ===
// Sign time: 2.1ms
// Verify time: 1.8ms
// Signature size: 544 bytes
// Size per ring member: 34 bytes
//
// === Large Ring (32) ===
// Sign time: 4.2ms
// Verify time: 3.5ms
// Signature size: 1056 bytes
// Size per ring member: 33 bytes
//
// === Lattice Small (4) ===
// Sign time: 3.5ms
// Verify time: 2.1ms
// Signature size: 8192 bytes
// Size per ring member: 2048 bytes
//
// === Hybrid (16) ===
// Sign time: 5.3ms
// Verify time: 3.9ms
// Signature size: 8704 bytes
// Size per ring member: 544 bytes
```
## Error Handling
### Comprehensive Error Handling
```go
func robustRingSignature() error {
// 1. Validate inputs
if len(ring) < MinRingSize {
return fmt.Errorf("ring too small: got %d, need at least %d",
len(ring), MinRingSize)
}
if signerIndex >= len(ring) {
return fmt.Errorf("signer index out of bounds: %d >= %d",
signerIndex, len(ring))
}
// 2. Check for duplicate keys in ring
seen := make(map[string]bool)
for i, pk := range ring {
key := pk.String()
if seen[key] {
return fmt.Errorf("duplicate key at index %d", i)
}
seen[key] = true
}
// 3. Validate private key corresponds to public key
derivedPub := DerivePublicKey(privKey)
if !derivedPub.Equal(ring[signerIndex]) {
return errors.New("private key doesn't match public key in ring")
}
// 4. Sign with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
sigChan := make(chan *RingSignature)
errChan := make(chan error)
go func() {
sig, err := SignRingWithContext(ctx, message, ring, signerIndex, privKey)
if err != nil {
errChan <- err
} else {
sigChan <- sig
}
}()
select {
case sig := <-sigChan:
// 5. Verify signature before returning
if !VerifyRing(message, sig) {
return errors.New("generated signature failed verification")
}
return nil
case err := <-errChan:
return fmt.Errorf("signing failed: %w", err)
case <-ctx.Done():
return errors.New("signing timeout exceeded")
}
}
```
## Testing Utilities
### Test Helper Functions
```go
// Generate test ring with known keys
func generateTestRing(size int) (*TestRing, error) {
tr := &TestRing{
Size: size,
PublicKeys: make([]*PublicKey, size),
PrivateKeys: make([]*PrivateKey, size),
}
for i := 0; i < size; i++ {
pub, priv, err := GenerateKeyPair()
if err != nil {
return nil, err
}
tr.PublicKeys[i] = pub
tr.PrivateKeys[i] = priv
}
return tr, nil
}
// Test double-spending detection
func testDoubleSpending(t *testing.T) {
ring, _ := generateTestRing(16)
message1 := []byte("first transaction")
message2 := []byte("second transaction")
// Sign two different messages with same key
sig1, _ := SignRing(message1, ring.PublicKeys, 0, ring.PrivateKeys[0])
sig2, _ := SignRing(message2, ring.PublicKeys, 0, ring.PrivateKeys[0])
// Key images should be identical (linkable)
if !sig1.KeyImage.Equal(sig2.KeyImage) {
t.Error("Key images should be identical for same signer")
}
// Create double-spend detector
detector := NewDoubleSpendDetector()
// First signature should be accepted
if !detector.Add(sig1.KeyImage) {
t.Error("First signature should be accepted")
}
// Second signature should be rejected (double-spend)
if detector.Add(sig2.KeyImage) {
t.Error("Second signature should be rejected as double-spend")
}
}
type DoubleSpendDetector struct {
keyImages map[string]bool
mu sync.RWMutex
}
func NewDoubleSpendDetector() *DoubleSpendDetector {
return &DoubleSpendDetector{
keyImages: make(map[string]bool),
}
}
func (d *DoubleSpendDetector) Add(ki *KeyImage) bool {
d.mu.Lock()
defer d.mu.Unlock()
key := ki.String()
if d.keyImages[key] {
return false // Already seen
}
d.keyImages[key] = true
return true
}
```
## Conclusion
These examples demonstrate the versatility and power of Corona for privacy-preserving applications. From simple ring signatures to complex threshold protocols and post-quantum implementations, Corona provides the tools needed for strong privacy guarantees in blockchain and cryptographic applications.
-18
View File
@@ -1,18 +0,0 @@
import { docs } from "@/.source"
import { loader } from "fumadocs-core/source"
// Create a single source instance that is reused
// This prevents circular references and stack overflow issues
let _source: ReturnType<typeof loader> | null = null
export function getSource() {
if (!_source) {
_source = loader({
baseUrl: "/docs",
source: docs.toFumadocsSource(),
})
}
return _source
}
export const source = getSource()
-9
View File
@@ -1,9 +0,0 @@
import type { MDXComponents } from "mdx/types"
import defaultMdxComponents from "fumadocs-ui/mdx"
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...defaultMdxComponents,
...components,
}
}
+389
View File
@@ -0,0 +1,389 @@
# Corona — TEE Integration (proposed)
> Companion to `DEPLOYMENT-RUNBOOK.md §Operational mitigations item 5`
> ("TEE attestation, recommended"). This document specifies the
> attestation boundary, key-isolation contract, and the per-platform
> integration shape — without complecting any of this with the
> Corona cryptographic core.
**Decomplecting rule (load-bearing).** TEE attestation is a single
*layer* sitting outside the cryptographic math:
```
+--------------------------------------------------------------+
| operator policy: which TEE? which key-release predicate? | policy
+--------------------------------------------------------------+
| attestation: evidence -> verifier -> bool | trust
+--------------------------------------------------------------+
| share storage / signing: sealed share -> Round1/Round2/Final | crypto
+--------------------------------------------------------------+
| Corona kernel: 2-round R-LWE signing | math
+--------------------------------------------------------------+
```
The Corona kernel (`sign/`, `threshold/`, `dkg2/`, `reshare/`) MUST
NOT depend on, link against, or branch on TEE state. The TEE layer
is **only** consulted at three lifecycle moments:
1. **Bootstrap-trusted-dealer ceremony**: caller of
`keyera.BootstrapTrustedDealer` decides whether to run inside a
TEE. Corona observes the result via the standard
`BootstrapTranscript` — no API change.
2. **Per-validator share storage**: each validator's `KeyShare`
bytes are sealed under a TEE-bound key. Corona observes the
share via the standard `KeyShare` struct — no API change.
3. **Per-validator signing session**: `Round1`, `Round2`, and
`Finalize` execute inside the TEE. Corona observes the signing
protocol via the standard `(D, MACs)`, `Z`, `Signature` byte
structures — no API change.
There is no `corona.AttestationVerifier` interface, no
`corona.WithTEE(...)` option, no TEE-specific build tag. The
TEE layer is an *operator's deployment concern* and is wired
above Corona via the consensus layer's validator-bootstrap
plumbing.
---
## §1 Threat model addition
In `docs/mptc/threat-model.md §9.2 "Out of scope at this submission"`,
the following items are flagged as **deployment-layer-mitigated** via
TEE:
- Cache-timing side channels
- Power side channels
- EM side channels
- Aggregator-host compromise during signing session
(`DEPLOYMENT-RUNBOOK.md §Trust-model disclosure item 2`)
The TEE integration layer brings these from "not addressed" to
"addressed under the TCB of {SGX, SEV-SNP, TDX, NVIDIA-CC, Apple-SEP}",
with the following residual assumption surface:
| Assumption | What we trust |
|---|---|
| TEE vendor firmware | The CPU/GPU vendor's microcode, BIOS, and signed bootloader. |
| TEE attestation root | The vendor's attestation signing key (Intel IAS, AMD KDS, NVIDIA NRAS, Apple AAS). |
| Reproducible build | The `luxfi/corona` binary measured into the attestation report matches a publicly-published Git SHA at a tagged release. |
| TEE side-channel resistance | The TEE's own side-channel posture — bounded by the public CVE record of the chosen platform (e.g., SGX has a long history of micro-architectural side-channel attacks; SEV-SNP and TDX are more recent). |
The TEE layer trades a TCB inclusion (vendor microcode + attestation
root) for closure of the OS-kernel-compromise and side-channel
threats. Operators MUST evaluate whether this trade is acceptable
for their specific deployment.
## §2 Reproducible build contract
Every Corona release tag (`v0.7.x`) ships:
1. **Source SHA**: the Git SHA of the release commit.
2. **`scripts/build.sh` invocation**: produces the binary
`bin/corona` from the release commit.
3. **Reference-measure manifest**: SHA-256 of `bin/corona` produced
on a pinned builder (`golang:1.26.3-alpine` per `Dockerfile`).
4. **Cross-runtime byte-equality**: `scripts/regen-kats.sh --verify`
against the C++ port at `~/work/luxcpp/crypto/corona/`.
The TEE attestation verifier consumes (1) + (3) and rejects any
report whose measurement does not match the published manifest. The
manifest is published at `https://lux.network/release/corona/v0.7.x/manifest.json`
and signed by the foundation release key.
> **Roadmap (v0.7.6)**: pin the `golang:1.26.3-alpine` builder image
> by digest, not tag, so the reference measure is reproducible
> against a specific OCI image digest.
## §3 Per-platform integration
### §3.1 AMD SEV-SNP (Linux validators)
**Status**: recommended primary path for Linux validators.
**Boundary**: full VM-level confidentiality. The Corona validator
process runs inside a SEV-SNP-protected VM ("CVM"). The
hypervisor sees encrypted memory; the host kernel cannot read
guest pages.
**Attestation flow**:
1. CVM boot loads OVMF -> Linux kernel -> `cosi-cvm-agent` (the
Lux CVM bootstrap agent; lives in `~/work/lux/cosi-cvm-agent`).
2. Agent calls `SNP_GET_REPORT` ioctl to obtain a SEV-SNP
attestation report binding the in-memory `(measurement,
report_data)`. `report_data` is the SHA-256 of the
validator-bound `report_nonce` (received from the registration
service on first contact).
3. Agent posts the report to the consensus layer's TEE-registration
endpoint. The endpoint verifies the report against AMD's KDS
(Key Distribution Service) using the chip's VCEK certificate,
confirms `measurement` matches the published `bin/corona`
manifest, and accepts the validator.
4. On accept, the consensus layer releases the validator's
pre-sealed `KeyShare` (encrypted under a key derived from the
attestation transcript) to the agent.
5. Agent unseals into the SEV-SNP-protected memory; Corona's
`threshold.NewSigner` operates on the unsealed share.
**Key isolation**: the SEV-SNP VM memory is encrypted by the
PSP-bound C-bit memory encryption. The unsealed share is never
visible to the hypervisor or the host kernel.
**Side channels addressed**: cold-boot, DMA-based read of guest
memory, hypervisor-introspection, and (under SEV-SNP integrity
protection, not SEV-ES) RowHammer and Spectre-class transient
attacks from the host.
**Residual risks**: SEV-SNP's own published CVE record (e.g.,
CVE-2023-20593 "Zenbleed", CVE-2024-3315 "SmmCallout"). Operators
MUST apply microcode patches.
**Code touchpoint**: zero. Corona's `KeyShare` bytes are unsealed
by the agent and handed to `threshold.NewSigner` via the standard
Go struct.
### §3.2 NVIDIA Confidential Computing (H100/H200/B200 CC mode)
**Status**: recommended when GPU-accelerated NTT/Montgomery is
used in production. Corona's `gpu` package opts the lattice NTT
dispatch into the GPU at validator setup time; the GPU itself
becomes part of the validator's TCB. **Confidential Computing**
mode on H100/H200/B200 puts the GPU into an attestable
confidential mode where the host CPU cannot read GPU memory.
**Boundary**: a CVM (SEV-SNP or TDX) is paired with a CC-mode
GPU. The PCIe link is encrypted via SPDM (Security Protocol and
Data Model) session keys. The GPU's HBM memory is protected by
the GPU's own confidential-mode firmware.
**Attestation flow**:
1. CVM boot (per §3.1 or §3.3).
2. CVM-to-GPU SPDM session is established. The GPU produces a
**device attestation report** signed by the NRAS (NVIDIA Remote
Attestation Service) root.
3. Agent verifies the GPU report via NRAS, confirms the GPU
firmware version + CC-mode active.
4. Composite attestation: agent emits `evidence = SNP_report ||
NRAS_GPU_report || binding_nonce`. The consensus-layer verifier
checks both reports and the binding.
5. Corona's `gpu.UseAccelerator()` is called inside the CVM after
composite attestation passes. The lattice GPU NTT dispatch
runs over the encrypted PCIe link to the CC-mode GPU.
**Key isolation**: secret shares never leave the CVM's encrypted
memory. The data sent to the GPU for NTT computation is the
*coefficient vector after Montgomery encoding* — the secret-share
material is mixed in earlier (in CPU code) and the GPU NTT operates
on values that are blinded by the per-session mask `R` (Round 1)
and the public matrix `A` (Round 2). The GPU sees no clear-text
share.
> **HONEST GAP**: the precise blinding analysis ("what does the GPU
> see, formally?") for the CC-mode dispatch is an open
> question for the v0.8.0 cryptographic audit. Until that audit
> completes, deploying Corona under CC-mode GPU is APPROVED with
> the caveat that the GPU is part of the TCB; operators MUST
> evaluate whether they trust NVIDIA's CC-mode firmware + NRAS
> attestation.
**Side channels addressed**: PCIe sniffing, host-DMA-into-GPU-memory,
GPU-firmware-from-host introspection.
**Residual risks**: NVIDIA CC-mode CVE record (e.g., GPU-resident
side-channels in shared-memory paths — none publicly disclosed as
of 2026-06-01, but the platform is recent).
**Code touchpoint**: zero in `corona/sign`, `corona/threshold`. One
plumbing change in `corona/gpu/gpu.go::MaybeRegister` to optionally
gate registration on a "confidential-mode-attested" flag passed at
context-init time. **Proposed (not yet implemented)**:
```go
// MaybeRegisterAttested is the CC-mode variant of MaybeRegister.
// It registers the ring only if the supplied attestation token
// passes the consensus-layer's attestation verifier.
//
// On nil token the call falls through to plain MaybeRegister
// (preserves the existing API contract).
func MaybeRegisterAttested(r *ring.Ring, attestation []byte) error {
if attestation == nil {
MaybeRegister(r)
return nil
}
if !attestation_verifier.Verify(attestation) {
return ErrAttestationRejected
}
return RegisterRing(r)
}
```
The `attestation_verifier` package would live OUTSIDE `corona/` —
in the consensus layer at `~/work/lux/threshold/attestation/` or
similar. Corona just consumes the bool.
### §3.3 Intel TDX (alternative Linux path)
**Status**: alternative to §3.1 for Intel platforms. Substantively
equivalent boundary (VM-level confidentiality, attestation via
Intel TDX Provisioning Certification Service).
**Attestation flow**: analogous to §3.1, replacing `SNP_GET_REPORT`
with `TDG.MR.REPORT` and replacing AMD KDS with Intel PCCS / TGS.
**Composite with NVIDIA CC**: TDX + H100 CC is a supported
deployment configuration per NVIDIA's CC documentation.
### §3.4 Apple Secure Enclave (validator share storage on
Apple Silicon)
**Status**: recommended for Apple Silicon validators where the
SEP-bound key is used to seal the at-rest `KeyShare`.
**Boundary**: the SEP is a separate ARM core inside the M-series
SoC with its own bootloader, kernel, and tamper-resistant key
storage. SEP-bound keys are non-extractable and gated by
SecureEnclave biometric / passcode policy.
**Attestation flow** (limited compared to SEV-SNP):
1. Validator process generates a per-validator long-term key
via `Security.framework` `SecKeyCreateRandomKey` with
`kSecAttrTokenIDSecureEnclave`. The private key never leaves
the SEP.
2. The KeyShare bytes are wrapped under an AES-256-GCM key whose
master key is derived from a `SecKeyCreateRandomKey`-backed
ECC P-256 key in the SEP. Decryption requires the SEP to
sign a challenge under the SEP-bound private key.
3. There is NO ATTESTATION REPORT comparable to SEV-SNP/TDX/CC —
Apple does NOT publish a remote-attestation root for the SEP.
Validators using SEP-bound storage cannot prove to the chain
that their share is SEP-sealed; this is a **local-only**
defense-in-depth measure.
**Key isolation**: at-rest share material is sealed under a
non-extractable SEP key. A non-root attacker on the macOS host
cannot extract the share without unlocking the SEP via the
`LAContext.evaluatePolicy` biometric/passcode flow.
**Side channels addressed**: cold-boot, disk-image-exfiltration,
non-root userland processes reading the validator's share file.
**NOT addressed**: a root-equivalent attacker on the macOS host
who calls `Security.framework` after the SEP unlocks (because
SEP unlock policy delegates to the host OS for biometric
evaluation). For full host-isolation on Apple Silicon, run the
validator inside a `virtio-fs`-backed Linux VM under Apple's
`Virtualization.framework` and use the Linux VM's SEV-SNP-style
attestation (not available natively; future work).
**Code touchpoint**: zero in `corona/`. The Apple SEP wrapping
lives in `~/work/lux/threshold/storage/sep-darwin.go` (or
analogous) at the storage layer. Corona's `KeyShare` is loaded
via the storage layer's `LoadShare()` API which returns the
unwrapped `KeyShare` struct.
### §3.5 Intel SGX (deprecated for Corona)
**Status**: NOT recommended. Intel SGX has a long published CVE
record of micro-architectural side-channel attacks (Foreshadow,
ZombieLoad, LVI, ÆPIC Leak). Corona's R-LWE arithmetic spends
significant time on `lattigo`'s NTT and Gaussian sampling — both
data-dependent paths that have been demonstrated vulnerable to
side-channel extraction on SGX in the published literature.
SGX is supported in the *attestation-layer interface* (the
consensus-layer verifier accepts SGX EPID/DCAP reports), but the
**Corona reference deployment guidance** in
`DEPLOYMENT-RUNBOOK.md` explicitly prefers SEV-SNP / TDX / CC-mode
over SGX.
## §4 Composite attestation evidence
The consensus-layer registration endpoint accepts a single composite
attestation evidence bundle for any combination of:
- **CPU side**: SEV-SNP report OR TDX report OR (SGX report,
not recommended).
- **GPU side**: NRAS attestation report (optional; required only
if `gpu.UseAccelerator()` is invoked).
- **Storage side**: SEP-bound key witness (Apple-only;
defense-in-depth, no remote-attestation).
The composite is bound by:
```
binding_nonce = SHA-256( "corona-tee-bind-v1" ||
validator_id ||
group_id ||
era_id ||
challenge_nonce )
```
where `challenge_nonce` is fresh per-registration randomness from
the consensus layer. Each platform-specific report embeds
`binding_nonce` in its `report_data` field. The verifier checks
all reports, confirms binding-nonce consistency, and accepts the
validator on success.
## §5 Key release predicate
The pre-sealed `KeyShare` at the consensus-layer registration
service is encrypted under a key derived from the attestation
transcript. **The key release predicate** is:
```
release_key = HKDF-SHA-256(
salt = "corona-share-release-v1",
ikm = composite_attestation_transcript,
info = validator_id || group_id || era_id,
length = 32,
)
```
The consensus-layer registration service computes this key only on
successful composite-attestation verification. The registration
service then unwraps the validator's `KeyShare` under this key and
delivers the wrapped bytes to the attesting validator.
**This is the policy gate.** Without a passing attestation, no
key release. Without a key release, no Round 1 / Round 2 / Final.
## §6 Honest non-claims
- Corona does NOT today ship `~/work/lux/threshold/attestation/`.
That package is a **proposed** addition for the v0.7.6 / v0.8.0
roadmap; this document specifies its API surface.
- Corona does NOT today ship `MaybeRegisterAttested` in `gpu/`.
Same roadmap.
- Apple SEP integration is **local-only** — no remote attestation
is possible against Apple's platform. Operators relying on
Apple-Silicon validators MUST accept this as a defense-in-depth
measure, not a primary trust anchor.
- The "what does the CC-mode GPU see, formally?" blinding analysis
for §3.2 is an OPEN audit item for v0.8.0.
## §7 Roadmap
| Item | Target | Owner |
|---|---|---|
| `attestation_verifier` package at `~/work/lux/threshold/attestation/` | v0.7.6 | consensus team |
| `MaybeRegisterAttested` in `gpu/gpu.go` | v0.7.6 | corona |
| SEV-SNP CVM bootstrap agent at `~/work/lux/cosi-cvm-agent` | v0.7.6 | infra |
| Reference docker-compose for "validator + SEV-SNP CVM + H100 CC" | v0.8.0 | infra |
| Apple SEP storage layer at `~/work/lux/threshold/storage/sep-darwin.go` | v0.8.0 | apple-silicon track |
| Formal blinding analysis for CC-mode GPU (NTT inputs/outputs) | v0.8.0 | external audit (GATE-4) |
| Reproducible builder image pinned by digest | v0.7.6 | release |
---
**Document metadata**
- Name: `docs/mptc/tee-integration.md`
- Version: v0.1 (initial TEE integration spec)
- Date: 2026-06-02
- Companion to: `DEPLOYMENT-RUNBOOK.md §Operational mitigations item 5`,
`docs/mptc/threat-model.md §9.2`
- Status: proposed; no Corona code change required for §3.1, §3.3, §3.4.
§3.2 adds one optional plumbing function (`MaybeRegisterAttested`)
at the next minor version.
-20
View File
@@ -1,20 +0,0 @@
import { createMDX } from "fumadocs-mdx/next"
/** @type {import('next').NextConfig} */
const config = {
output: 'export',
reactStrictMode: true,
typescript: {
ignoreBuildErrors: true,
},
experimental: {
webpackBuildWorker: true,
},
images: {
unoptimized: true,
},
}
const withMDX = createMDX()
export default withMDX(config)
-36
View File
@@ -1,36 +0,0 @@
{
"name": "@luxfi/corona-docs",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev --port 3001",
"build": "fumadocs-mdx && TURBOPACK=0 next build",
"export": "TURBOPACK=0 next build",
"start": "next start",
"lint": "next lint",
"postinstall": "fumadocs-mdx"
},
"dependencies": {
"fumadocs-core": "^15.8.5",
"fumadocs-mdx": "^12.0.3",
"fumadocs-ui": "^15.8.5",
"lucide-react": "^0.468.0",
"next": "16.0.1",
"react": "19.2.0",
"react-dom": "19.2.0",
"tailwindcss": "^4.1.16",
"zod": "^3.24.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.16",
"@types/node": "^22.10.2",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
"rehype-pretty-code": "^0.14.1",
"shiki": "^1.27.2",
"typescript": "^5.7.2"
},
"packageManager": "pnpm@10.12.4"
}
-4136
View File
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}
View File
-27
View File
@@ -1,27 +0,0 @@
import {
defineConfig,
defineDocs,
} from "fumadocs-mdx/config"
import rehypePrettyCode from "rehype-pretty-code"
export default defineConfig({
mdxOptions: {
rehypePlugins: [
[
rehypePrettyCode,
{
theme: {
dark: "github-dark-dimmed",
light: "github-light",
},
keepBackground: false,
defaultLang: "go",
},
],
],
},
})
export const docs = defineDocs({
dir: "content/docs",
})
-41
View File
@@ -1,41 +0,0 @@
{
"compilerOptions": {
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./*"
]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}