mirror of
https://github.com/luxfi/corona.git
synced 2026-07-27 02:50:34 +00:00
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:
+1
-1
@@ -89,7 +89,7 @@ linters-settings:
|
||||
min-complexity: 15
|
||||
|
||||
goimports:
|
||||
local-prefixes: corona
|
||||
local-prefixes: github.com/luxfi/corona
|
||||
|
||||
gomnd:
|
||||
settings:
|
||||
|
||||
@@ -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 53–54 — 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)
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
```
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
}
|
||||
Generated
-4136
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -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",
|
||||
})
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user