corona: symmetric domain separation — PULSAR-* tags -> CORONA-*

Leftover from Corona's "Pulsar-R" lineage. Pulsar (M-LWE) and Corona
(R-LWE) are independent constructions with separate hardness assumptions,
so their cSHAKE personalisation strings must be distinct.

Changes (Go, non-luxcpp):
  - hash tags: PULSAR-HC-v1 -> CORONA-HC-v1, etc. (HU, TRANSCRIPT, PRF, MAC, PAIRWISE)
  - profile IDs: "Pulsar-SHA3" -> "Corona-SHA3", "Pulsar-BLAKE3" -> "Corona-BLAKE3"
  - context strings: pulsar.dkg2.A.v1 -> corona.dkg2.A.v1, etc.
  - env vars: PULSAR_RESHARE_KAT_PATH -> CORONA_RESHARE_KAT_PATH, etc.
  - struct names: pulsarSHA3 -> coronaSHA3
  - KAT derive roots: sign_e2e_pulsar -> sign_e2e_corona

What's preserved (different scope):
  - luxcpp/crypto/pulsar/* path references in comments (separate repo,
    out of scope; the C++ side will rename in its own commit)
  - Cross-runtime KAT files on disk (will regenerate next CI run)

All 11 packages test green: dkg, dkg2, hash, keyera, networking,
primitives, reshare, sign, threshold, utils, wire.
This commit is contained in:
Hanzo AI
2026-05-13 14:19:13 -07:00
parent 44c1c990c3
commit a2c1d1da31
39 changed files with 331 additions and 331 deletions
+7 -7
View File
@@ -4,7 +4,7 @@
// activation_oracle — emits byte-equal KATs for the post-reshare // activation_oracle — emits byte-equal KATs for the post-reshare
// activation circuit-breaker. Drives reshare.ActivationMessage and // activation circuit-breaker. Drives reshare.ActivationMessage and
// reshare.ReshareTranscript through their canonical SignableBytes / // reshare.ReshareTranscript through their canonical SignableBytes /
// Hash routines under the production Pulsar-SHA3 suite. // Hash routines under the production Corona-SHA3 suite.
// //
// Wire format (per entry): // Wire format (per entry):
// //
@@ -30,8 +30,8 @@
// Output: <luxcpp/crypto>/pulsar/test/kat/activation_kat.json // Output: <luxcpp/crypto>/pulsar/test/kat/activation_kat.json
// //
// Algorithm references: // Algorithm references:
// - pulsar/reshare/activation.go (canonical Go) // - corona/reshare/activation.go (canonical Go)
// - luxcpp/crypto/pulsar/reshare/activation.hpp (C++ port) // - luxcpp/crypto/corona/reshare/activation.hpp (C++ port)
package main package main
import ( import (
@@ -78,7 +78,7 @@ type Output struct {
// ComplaintHashes so the KAT is reproducible without any RNG state. // ComplaintHashes so the KAT is reproducible without any RNG state.
func counterDigest(label string, idx int) [32]byte { func counterDigest(label string, idx int) [32]byte {
h := sha256.New() h := sha256.New()
_, _ = io.WriteString(h, "pulsar.activation.kat.v1:") _, _ = io.WriteString(h, "corona.activation.kat.v1:")
_, _ = io.WriteString(h, label) _, _ = io.WriteString(h, label)
var b [8]byte var b [8]byte
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
@@ -156,7 +156,7 @@ func entry(
func main() { func main() {
out := Output{ out := Output{
Suite: "Pulsar-SHA3", Suite: "Corona-SHA3",
Version: "v1", Version: "v1",
} }
out.Entries = append(out.Entries, out.Entries = append(out.Entries,
@@ -173,12 +173,12 @@ func main() {
) )
// Default output: canonical luxcpp KAT directory; allow override via // Default output: canonical luxcpp KAT directory; allow override via
// PULSAR_ACTIVATION_KAT_PATH env or a positional arg. // CORONA_ACTIVATION_KAT_PATH env or a positional arg.
outPath := filepath.Join( outPath := filepath.Join(
os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar", os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar",
"test", "kat", "activation_kat.json", "test", "kat", "activation_kat.json",
) )
if env := os.Getenv("PULSAR_ACTIVATION_KAT_PATH"); env != "" { if env := os.Getenv("CORONA_ACTIVATION_KAT_PATH"); env != "" {
outPath = env outPath = env
} }
if len(os.Args) >= 2 { if len(os.Args) >= 2 {
+3 -3
View File
@@ -43,11 +43,11 @@ import (
// legacyBLAKE3Suite is the suite this oracle uses for every primitives.* // legacyBLAKE3Suite is the suite this oracle uses for every primitives.*
// call. The JSON files produced here are the BLAKE3 KAT transcripts // call. The JSON files produced here are the BLAKE3 KAT transcripts
// downstream ports (C++, GPU) byte-match against. They were pinned before // downstream ports (C++, GPU) byte-match against. They were pinned before
// the Pulsar-SHA3 default was wired into primitives/hash.go, so emission // the Corona-SHA3 default was wired into primitives/hash.go, so emission
// must remain on the legacy BLAKE3 suite to keep the existing transcripts // must remain on the legacy BLAKE3 suite to keep the existing transcripts
// byte-stable. A separate Pulsar-SHA3 KAT oracle lands as follow-up // byte-stable. A separate Corona-SHA3 KAT oracle lands as follow-up
// (see CHANGELOG.md). // (see CHANGELOG.md).
var legacyBLAKE3Suite = pulsarhash.NewPulsarBLAKE3() var legacyBLAKE3Suite = pulsarhash.NewCoronaBLAKE3()
// MasterSeed is the deterministic root of all KAT generation. Changing it // MasterSeed is the deterministic root of all KAT generation. Changing it
// invalidates every downstream port's expected outputs, so it stays fixed // invalidates every downstream port's expected outputs, so it stays fixed
+7 -7
View File
@@ -1,14 +1,14 @@
// Package main is the Pulsar cross-runtime KAT oracle. // Package main is the Corona cross-runtime KAT oracle.
// //
// Emits a single JSON manifest at <out>/cross_runtime_kat.json that ties // Emits a single JSON manifest at <out>/cross_runtime_kat.json that ties
// together the three canonical Pulsar KATs (sign, reshare, dkg2) with // together the three canonical Corona KATs (sign, reshare, dkg2) with
// the SHA-256 of each individual KAT file. The C++ side // the SHA-256 of each individual KAT file. The C++ side
// (luxcpp/crypto/pulsar/test/cross_runtime_test.cpp) replays each KAT // (luxcpp/crypto/corona/test/cross_runtime_test.cpp) replays each KAT
// in C++ and verifies byte-equality; running this oracle first then // in C++ and verifies byte-equality; running this oracle first then
// the C++ test is the "Go → C++" direction of the gate. // the C++ test is the "Go → C++" direction of the gate.
// //
// Reverse direction (C++ → Go) is handled by: // Reverse direction (C++ → Go) is handled by:
// - luxcpp/crypto/pulsar/cmd/cross_runtime_oracle/ (C++ writer) // - luxcpp/crypto/corona/cmd/cross_runtime_oracle/ (C++ writer)
// - lux/pulsar/cmd/cross_runtime_verify/ (Go reader) // - lux/pulsar/cmd/cross_runtime_verify/ (Go reader)
// //
// Determinism is required. Two runs with the same MasterSeed produce // Determinism is required. Two runs with the same MasterSeed produce
@@ -77,8 +77,8 @@ func main() {
// hardcoded locations in luxcpp/crypto. For the cross-runtime gate // hardcoded locations in luxcpp/crypto. For the cross-runtime gate
// we hash the canonical paths. // we hash the canonical paths.
signPath := filepath.Join(*out, "sign_kat.json") signPath := filepath.Join(*out, "sign_kat.json")
resharePath := "/Users/z/work/luxcpp/crypto/pulsar/test/kat/reshare_kat.json" resharePath := "/Users/z/work/luxcpp/crypto/corona/test/kat/reshare_kat.json"
dkg2Path := "/Users/z/work/luxcpp/crypto/pulsar/dkg2/test/kat/dkg2_kat.json" dkg2Path := "/Users/z/work/luxcpp/crypto/corona/dkg2/test/kat/dkg2_kat.json"
files := []struct { files := []struct {
name string name string
@@ -90,7 +90,7 @@ func main() {
} }
m := manifest{ m := manifest{
Description: "Pulsar cross-runtime KAT manifest. SHA-256 of each canonical Go-emitted KAT JSON. The C++ cross_runtime_test consumes the same paths and asserts byte-equality at every entry; this manifest pins the Go-side bytes so any drift produces a SHA-256 mismatch caught by the gate.", Description: "Corona cross-runtime KAT manifest. SHA-256 of each canonical Go-emitted KAT JSON. The C++ cross_runtime_test consumes the same paths and asserts byte-equality at every entry; this manifest pins the Go-side bytes so any drift produces a SHA-256 mismatch caught by the gate.",
Direction: "go-to-cpp", Direction: "go-to-cpp",
} }
for _, f := range files { for _, f := range files {
+4 -4
View File
@@ -1,8 +1,8 @@
// Package main is the Go-side verifier for the Pulsar cross-runtime // Package main is the Go-side verifier for the Corona cross-runtime
// KAT gate (C++ → Go direction). // KAT gate (C++ → Go direction).
// //
// Reads a C++-emitted manifest produced by // Reads a C++-emitted manifest produced by
// luxcpp/crypto/pulsar/cmd/cross_runtime_oracle and confirms that each // luxcpp/crypto/corona/cmd/cross_runtime_oracle and confirms that each
// SHA-256 digest in the manifest matches the bytes Go observes for the // SHA-256 digest in the manifest matches the bytes Go observes for the
// same file path. Mismatch → non-zero exit code. // same file path. Mismatch → non-zero exit code.
// //
@@ -11,8 +11,8 @@
// cross_runtime_verify --manifest <path/to/cross_runtime_kat_cpp.json> // cross_runtime_verify --manifest <path/to/cross_runtime_kat_cpp.json>
// //
// This is the reverse leg of the cross-runtime gate. The forward leg // This is the reverse leg of the cross-runtime gate. The forward leg
// (Go → C++) lives in luxcpp/crypto/pulsar/test/cross_runtime_test.cpp; // (Go → C++) lives in luxcpp/crypto/corona/test/cross_runtime_test.cpp;
// CTest target `pulsar_cross_runtime_kat` exercises both directions. // CTest target `corona_cross_runtime_kat` exercises both directions.
package main package main
import ( import (
+12 -12
View File
@@ -10,8 +10,8 @@
// 2. From that master seed, derives one 32-byte sub-seed per party // 2. From that master seed, derives one 32-byte sub-seed per party
// (BLAKE3(master || "party" || BE32(i))). // (BLAKE3(master || "party" || BE32(i))).
// 3. Constructs n DKGSessions (party 0..n-1) — they all share the same // 3. Constructs n DKGSessions (party 0..n-1) — they all share the same
// deterministic A and B matrices (derived from b"pulsar.dkg2.A.v1" / // deterministic A and B matrices (derived from b"corona.dkg2.A.v1" /
// b"pulsar.dkg2.B.v1" via BLAKE3-XOF). // b"corona.dkg2.B.v1" via BLAKE3-XOF).
// 4. Each party calls Round1WithSeed(party_seed[i]) to produce its // 4. Each party calls Round1WithSeed(party_seed[i]) to produce its
// Commits, Shares (per recipient), and Blinds (per recipient). // Commits, Shares (per recipient), and Blinds (per recipient).
// 5. Each party calls Round2 with the assembled shares/blinds/commits. // 5. Each party calls Round2 with the assembled shares/blinds/commits.
@@ -32,7 +32,7 @@
// one entry must be identical. The KAT records all n of them so the C++ // one entry must be identical. The KAT records all n of them so the C++
// port can prove that property too. // port can prove that property too.
// //
// Output: <luxcpp/crypto>/pulsar/dkg2/test/kat/dkg2_kat.json (4 entries: // Output: <luxcpp/crypto>/corona/dkg2/test/kat/dkg2_kat.json (4 entries:
// 2-of-3, 3-of-5, 5-of-7, 7-of-11). // 2-of-3, 3-of-5, 5-of-7, 7-of-11).
package main package main
@@ -159,8 +159,8 @@ func runEntry(t, n int) Entry {
sessions := make([]*dkg2.DKGSession, n) sessions := make([]*dkg2.DKGSession, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
// Use the legacy BLAKE3 hash suite for the canonical KAT — keeps // Use the legacy BLAKE3 hash suite for the canonical KAT — keeps
// every byte in dkg2_kat.json byte-stable across the Pulsar-SHA3 // every byte in dkg2_kat.json byte-stable across the Corona-SHA3
// cutover. Production-track callers use hash.Default() (Pulsar-SHA3). // cutover. Production-track callers use hash.Default() (Corona-SHA3).
s, err := dkg2.NewDKGSession(params, i, n, t, nil) s, err := dkg2.NewDKGSession(params, i, n, t, nil)
if err != nil { if err != nil {
panic(fmt.Errorf("NewDKGSession(%d, %d, %d): %w", i, n, t, err)) panic(fmt.Errorf("NewDKGSession(%d, %d, %d): %w", i, n, t, err))
@@ -272,8 +272,8 @@ func main() {
out := OracleOut{ out := OracleOut{
Description: "Pedersen-style DKG over R = Z_q[X]/(X^256+1), Q=0x1000000004A01. " + Description: "Pedersen-style DKG over R = Z_q[X]/(X^256+1), Q=0x1000000004A01. " +
"C_k = A·NTT(c_k) + B·NTT(r_k) — hiding under MLWE on B, binding under " + "C_k = A·NTT(c_k) + B·NTT(r_k) — hiding under MLWE on B, binding under " +
"MSIS on [A|B]. A derived from BLAKE3(\"pulsar.dkg2.A.v1\"); B from " + "MSIS on [A|B]. A derived from BLAKE3(\"corona.dkg2.A.v1\"); B from " +
"BLAKE3(\"pulsar.dkg2.B.v1\"). Each entry runs the full t-of-n protocol " + "BLAKE3(\"corona.dkg2.B.v1\"). Each entry runs the full t-of-n protocol " +
"with deterministic per-party Round1WithSeed inputs derived from " + "with deterministic per-party Round1WithSeed inputs derived from " +
"MasterSeed=0xC0FFEEF00DFACE. Wire format: structs.{Vector,Matrix}[ring.Poly]" + "MasterSeed=0xC0FFEEF00DFACE. Wire format: structs.{Vector,Matrix}[ring.Poly]" +
".WriteTo (LE u64). Hashes are SHA-256 of those wire bytes. CommitDigest " + ".WriteTo (LE u64). Hashes are SHA-256 of those wire bytes. CommitDigest " +
@@ -284,8 +284,8 @@ func main() {
M: sign.M, M: sign.M,
Nvec: sign.N, Nvec: sign.N,
Xi: sign.Xi, Xi: sign.Xi,
TagAHex: hex.EncodeToString([]byte("pulsar.dkg2.A.v1")), TagAHex: hex.EncodeToString([]byte("corona.dkg2.A.v1")),
TagBHex: hex.EncodeToString([]byte("pulsar.dkg2.B.v1")), TagBHex: hex.EncodeToString([]byte("corona.dkg2.B.v1")),
} }
for _, c := range cases { for _, c := range cases {
@@ -295,10 +295,10 @@ func main() {
// Default output path: canonical luxcpp KAT directory. Pass an // Default output path: canonical luxcpp KAT directory. Pass an
// argument to override. When the oracle is invoked via `go run` from // argument to override. When the oracle is invoked via `go run` from
// the pulsar repo root, the relative form ../../../luxcpp/... resolves // the corona repo root, the relative form ../../../luxcpp/... resolves
// correctly; otherwise pass an absolute path. // correctly; otherwise pass an absolute path.
outPath := "../../../luxcpp/crypto/pulsar/dkg2/test/kat/dkg2_kat.json" outPath := "../../../luxcpp/crypto/corona/dkg2/test/kat/dkg2_kat.json"
if env := os.Getenv("PULSAR_DKG2_KAT_PATH"); env != "" { if env := os.Getenv("CORONA_DKG2_KAT_PATH"); env != "" {
outPath = env outPath = env
} }
if len(os.Args) >= 2 { if len(os.Args) >= 2 {
+10 -10
View File
@@ -3,7 +3,7 @@
// //
// reshare_oracle — emits byte-equal KATs for the proactive secret-resharing // reshare_oracle — emits byte-equal KATs for the proactive secret-resharing
// protocol implemented in github.com/luxfi/corona/reshare. The C++ port at // protocol implemented in github.com/luxfi/corona/reshare. The C++ port at
// ~/work/luxcpp/crypto/pulsar/reshare/ replays each entry's seeds and must // ~/work/luxcpp/crypto/corona/reshare/ replays each entry's seeds and must
// produce share bytes whose SHA-256 commitment matches the entry's // produce share bytes whose SHA-256 commitment matches the entry's
// new_share_sha256_hex field. // new_share_sha256_hex field.
// //
@@ -35,16 +35,16 @@
// old_set, new_set, t_old, t_new) and must reproduce // old_set, new_set, t_old, t_new) and must reproduce
// (old_shares_hex, new_shares_hex) exactly. // (old_shares_hex, new_shares_hex) exactly.
// //
// Output: ~/work/luxcpp/crypto/pulsar/test/kat/reshare_kat.json // Output: ~/work/luxcpp/crypto/corona/test/kat/reshare_kat.json
// //
// Algorithm references: // Algorithm references:
// - pulsar/reshare/reshare.go (canonical Go) // - corona/reshare/reshare.go (canonical Go)
// - pulsar/papers/lp-073-pulsar/sections/06-resharing.tex (paper) // - corona/papers/lp-073-pulsar/sections/06-resharing.tex (paper)
// //
// Note on RNG choice: production reshare.Reshare consumes from // Note on RNG choice: production reshare.Reshare consumes from
// crypto/rand.Reader by default. For KAT determinism we substitute a // crypto/rand.Reader by default. For KAT determinism we substitute a
// SHA-256-counter PRNG (counterRand below). The same counterRand is // SHA-256-counter PRNG (counterRand below). The same counterRand is
// implemented in C++ (luxcpp/crypto/pulsar/reshare/counter_rand.cpp). // implemented in C++ (luxcpp/crypto/corona/reshare/counter_rand.cpp).
package main package main
import ( import (
@@ -72,7 +72,7 @@ const (
// QByteLen = len(big.Int.Bytes(Q)). // QByteLen = len(big.Int.Bytes(Q)).
QByteLen = 7 QByteLen = 7
// PolyCount is sign.Nvec = 7 (the secret-vector dimension in the // PolyCount is sign.Nvec = 7 (the secret-vector dimension in the
// production Pulsar path). Smaller values would also work; 7 lets // production Corona path). Smaller values would also work; 7 lets
// the KAT exercise the same shape as production. // the KAT exercise the same shape as production.
PolyCount = 7 PolyCount = 7
) )
@@ -212,14 +212,14 @@ func main() {
} }
out := OracleOut{ out := OracleOut{
Description: "Pulsar proactive resharing KAT. " + Description: "Corona proactive resharing KAT. " +
"Each entry deterministically reconstructs (1) Shamir shares of " + "Each entry deterministically reconstructs (1) Shamir shares of " +
"a planted secret s for the old committee and (2) reshared " + "a planted secret s for the old committee and (2) reshared " +
"shares for the new committee, using counterRand SHA-256 streams " + "shares for the new committee, using counterRand SHA-256 streams " +
"seeded by old_shamir_seed_hex / reshare_rng_seed_hex. The new " + "seeded by old_shamir_seed_hex / reshare_rng_seed_hex. The new " +
"shares interpolate (any t_new of them) to the SAME s. The " + "shares interpolate (any t_new of them) to the SAME s. The " +
"public key b in production is computed from s and is therefore " + "public key b in production is computed from s and is therefore " +
"unchanged across resharing — see pulsar/papers/lp-073-pulsar/" + "unchanged across resharing — see corona/papers/lp-073-pulsar/" +
"sections/06-resharing.tex.", "sections/06-resharing.tex.",
Modulus: Q, Modulus: Q,
NPoly: N, NPoly: N,
@@ -344,12 +344,12 @@ func main() {
} }
// Write the file. Default output: canonical luxcpp KAT directory; // Write the file. Default output: canonical luxcpp KAT directory;
// allow override via PULSAR_RESHARE_KAT_PATH or a positional arg. // allow override via CORONA_RESHARE_KAT_PATH or a positional arg.
outPath := filepath.Join( outPath := filepath.Join(
os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar", os.Getenv("HOME"), "work", "luxcpp", "crypto", "pulsar",
"test", "kat", "reshare_kat.json", "test", "kat", "reshare_kat.json",
) )
if env := os.Getenv("PULSAR_RESHARE_KAT_PATH"); env != "" { if env := os.Getenv("CORONA_RESHARE_KAT_PATH"); env != "" {
outPath = env outPath = env
} }
if len(os.Args) >= 2 { if len(os.Args) >= 2 {
+7 -7
View File
@@ -1,13 +1,13 @@
// Package main is the Pulsar sign+verify KAT oracle. // Package main is the Corona sign+verify KAT oracle.
// //
// Given a fixed master seed, it emits a deterministic JSON file of // Given a fixed master seed, it emits a deterministic JSON file of
// known-answer test vectors covering the full LP-073 Pulsar threshold // known-answer test vectors covering the full LP-073 Corona threshold
// signature pipeline (Gen + SignRound1 + SignRound2{Preprocess,} + // signature pipeline (Gen + SignRound1 + SignRound2{Preprocess,} +
// SignFinalize + Verify) for the canonical (t, n) configurations // SignFinalize + Verify) for the canonical (t, n) configurations
// 2-of-3, 3-of-5, 5-of-7, 7-of-11. The C++ port at // 2-of-3, 3-of-5, 5-of-7, 7-of-11. The C++ port at
// luxcpp/crypto/pulsar/cpp/sign/ replays these entries byte-equal. // luxcpp/crypto/corona/cpp/sign/ replays these entries byte-equal.
// //
// Pulsar's sign/sign.go is byte-identical to corona/sign/sign.go (the // Corona's sign/sign.go is byte-identical to corona/sign/sign.go (the
// only diff is the import path), so the JSON shape mirrors the existing // only diff is the import path), so the JSON shape mirrors the existing
// corona sign_verify_e2e KAT. // corona sign_verify_e2e KAT.
// //
@@ -127,7 +127,7 @@ type signEntry struct {
} }
func emitSignVerify(outDir string) error { func emitSignVerify(outDir string) error {
root := derive("sign_e2e_pulsar") root := derive("sign_e2e_corona")
cfgs := []struct{ t, n int }{ cfgs := []struct{ t, n int }{
{2, 3}, {3, 5}, {5, 7}, {7, 11}, {2, 3}, {3, 5}, {5, 7}, {7, 11},
} }
@@ -139,10 +139,10 @@ func emitSignVerify(outDir string) error {
Description string `json:"description"` Description string `json:"description"`
Entries []signEntry `json:"entries"` Entries []signEntry `json:"entries"`
}{ }{
Description: "Full Pulsar Sign+Verify round-trip (LP-073 Q-witness). " + Description: "Full Corona Sign+Verify round-trip (LP-073 Q-witness). " +
"For each (t,n,msg,seed): Gen → SignRound1 (all parties) → " + "For each (t,n,msg,seed): Gen → SignRound1 (all parties) → " +
"SignRound2Preprocess+SignRound2 (all parties) → SignFinalize → " + "SignRound2Preprocess+SignRound2 (all parties) → SignFinalize → " +
"Verify. Pulsar's sign module is byte-identical to the original construction at " + "Verify. Corona's sign module is byte-identical to the original construction at " +
"the Go source level (only the import path differs). The current " + "the Go source level (only the import path differs). The current " +
"KAT signs with K=Threshold=n; the t field documents the " + "KAT signs with K=Threshold=n; the t field documents the " +
"threshold-aware variant for downstream use. SHA-256 hashes are " + "threshold-aware variant for downstream use. SHA-256 hashes are " +
+4 -4
View File
@@ -139,11 +139,11 @@ type Complaint struct {
// //
// Format: // Format:
// //
// "pulsar.dkg2.complaint.v1" || transcript || sender_id_be32 || // "corona.dkg2.complaint.v1" || transcript || sender_id_be32 ||
// complainer_id_be32 || reason_u8 || evidence_len_be32 || evidence // complainer_id_be32 || reason_u8 || evidence_len_be32 || evidence
func (c *Complaint) Bytes() []byte { func (c *Complaint) Bytes() []byte {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("pulsar.dkg2.complaint.v1") buf.WriteString("corona.dkg2.complaint.v1")
buf.Write(c.TranscriptHash[:]) buf.Write(c.TranscriptHash[:])
var b4 [4]byte var b4 [4]byte
binary.BigEndian.PutUint32(b4[:], uint32(c.SenderID)) binary.BigEndian.PutUint32(b4[:], uint32(c.SenderID))
@@ -186,10 +186,10 @@ func (c *Complaint) Verify() error {
// commit to the SET of complaints in the Round 2 transcript and the // commit to the SET of complaints in the Round 2 transcript and the
// activation message. // activation message.
// //
// Pass nil for the production default (Pulsar-SHA3). // Pass nil for the production default (Corona-SHA3).
func ComplaintHash(suite hash.HashSuite, c *Complaint) [32]byte { func ComplaintHash(suite hash.HashSuite, c *Complaint) [32]byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
return s.TranscriptHash([]byte("pulsar.dkg2.complaint-hash.v1"), c.Bytes(), c.Signature) return s.TranscriptHash([]byte("corona.dkg2.complaint-hash.v1"), c.Bytes(), c.Signature)
} }
// DisqualificationThreshold returns the minimum number of distinct, // DisqualificationThreshold returns the minimum number of distinct,
+23 -23
View File
@@ -2,10 +2,10 @@
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package dkg2 implements a Pedersen-style verifiable-secret-sharing-based // Package dkg2 implements a Pedersen-style verifiable-secret-sharing-based
// distributed key generation over the Pulsar polynomial ring // distributed key generation over the Corona polynomial ring
// R_q = Z_q[X]/(X^256 + 1). // R_q = Z_q[X]/(X^256 + 1).
// //
// dkg2 is the production parallel-track keygen for Pulsar. It replaces the // dkg2 is the production parallel-track keygen for Corona. It replaces the
// pseudoinverse-recoverable Feldman commit C_k = A · NTT(c_k) of the // pseudoinverse-recoverable Feldman commit C_k = A · NTT(c_k) of the
// upstream Corona DKG with a Pedersen commit // upstream Corona DKG with a Pedersen commit
// //
@@ -16,7 +16,7 @@
// decisional MLWE on B; binding holds under MSIS on the wide concatenation // decisional MLWE on B; binding holds under MSIS on the wide concatenation
// [A | B]. Formal statements live in // [A | B]. Formal statements live in
// papers/lp-073-pulsar/sections/07-pedersen-dkg.tex; Lean theorem references // papers/lp-073-pulsar/sections/07-pedersen-dkg.tex; Lean theorem references
// are in proofs/lean/Crypto/Pulsar/dkg2.lean. // are in proofs/lean/Crypto/Corona/dkg2.lean.
// //
// # Round structure // # Round structure
// //
@@ -45,14 +45,14 @@
// # Hash suite // # Hash suite
// //
// dkg2 routes every cohort-bound digest through the canonical // dkg2 routes every cohort-bound digest through the canonical
// hash.HashSuite (Pulsar-SHA3 in production; Pulsar-BLAKE3 retained for // hash.HashSuite (Corona-SHA3 in production; Corona-BLAKE3 retained for
// byte-equality with pre-cutover KATs). NewDKGSession accepts a HashSuite; // byte-equality with pre-cutover KATs). NewDKGSession accepts a HashSuite;
// nil resolves to the production default. Matrix derivation (A, B) uses a // nil resolves to the production default. Matrix derivation (A, B) uses a
// dedicated, version-pinned BLAKE3 path to keep KAT bytes stable across // dedicated, version-pinned BLAKE3 path to keep KAT bytes stable across
// the SHA3 cutover — the matrix derivation is structural, not transcript- // the SHA3 cutover — the matrix derivation is structural, not transcript-
// bound, so it has its own version tag (pulsar.dkg2.A.v1 / .B.v1). The // bound, so it has its own version tag (corona.dkg2.A.v1 / .B.v1). The
// Round 1.5 commit digest, by contrast, is HashSuite-bound and uses the // Round 1.5 commit digest, by contrast, is HashSuite-bound and uses the
// PULSAR-TRANSCRIPT-v1 customization of the active suite. // CORONA-TRANSCRIPT-v1 customization of the active suite.
// //
// # Identifiable abort // # Identifiable abort
// //
@@ -64,12 +64,12 @@
// //
// # File-level invariants // # File-level invariants
// //
// - All ring arithmetic uses sign.Q (the Pulsar 48-bit prime). // - All ring arithmetic uses sign.Q (the Corona 48-bit prime).
// - Sampler parameters reuse sign.SigmaE / sign.BoundE for both c_{i,k} // - Sampler parameters reuse sign.SigmaE / sign.BoundE for both c_{i,k}
// and r_{i,k}, mirroring the Pulsar secret distribution. // and r_{i,k}, mirroring the Corona secret distribution.
// - A is derived from the 16-byte tag b"pulsar.dkg2.A.v1" via BLAKE3-XOF // - A is derived from the 16-byte tag b"corona.dkg2.A.v1" via BLAKE3-XOF
// (KAT-pinned). // (KAT-pinned).
// - B is derived from the 16-byte tag b"pulsar.dkg2.B.v1" via BLAKE3-XOF // - B is derived from the 16-byte tag b"corona.dkg2.B.v1" via BLAKE3-XOF
// (KAT-pinned). // (KAT-pinned).
// - Commits are stored in NTT-Montgomery form (matches A, B). // - Commits are stored in NTT-Montgomery form (matches A, B).
// - Shares are stored in standard coefficient form (NTT=false, mont=false). // - Shares are stored in standard coefficient form (NTT=false, mont=false).
@@ -80,7 +80,7 @@
// //
// Round1WithSeed pins every byte of the protocol output for byte-equal C++ // Round1WithSeed pins every byte of the protocol output for byte-equal C++
// porting. See cmd/dkg2_oracle for the canonical generator and // porting. See cmd/dkg2_oracle for the canonical generator and
// luxcpp/crypto/pulsar/dkg2/test/kat/dkg2_kat.json for the 4 reference // luxcpp/crypto/corona/dkg2/test/kat/dkg2_kat.json for the 4 reference
// entries (2-of-3, 3-of-5, 5-of-7, 7-of-11). // entries (2-of-3, 3-of-5, 5-of-7, 7-of-11).
package dkg2 package dkg2
@@ -113,18 +113,18 @@ import (
// breaking compatibility. // breaking compatibility.
// //
// Matrix derivation is BLAKE3 directly, NOT the active HashSuite. This keeps // Matrix derivation is BLAKE3 directly, NOT the active HashSuite. This keeps
// public-matrix bytes stable across the Pulsar-SHA3 cutover (the matrices // public-matrix bytes stable across the Corona-SHA3 cutover (the matrices
// are structural, not transcript-bound; their version tag covers any future // are structural, not transcript-bound; their version tag covers any future
// rotation). // rotation).
var ( var (
tagA = []byte("pulsar.dkg2.A.v1") tagA = []byte("corona.dkg2.A.v1")
tagB = []byte("pulsar.dkg2.B.v1") tagB = []byte("corona.dkg2.B.v1")
) )
// Customization tag bound into the Round 1.5 commit-digest under the active // Customization tag bound into the Round 1.5 commit-digest under the active
// HashSuite. The suite ID is bound into the digest input as well so two // HashSuite. The suite ID is bound into the digest input as well so two
// suites can never produce a colliding digest for the same commit vector. // suites can never produce a colliding digest for the same commit vector.
const tagCommitDigest = "PULSAR-DKG2-COMMIT-DIGEST-v1" const tagCommitDigest = "CORONA-DKG2-COMMIT-DIGEST-v1"
var ( var (
ErrInvalidThreshold = errors.New("dkg2: threshold must be > 0 and < total parties") ErrInvalidThreshold = errors.New("dkg2: threshold must be > 0 and < total parties")
@@ -150,7 +150,7 @@ type Params struct {
// //
// Mirrors dkg.NewParams: the RXi ring is the post-rounding modulus // Mirrors dkg.NewParams: the RXi ring is the post-rounding modulus
// (Xi = 30 bits, sign.QXi = 0x40000 = 2^18) — not prime, but that's the // (Xi = 30 bits, sign.QXi = 0x40000 = 2^18) — not prime, but that's the
// canonical Pulsar layout used by sign.Gen and the Round2 b_ped output. // canonical Corona layout used by sign.Gen and the Round2 b_ped output.
// ring.NewRing returns a non-prime-modulus error here that we deliberately // ring.NewRing returns a non-prime-modulus error here that we deliberately
// ignore (matches dkg/dkg.go:53), and the constructor remains usable // ignore (matches dkg/dkg.go:53), and the constructor remains usable
// because RoundVector only needs the ring as a coefficient container. // because RoundVector only needs the ring as a coefficient container.
@@ -240,7 +240,7 @@ func (r *Round1Output) SerializeCommits() ([]byte, error) {
// CommitDigest returns the Round 1.5 cross-party-consistency digest under // CommitDigest returns the Round 1.5 cross-party-consistency digest under
// the supplied HashSuite. Passing nil resolves to the production default // the supplied HashSuite. Passing nil resolves to the production default
// (Pulsar-SHA3); pass hash.NewPulsarBLAKE3() for byte-equal replay against // (Corona-SHA3); pass hash.NewCoronaBLAKE3() for byte-equal replay against
// the canonical KATs. // the canonical KATs.
// //
// Format (suite-agnostic): // Format (suite-agnostic):
@@ -260,7 +260,7 @@ func (r *Round1Output) CommitDigest(suite hash.HashSuite) ([32]byte, error) {
} }
// CommitDigestBLAKE3 returns the legacy BLAKE3 commit digest used by the // CommitDigestBLAKE3 returns the legacy BLAKE3 commit digest used by the
// pre-SHA3-cutover KAT (pulsar/dkg2 oracle, luxcpp dkg2_kat.json). Format: // pre-SHA3-cutover KAT (corona/dkg2 oracle, luxcpp dkg2_kat.json). Format:
// //
// BLAKE3(serialize(Commits[0]) || ... || serialize(Commits[t-1]))[:32] // BLAKE3(serialize(Commits[0]) || ... || serialize(Commits[t-1]))[:32]
// //
@@ -301,9 +301,9 @@ type DKGSession struct {
// //
// suite parameterizes the cohort-bound hash routines (Round 1.5 commit // suite parameterizes the cohort-bound hash routines (Round 1.5 commit
// digest, complaint transcripts). nil resolves to the production default // digest, complaint transcripts). nil resolves to the production default
// (Pulsar-SHA3). Public-matrix derivation is deliberately HashSuite- // (Corona-SHA3). Public-matrix derivation is deliberately HashSuite-
// independent — it uses a dedicated BLAKE3 path so KAT bytes stay stable // independent — it uses a dedicated BLAKE3 path so KAT bytes stay stable
// across the Pulsar-SHA3 cutover (see package documentation). // across the Corona-SHA3 cutover (see package documentation).
// //
// Mirrors dkg.NewDKGSession exactly except that the matrices A, B are // Mirrors dkg.NewDKGSession exactly except that the matrices A, B are
// derived from public domain-separated tags via BLAKE3, removing the // derived from public domain-separated tags via BLAKE3, removing the
@@ -603,11 +603,11 @@ func uint64SliceToBytes(s []uint64) []byte {
// //
// Returns (s_j, u_j, b_ped) on success. // Returns (s_j, u_j, b_ped) on success.
// //
// s_j = Σ_i share_{i→j} (Pulsar secret share) // s_j = Σ_i share_{i→j} (Corona secret share)
// u_j = Σ_i blind_{i→j} (private; discarded by Pulsar Sign callers) // u_j = Σ_i blind_{i→j} (private; discarded by Corona Sign callers)
// b_ped = Σ_i C_{i,0} (rounded to Xi, Pedersen-shaped pk) // b_ped = Σ_i C_{i,0} (rounded to Xi, Pedersen-shaped pk)
// //
// b_ped has shape Round_Xi(A·s + B·t_master). Pulsar Sign verification // b_ped has shape Round_Xi(A·s + B·t_master). Corona Sign verification
// running in 2-secret mode (path (b)) takes (A, B, b_ped) jointly; see // running in 2-secret mode (path (b)) takes (A, B, b_ped) jointly; see
// papers/lp-073-pulsar/sections/07-pedersen-dkg.tex §Mapping for the // papers/lp-073-pulsar/sections/07-pedersen-dkg.tex §Mapping for the
// integration recipe. // integration recipe.
+19 -19
View File
@@ -87,8 +87,8 @@ func TestDKG2_DeterministicMatrices(t *testing.T) {
// the hash exchanged in Round 1.5 to defeat the cross-party-inconsistency // the hash exchanged in Round 1.5 to defeat the cross-party-inconsistency
// attack (Finding 2 of RED-DKG-REVIEW.md). // attack (Finding 2 of RED-DKG-REVIEW.md).
// //
// Runs against both supported hash suites (Pulsar-SHA3 default and // Runs against both supported hash suites (Corona-SHA3 default and
// Pulsar-BLAKE3 legacy) so neither the signature surface nor the byte // Corona-BLAKE3 legacy) so neither the signature surface nor the byte
// stability silently regresses across the cutover. // stability silently regresses across the cutover.
func TestDKG2_CommitDigestConsistency(t *testing.T) { func TestDKG2_CommitDigestConsistency(t *testing.T) {
params, err := NewParams() params, err := NewParams()
@@ -101,8 +101,8 @@ func TestDKG2_CommitDigestConsistency(t *testing.T) {
s hash.HashSuite s hash.HashSuite
}{ }{
{"default", nil}, {"default", nil},
{"sha3", hash.NewPulsarSHA3()}, {"sha3", hash.NewCoronaSHA3()},
{"blake3", hash.NewPulsarBLAKE3()}, {"blake3", hash.NewCoronaBLAKE3()},
} }
for _, sc := range suites { for _, sc := range suites {
@@ -160,7 +160,7 @@ func TestDKG2_HashSuiteCrossProfile(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("NewParams: %v", err) t.Fatalf("NewParams: %v", err)
} }
sess, err := NewDKGSession(params, 0, 3, 2, hash.NewPulsarSHA3()) sess, err := NewDKGSession(params, 0, 3, 2, hash.NewCoronaSHA3())
if err != nil { if err != nil {
t.Fatalf("NewDKGSession: %v", err) t.Fatalf("NewDKGSession: %v", err)
} }
@@ -172,11 +172,11 @@ func TestDKG2_HashSuiteCrossProfile(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Round1: %v", err) t.Fatalf("Round1: %v", err)
} }
dSHA3, err := out.CommitDigest(hash.NewPulsarSHA3()) dSHA3, err := out.CommitDigest(hash.NewCoronaSHA3())
if err != nil { if err != nil {
t.Fatalf("CommitDigest sha3: %v", err) t.Fatalf("CommitDigest sha3: %v", err)
} }
dBLAKE3, err := out.CommitDigest(hash.NewPulsarBLAKE3()) dBLAKE3, err := out.CommitDigest(hash.NewCoronaBLAKE3())
if err != nil { if err != nil {
t.Fatalf("CommitDigest blake3: %v", err) t.Fatalf("CommitDigest blake3: %v", err)
} }
@@ -679,7 +679,7 @@ func TestDKG2_FilterQualifiedQuorum(t *testing.T) {
// //
// A · NTT(s) + B · NTT(t_master) ?= Σ_i C_{i,0} (mod q) // A · NTT(s) + B · NTT(t_master) ?= Σ_i C_{i,0} (mod q)
// //
// where s = Σ_j λ_j · s_j is the reconstructed Pulsar secret and // where s = Σ_j λ_j · s_j is the reconstructed Corona secret and
// t_master = Σ_j λ_j · u_j is the reconstructed blinding scalar (Lagrange // t_master = Σ_j λ_j · u_j is the reconstructed blinding scalar (Lagrange
// recombination over an arbitrary t-subset T). // recombination over an arbitrary t-subset T).
// //
@@ -869,23 +869,23 @@ func TestDKG2_VerifyShareAgainstCommits_Pure(t *testing.T) {
} }
// TestDKG2_SignIntegration_PathC — closes the loop from DKG2 output to a // TestDKG2_SignIntegration_PathC — closes the loop from DKG2 output to a
// Pulsar Sign-compatible public key via the recommended path (c) of // Corona Sign-compatible public key via the recommended path (c) of
// papers/lp-073-pulsar/sections/08a-pedersen-dkg.tex. // papers/lp-073-pulsar/sections/08a-pedersen-dkg.tex.
// //
// Path (c) in production: run dkg2, recombine s = Σ_j λ_j s_j over a // Path (c) in production: run dkg2, recombine s = Σ_j λ_j s_j over a
// t-subset, sample fresh Gaussian e, build b = A·s + e, round to bTilde, // t-subset, sample fresh Gaussian e, build b = A·s + e, round to bTilde,
// then run Pulsar Sign as normal under bTilde. This test mechanises the // then run Corona Sign as normal under bTilde. This test mechanises the
// "DKG-output → Pulsar-shaped pk" leg and confirms (i) recombined s has // "DKG-output → Corona-shaped pk" leg and confirms (i) recombined s has
// the expected dimension and lattice shape, (ii) the b = A·s + e // the expected dimension and lattice shape, (ii) the b = A·s + e
// construction yields a Pulsar-shaped bTilde of the right shape, and // construction yields a Corona-shaped bTilde of the right shape, and
// (iii) the round-trip RestoreVector(RoundVector(b)) deviates from b by // (iii) the round-trip RestoreVector(RoundVector(b)) deviates from b by
// at most the Xi rounding tolerance — i.e., Pulsar Sign Verify's // at most the Xi rounding tolerance — i.e., Corona Sign Verify's
// L2-norm check would accept a signature produced under (A, bTilde). // L2-norm check would accept a signature produced under (A, bTilde).
// //
// The full Sign1/Sign2/Combine path uses sign.Gen which generates s // The full Sign1/Sign2/Combine path uses sign.Gen which generates s
// internally; injecting an externally-supplied DKG s into Sign requires // internally; injecting an externally-supplied DKG s into Sign requires
// the small refactor of sign.Gen to accept an external secret. That // the small refactor of sign.Gen to accept an external secret. That
// refactor is independent of dkg2 and tracked in pulsar/sign; the // refactor is independent of dkg2 and tracked in corona/sign; the
// algebraic compatibility this test confirms is the binding contract // algebraic compatibility this test confirms is the binding contract
// between dkg2 and Sign. // between dkg2 and Sign.
func TestDKG2_SignIntegration_PathC(t *testing.T) { func TestDKG2_SignIntegration_PathC(t *testing.T) {
@@ -953,9 +953,9 @@ func TestDKG2_SignIntegration_PathC(t *testing.T) {
utils.MatrixVectorMul(r, A, sNTT, asN) utils.MatrixVectorMul(r, A, sNTT, asN)
utils.ConvertVectorFromNTT(r, asN) utils.ConvertVectorFromNTT(r, asN)
// Sample fresh small e — uses the same parameters Pulsar Sign Gen // Sample fresh small e — uses the same parameters Corona Sign Gen
// uses (sign.go:66), so the resulting (A, b) pair is statistically // uses (sign.go:66), so the resulting (A, b) pair is statistically
// identical to a trusted-dealer Pulsar setup. // identical to a trusted-dealer Corona setup.
eSeed := make([]byte, sign.KeySize) eSeed := make([]byte, sign.KeySize)
for i := range eSeed { for i := range eSeed {
eSeed[i] = byte(0xE5 ^ i) eSeed[i] = byte(0xE5 ^ i)
@@ -974,7 +974,7 @@ func TestDKG2_SignIntegration_PathC(t *testing.T) {
} }
// Round and restore — the deviation must be within the Xi rounding // Round and restore — the deviation must be within the Xi rounding
// tolerance. The Pulsar Sign verify path does exactly this round-trip // tolerance. The Corona Sign verify path does exactly this round-trip
// (sign/sign.go:284-285). // (sign/sign.go:284-285).
bTilde := utils.RoundVector(r, params.RXi, b, sign.Xi) bTilde := utils.RoundVector(r, params.RXi, b, sign.Xi)
bRestored := utils.RestoreVector(r, params.RXi, bTilde, sign.Xi) bRestored := utils.RestoreVector(r, params.RXi, bTilde, sign.Xi)
@@ -1033,7 +1033,7 @@ func TestDKG2_KAT(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("CommitDigestBLAKE3: %v", err) t.Fatalf("CommitDigestBLAKE3: %v", err)
} }
dSHA3, err := out.CommitDigest(hash.NewPulsarSHA3()) dSHA3, err := out.CommitDigest(hash.NewCoronaSHA3())
if err != nil { if err != nil {
t.Fatalf("CommitDigest sha3: %v", err) t.Fatalf("CommitDigest sha3: %v", err)
} }
@@ -1048,7 +1048,7 @@ func TestDKG2_KAT(t *testing.T) {
t.Fatalf("Round1WithSeed (2): %v", err) t.Fatalf("Round1WithSeed (2): %v", err)
} }
dBLAKE3b, _ := out2.CommitDigestBLAKE3() dBLAKE3b, _ := out2.CommitDigestBLAKE3()
dSHA3b, _ := out2.CommitDigest(hash.NewPulsarSHA3()) dSHA3b, _ := out2.CommitDigest(hash.NewCoronaSHA3())
if dBLAKE3 != dBLAKE3b { if dBLAKE3 != dBLAKE3b {
t.Fatal("CommitDigestBLAKE3 not stable across sessions") t.Fatal("CommitDigestBLAKE3 not stable across sessions")
} }
+2 -2
View File
@@ -26,12 +26,12 @@ import (
// fuzzMaxRawSize bounds the input handed to the lattigo decoder so // fuzzMaxRawSize bounds the input handed to the lattigo decoder so
// the unpatched-upstream-lattigo DoS path (luxfi/lattice#2) is // the unpatched-upstream-lattigo DoS path (luxfi/lattice#2) is
// neutralized by the recover boundary in <1ms. See // neutralized by the recover boundary in <1ms. See
// pulsar/threshold/fuzz_round_test.go for the rationale. // corona/threshold/fuzz_round_test.go for the rationale.
const fuzzMaxRawSize = 1024 const fuzzMaxRawSize = 1024
// decodeVectorWithRecover decodes a Vector[Poly] from raw bytes with // decodeVectorWithRecover decodes a Vector[Poly] from raw bytes with
// the same defense-in-depth stack as // the same defense-in-depth stack as
// pulsar/threshold/fuzz_round_test.go: hard byte cap + recover. // corona/threshold/fuzz_round_test.go: hard byte cap + recover.
func decodeVectorWithRecover(raw []byte) (err error) { func decodeVectorWithRecover(raw []byte) (err error) {
if len(raw) > fuzzMaxRawSize { if len(raw) > fuzzMaxRawSize {
return fmt.Errorf("input exceeds fuzzMaxRawSize") return fmt.Errorf("input exceeds fuzzMaxRawSize")
+15 -15
View File
@@ -3,9 +3,9 @@
package hash package hash
// PulsarBLAKE3 is a NON-NORMATIVE legacy suite kept for byte-equality // CoronaBLAKE3 is a NON-NORMATIVE legacy suite kept for byte-equality
// checks against transcripts produced before the Pulsar-SHA3 profile // checks against transcripts produced before the Corona-SHA3 profile
// was pinned as canonical. NEW deployments MUST use Pulsar-SHA3. // was pinned as canonical. NEW deployments MUST use Corona-SHA3.
import ( import (
"encoding/binary" "encoding/binary"
@@ -13,22 +13,22 @@ import (
"github.com/zeebo/blake3" "github.com/zeebo/blake3"
) )
// pulsarBLAKE3 implements HashSuite using BLAKE3 primitives. // coronaBLAKE3 implements HashSuite using BLAKE3 primitives.
type pulsarBLAKE3 struct{} type coronaBLAKE3 struct{}
// NewPulsarBLAKE3 returns the legacy BLAKE3 suite. NOT for production. // NewCoronaBLAKE3 returns the legacy BLAKE3 suite. NOT for production.
func NewPulsarBLAKE3() HashSuite { return pulsarBLAKE3{} } func NewCoronaBLAKE3() HashSuite { return coronaBLAKE3{} }
func (pulsarBLAKE3) ID() string { return "Pulsar-BLAKE3" } func (coronaBLAKE3) ID() string { return "Corona-BLAKE3" }
func (pulsarBLAKE3) Hc(transcript []byte) []byte { func (coronaBLAKE3) Hc(transcript []byte) []byte {
h := blake3.New() h := blake3.New()
_, _ = h.Write([]byte(tagHC)) _, _ = h.Write([]byte(tagHC))
_, _ = h.Write(transcript) _, _ = h.Write(transcript)
return h.Sum(nil)[:32] return h.Sum(nil)[:32]
} }
func (pulsarBLAKE3) Hu(transcript []byte, outLen int) []byte { func (coronaBLAKE3) Hu(transcript []byte, outLen int) []byte {
h := blake3.New() h := blake3.New()
_, _ = h.Write([]byte(tagHU)) _, _ = h.Write([]byte(tagHU))
_, _ = h.Write(transcript) _, _ = h.Write(transcript)
@@ -37,7 +37,7 @@ func (pulsarBLAKE3) Hu(transcript []byte, outLen int) []byte {
return out return out
} }
func (pulsarBLAKE3) TranscriptHash(parts ...[]byte) [32]byte { func (coronaBLAKE3) TranscriptHash(parts ...[]byte) [32]byte {
h := blake3.New() h := blake3.New()
_, _ = h.Write([]byte(tagTranscript)) _, _ = h.Write([]byte(tagTranscript))
for _, p := range parts { for _, p := range parts {
@@ -51,7 +51,7 @@ func (pulsarBLAKE3) TranscriptHash(parts ...[]byte) [32]byte {
return out return out
} }
func (pulsarBLAKE3) PRF(key, msg []byte, outLen int) []byte { func (coronaBLAKE3) PRF(key, msg []byte, outLen int) []byte {
keyArr := blake3SizedKey(key) keyArr := blake3SizedKey(key)
h, _ := blake3.NewKeyed(keyArr[:]) h, _ := blake3.NewKeyed(keyArr[:])
_, _ = h.Write([]byte(tagPRF)) _, _ = h.Write([]byte(tagPRF))
@@ -61,7 +61,7 @@ func (pulsarBLAKE3) PRF(key, msg []byte, outLen int) []byte {
return out return out
} }
func (pulsarBLAKE3) MAC(key, msg []byte, outLen int) []byte { func (coronaBLAKE3) MAC(key, msg []byte, outLen int) []byte {
keyArr := blake3SizedKey(key) keyArr := blake3SizedKey(key)
h, _ := blake3.NewKeyed(keyArr[:]) h, _ := blake3.NewKeyed(keyArr[:])
_, _ = h.Write([]byte(tagMAC)) _, _ = h.Write([]byte(tagMAC))
@@ -71,7 +71,7 @@ func (pulsarBLAKE3) MAC(key, msg []byte, outLen int) []byte {
return out return out
} }
func (pulsarBLAKE3) DerivePairwise( func (coronaBLAKE3) DerivePairwise(
kex []byte, kex []byte,
chainID, groupID []byte, chainID, groupID []byte,
eraID, generation uint64, eraID, generation uint64,
@@ -111,7 +111,7 @@ func blake3SizedKey(kex []byte) [32]byte {
return out return out
} }
h := blake3.New() h := blake3.New()
_, _ = h.Write([]byte("PULSAR-KDF-KEY-v1")) _, _ = h.Write([]byte("CORONA-KDF-KEY-v1"))
_, _ = h.Write(kex) _, _ = h.Write(kex)
var out [32]byte var out [32]byte
copy(out[:], h.Sum(nil)[:32]) copy(out[:], h.Sum(nil)[:32])
+10 -10
View File
@@ -2,15 +2,15 @@
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package hash defines the canonical hashing profile used by every // Package hash defines the canonical hashing profile used by every
// Pulsar reshare / activation / pairwise routine. // Corona reshare / activation / pairwise routine.
// //
// Two profiles are shipped: // Two profiles are shipped:
// //
// - Pulsar-SHA3 — the production profile. Built on cSHAKE256, KMAC256, // - Corona-SHA3 — the production profile. Built on cSHAKE256, KMAC256,
// and TupleHash256 from FIPS 202 / NIST SP 800-185. KATs in the // and TupleHash256 from FIPS 202 / NIST SP 800-185. KATs in the
// reshare oracle are emitted under this profile. // reshare oracle are emitted under this profile.
// //
// - Pulsar-BLAKE3 — the legacy / non-normative profile. Preserved so // - Corona-BLAKE3 — the legacy / non-normative profile. Preserved so
// historical bytes can be reproduced for cross-checks. Marked NOT // historical bytes can be reproduced for cross-checks. Marked NOT
// for production. // for production.
// //
@@ -34,12 +34,12 @@
// produce different bytes (cross-profile collision avoidance). // produce different bytes (cross-profile collision avoidance).
package hash package hash
// HashSuite is the canonical hashing surface every Pulsar reshare, // HashSuite is the canonical hashing surface every Corona reshare,
// activation, and pairwise routine uses. Implementations are // activation, and pairwise routine uses. Implementations are
// stateless, goroutine-safe, and deterministic. // stateless, goroutine-safe, and deterministic.
type HashSuite interface { type HashSuite interface {
// ID returns the profile identifier, e.g. "Pulsar-SHA3" or // ID returns the profile identifier, e.g. "Corona-SHA3" or
// "Pulsar-BLAKE3". Bound into transcripts so two profiles can // "Corona-BLAKE3". Bound into transcripts so two profiles can
// never collide on the byte level. // never collide on the byte level.
ID() string ID() string
@@ -82,14 +82,14 @@ type HashSuite interface {
) []byte ) []byte
} }
// Default returns the production hash suite: Pulsar-SHA3. // Default returns the production hash suite: Corona-SHA3.
func Default() HashSuite { return defaultSuite } func Default() HashSuite { return defaultSuite }
// DefaultID is the string ID of the production suite. // DefaultID is the string ID of the production suite.
const DefaultID = "Pulsar-SHA3" const DefaultID = "Corona-SHA3"
// LegacyBLAKE3ID is the string ID of the non-normative legacy suite. // LegacyBLAKE3ID is the string ID of the non-normative legacy suite.
const LegacyBLAKE3ID = "Pulsar-BLAKE3" const LegacyBLAKE3ID = "Corona-BLAKE3"
// resolve picks the suite to use for a given call. // resolve picks the suite to use for a given call.
func resolve(s HashSuite) HashSuite { func resolve(s HashSuite) HashSuite {
@@ -103,4 +103,4 @@ func resolve(s HashSuite) HashSuite {
func Resolve(s HashSuite) HashSuite { return resolve(s) } func Resolve(s HashSuite) HashSuite { return resolve(s) }
// defaultSuite is the package-level singleton for the production profile. // defaultSuite is the package-level singleton for the production profile.
var defaultSuite HashSuite = NewPulsarSHA3() var defaultSuite HashSuite = NewCoronaSHA3()
+16 -16
View File
@@ -12,14 +12,14 @@ import (
// TestSuiteIDs ensures the two suites declare distinct, stable IDs. // TestSuiteIDs ensures the two suites declare distinct, stable IDs.
func TestSuiteIDs(t *testing.T) { func TestSuiteIDs(t *testing.T) {
sha3 := NewPulsarSHA3() sha3 := NewCoronaSHA3()
bl3 := NewPulsarBLAKE3() bl3 := NewCoronaBLAKE3()
if sha3.ID() != "Pulsar-SHA3" { if sha3.ID() != "Corona-SHA3" {
t.Errorf("SHA3 suite ID: want Pulsar-SHA3, got %q", sha3.ID()) t.Errorf("SHA3 suite ID: want Corona-SHA3, got %q", sha3.ID())
} }
if bl3.ID() != "Pulsar-BLAKE3" { if bl3.ID() != "Corona-BLAKE3" {
t.Errorf("BLAKE3 suite ID: want Pulsar-BLAKE3, got %q", bl3.ID()) t.Errorf("BLAKE3 suite ID: want Corona-BLAKE3, got %q", bl3.ID())
} }
if sha3.ID() == bl3.ID() { if sha3.ID() == bl3.ID() {
t.Error("SHA3 and BLAKE3 suites must declare distinct IDs") t.Error("SHA3 and BLAKE3 suites must declare distinct IDs")
@@ -31,7 +31,7 @@ func TestSuiteIDs(t *testing.T) {
// TestDefaultIsSHA3 — production default must be SHA3. // TestDefaultIsSHA3 — production default must be SHA3.
func TestDefaultIsSHA3(t *testing.T) { func TestDefaultIsSHA3(t *testing.T) {
if Default().ID() != "Pulsar-SHA3" { if Default().ID() != "Corona-SHA3" {
t.Fatalf("production default must be SHA3; got %q", Default().ID()) t.Fatalf("production default must be SHA3; got %q", Default().ID())
} }
} }
@@ -41,7 +41,7 @@ func TestResolveNil(t *testing.T) {
if Resolve(nil).ID() != DefaultID { if Resolve(nil).ID() != DefaultID {
t.Errorf("Resolve(nil) must return the production default") t.Errorf("Resolve(nil) must return the production default")
} }
bl3 := NewPulsarBLAKE3() bl3 := NewCoronaBLAKE3()
if Resolve(bl3).ID() != bl3.ID() { if Resolve(bl3).ID() != bl3.ID() {
t.Errorf("Resolve(suite) must return that suite") t.Errorf("Resolve(suite) must return that suite")
} }
@@ -50,8 +50,8 @@ func TestResolveNil(t *testing.T) {
// TestSuitesProduceDifferentBytes — two suites with distinct IDs must // TestSuitesProduceDifferentBytes — two suites with distinct IDs must
// produce distinct bytes for the same input. // produce distinct bytes for the same input.
func TestSuitesProduceDifferentBytes(t *testing.T) { func TestSuitesProduceDifferentBytes(t *testing.T) {
sha3 := NewPulsarSHA3() sha3 := NewCoronaSHA3()
bl3 := NewPulsarBLAKE3() bl3 := NewCoronaBLAKE3()
t1 := sha3.TranscriptHash([]byte("a"), []byte("b")) t1 := sha3.TranscriptHash([]byte("a"), []byte("b"))
t2 := bl3.TranscriptHash([]byte("a"), []byte("b")) t2 := bl3.TranscriptHash([]byte("a"), []byte("b"))
@@ -76,7 +76,7 @@ func TestSuitesProduceDifferentBytes(t *testing.T) {
// TestPRFAndMACDifferByCustomization — same suite, same key, same // TestPRFAndMACDifferByCustomization — same suite, same key, same
// message, but PRF and MAC must produce distinct bytes. // message, but PRF and MAC must produce distinct bytes.
func TestPRFAndMACDifferByCustomization(t *testing.T) { func TestPRFAndMACDifferByCustomization(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
key := []byte("00000000000000000000000000000000") // 32 bytes key := []byte("00000000000000000000000000000000") // 32 bytes
msg := []byte("same-message") msg := []byte("same-message")
prf := s.PRF(key, msg, 32) prf := s.PRF(key, msg, 32)
@@ -90,7 +90,7 @@ func TestPRFAndMACDifferByCustomization(t *testing.T) {
// TestHcAndHuDifferByCustomization — same suite, same input, different // TestHcAndHuDifferByCustomization — same suite, same input, different
// tags → different bytes. // tags → different bytes.
func TestHcAndHuDifferByCustomization(t *testing.T) { func TestHcAndHuDifferByCustomization(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
hc := s.Hc([]byte("transcript")) hc := s.Hc([]byte("transcript"))
hu := s.Hu([]byte("transcript"), 32) hu := s.Hu([]byte("transcript"), 32)
if bytes.Equal(hc, hu) { if bytes.Equal(hc, hu) {
@@ -102,7 +102,7 @@ func TestHcAndHuDifferByCustomization(t *testing.T) {
// TestTranscriptHashAvoidsConcatenationCollisions — TupleHash framing // TestTranscriptHashAvoidsConcatenationCollisions — TupleHash framing
// must reject two distinct lists whose naive concatenation is identical. // must reject two distinct lists whose naive concatenation is identical.
func TestTranscriptHashAvoidsConcatenationCollisions(t *testing.T) { func TestTranscriptHashAvoidsConcatenationCollisions(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
a := s.TranscriptHash([]byte("abc"), []byte("")) a := s.TranscriptHash([]byte("abc"), []byte(""))
b := s.TranscriptHash([]byte("a"), []byte("bc")) b := s.TranscriptHash([]byte("a"), []byte("bc"))
if a == b { if a == b {
@@ -118,7 +118,7 @@ func TestTranscriptHashAvoidsConcatenationCollisions(t *testing.T) {
// TestPairwiseCanonicalOrdering — DerivePairwise must produce the same // TestPairwiseCanonicalOrdering — DerivePairwise must produce the same
// bytes for (i, j) and (j, i). // bytes for (i, j) and (j, i).
func TestPairwiseCanonicalOrdering(t *testing.T) { func TestPairwiseCanonicalOrdering(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
kex := []byte("0123456789abcdef0123456789abcdef") kex := []byte("0123456789abcdef0123456789abcdef")
ab := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 2, 5, 32) ab := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 2, 5, 32)
ba := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 5, 2, 32) ba := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 5, 2, 32)
@@ -131,7 +131,7 @@ func TestPairwiseCanonicalOrdering(t *testing.T) {
// TestPairwiseDistinctEras — different (era, generation) MUST yield // TestPairwiseDistinctEras — different (era, generation) MUST yield
// different pairwise material. // different pairwise material.
func TestPairwiseDistinctEras(t *testing.T) { func TestPairwiseDistinctEras(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
kex := []byte("0123456789abcdef0123456789abcdef") kex := []byte("0123456789abcdef0123456789abcdef")
base := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 2, 5, 32) base := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 7, 3, 2, 5, 32)
era2 := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 8, 3, 2, 5, 32) era2 := s.DerivePairwise(kex, []byte("chain"), []byte("group"), 8, 3, 2, 5, 32)
@@ -221,7 +221,7 @@ func TestTupleHash256NISTVector(t *testing.T) {
// TestSuiteDeterminism — same input, two calls, identical bytes. // TestSuiteDeterminism — same input, two calls, identical bytes.
func TestSuiteDeterminism(t *testing.T) { func TestSuiteDeterminism(t *testing.T) {
for _, s := range []HashSuite{NewPulsarSHA3(), NewPulsarBLAKE3()} { for _, s := range []HashSuite{NewCoronaSHA3(), NewCoronaBLAKE3()} {
a := s.TranscriptHash([]byte("a"), []byte("b")) a := s.TranscriptHash([]byte("a"), []byte("b"))
b := s.TranscriptHash([]byte("a"), []byte("b")) b := s.TranscriptHash([]byte("a"), []byte("b"))
if a != b { if a != b {
+25 -25
View File
@@ -3,17 +3,17 @@
package hash package hash
// PulsarSHA3 is the production hash suite for Pulsar. Built on // CoronaSHA3 is the production hash suite for Corona. Built on
// cSHAKE256 / KMAC256 / TupleHash256 from FIPS 202 and NIST SP 800-185. // cSHAKE256 / KMAC256 / TupleHash256 from FIPS 202 and NIST SP 800-185.
// //
// Customization tags pin every operation to the Pulsar protocol: // Customization tags pin every operation to the Corona protocol:
// //
// Hc "PULSAR-HC-v1" // Hc "CORONA-HC-v1"
// Hu "PULSAR-HU-v1" // Hu "CORONA-HU-v1"
// TranscriptHash "PULSAR-TRANSCRIPT-v1" // TranscriptHash "CORONA-TRANSCRIPT-v1"
// PRF "PULSAR-PRF-v1" (KMAC256) // PRF "CORONA-PRF-v1" (KMAC256)
// MAC "PULSAR-MAC-v1" (KMAC256) // MAC "CORONA-MAC-v1" (KMAC256)
// DerivePairwise "PULSAR-PAIRWISE-v1" (KMAC256) // DerivePairwise "CORONA-PAIRWISE-v1" (KMAC256)
// //
// Distinct customization strings are essential — same primitive + // Distinct customization strings are essential — same primitive +
// different tag = independent oracle. Bumping any tag invalidates // different tag = independent oracle. Bumping any tag invalidates
@@ -26,46 +26,46 @@ import (
) )
const ( const (
tagHC = "PULSAR-HC-v1" tagHC = "CORONA-HC-v1"
tagHU = "PULSAR-HU-v1" tagHU = "CORONA-HU-v1"
tagTranscript = "PULSAR-TRANSCRIPT-v1" tagTranscript = "CORONA-TRANSCRIPT-v1"
tagPRF = "PULSAR-PRF-v1" tagPRF = "CORONA-PRF-v1"
tagMAC = "PULSAR-MAC-v1" tagMAC = "CORONA-MAC-v1"
tagPairwise = "PULSAR-PAIRWISE-v1" tagPairwise = "CORONA-PAIRWISE-v1"
) )
// pulsarSHA3 implements HashSuite using the SP 800-185 primitives. // coronaSHA3 implements HashSuite using the SP 800-185 primitives.
type pulsarSHA3 struct{} type coronaSHA3 struct{}
// NewPulsarSHA3 returns the production hash suite. // NewCoronaSHA3 returns the production hash suite.
func NewPulsarSHA3() HashSuite { return pulsarSHA3{} } func NewCoronaSHA3() HashSuite { return coronaSHA3{} }
func (pulsarSHA3) ID() string { return "Pulsar-SHA3" } func (coronaSHA3) ID() string { return "Corona-SHA3" }
func (pulsarSHA3) Hc(transcript []byte) []byte { func (coronaSHA3) Hc(transcript []byte) []byte {
return cshake256Stream(tagHC, transcript, 32) return cshake256Stream(tagHC, transcript, 32)
} }
func (pulsarSHA3) Hu(transcript []byte, outLen int) []byte { func (coronaSHA3) Hu(transcript []byte, outLen int) []byte {
return cshake256Stream(tagHU, transcript, outLen) return cshake256Stream(tagHU, transcript, outLen)
} }
func (pulsarSHA3) TranscriptHash(parts ...[]byte) [32]byte { func (coronaSHA3) TranscriptHash(parts ...[]byte) [32]byte {
out := tupleHash256(parts, 32, tagTranscript) out := tupleHash256(parts, 32, tagTranscript)
var fixed [32]byte var fixed [32]byte
copy(fixed[:], out) copy(fixed[:], out)
return fixed return fixed
} }
func (pulsarSHA3) PRF(key, msg []byte, outLen int) []byte { func (coronaSHA3) PRF(key, msg []byte, outLen int) []byte {
return kmac256(key, msg, outLen, tagPRF) return kmac256(key, msg, outLen, tagPRF)
} }
func (pulsarSHA3) MAC(key, msg []byte, outLen int) []byte { func (coronaSHA3) MAC(key, msg []byte, outLen int) []byte {
return kmac256(key, msg, outLen, tagMAC) return kmac256(key, msg, outLen, tagMAC)
} }
func (pulsarSHA3) DerivePairwise( func (coronaSHA3) DerivePairwise(
kex []byte, kex []byte,
chainID, groupID []byte, chainID, groupID []byte,
eraID, generation uint64, eraID, generation uint64,
+14 -14
View File
@@ -33,11 +33,11 @@ import (
"github.com/luxfi/corona/hash" "github.com/luxfi/corona/hash"
) )
// TestBootstrapPinsSuiteSHA3 — Gate 3A: Bootstrap with Pulsar-SHA3 → // TestBootstrapPinsSuiteSHA3 — Gate 3A: Bootstrap with Corona-SHA3 →
// era.HashSuiteID == "Pulsar-SHA3". // era.HashSuiteID == "Corona-SHA3".
func TestBootstrapPinsSuiteSHA3(t *testing.T) { func TestBootstrapPinsSuiteSHA3(t *testing.T) {
era, err := BootstrapWithSuite( era, err := BootstrapWithSuite(
hash.NewPulsarSHA3(), hash.NewCoronaSHA3(),
3, 3,
[]string{"a", "b", "c"}, []string{"a", "b", "c"},
0, 0, 0, 0,
@@ -54,11 +54,11 @@ func TestBootstrapPinsSuiteSHA3(t *testing.T) {
} }
} }
// TestBootstrapPinsSuiteBLAKE3 — Gate 3A: Bootstrap with Pulsar-BLAKE3 // TestBootstrapPinsSuiteBLAKE3 — Gate 3A: Bootstrap with Corona-BLAKE3
// → era.HashSuiteID == "Pulsar-BLAKE3". // → era.HashSuiteID == "Corona-BLAKE3".
func TestBootstrapPinsSuiteBLAKE3(t *testing.T) { func TestBootstrapPinsSuiteBLAKE3(t *testing.T) {
era, err := BootstrapWithSuite( era, err := BootstrapWithSuite(
hash.NewPulsarBLAKE3(), hash.NewCoronaBLAKE3(),
3, 3,
[]string{"a", "b", "c"}, []string{"a", "b", "c"},
0, 0, 0, 0,
@@ -89,11 +89,11 @@ func TestBootstrapDefaultsToSHA3(t *testing.T) {
} }
} }
// TestReshareCannotChangeSuiteSHA3 — Gate 3B: Reshare on a Pulsar-SHA3 // TestReshareCannotChangeSuiteSHA3 — Gate 3B: Reshare on a Corona-SHA3
// era yields a state with HashSuiteID == "Pulsar-SHA3" (unchanged). // era yields a state with HashSuiteID == "Corona-SHA3" (unchanged).
func TestReshareCannotChangeSuiteSHA3(t *testing.T) { func TestReshareCannotChangeSuiteSHA3(t *testing.T) {
era, err := BootstrapWithSuite( era, err := BootstrapWithSuite(
hash.NewPulsarSHA3(), hash.NewCoronaSHA3(),
3, 3,
[]string{"v1", "v2", "v3"}, []string{"v1", "v2", "v3"},
0, 0, 0, 0,
@@ -120,7 +120,7 @@ func TestReshareCannotChangeSuiteSHA3(t *testing.T) {
// profile. A BLAKE3-pinned era stays BLAKE3 across Reshare. // profile. A BLAKE3-pinned era stays BLAKE3 across Reshare.
func TestReshareCannotChangeSuiteBLAKE3(t *testing.T) { func TestReshareCannotChangeSuiteBLAKE3(t *testing.T) {
era, err := BootstrapWithSuite( era, err := BootstrapWithSuite(
hash.NewPulsarBLAKE3(), hash.NewCoronaBLAKE3(),
3, 3,
[]string{"v1", "v2", "v3"}, []string{"v1", "v2", "v3"},
0, 0, 0, 0,
@@ -170,11 +170,11 @@ func TestReshareAPIHasNoHashSuiteParameter(t *testing.T) {
} }
// TestReanchorMayChangeSuite — Gate 3C: ReanchorWithSuite from a // TestReanchorMayChangeSuite — Gate 3C: ReanchorWithSuite from a
// Pulsar-SHA3 era to a Pulsar-BLAKE3 era yields era_2.HashSuiteID == // Corona-SHA3 era to a Corona-BLAKE3 era yields era_2.HashSuiteID ==
// "Pulsar-BLAKE3", and era_1 is unchanged. // "Corona-BLAKE3", and era_1 is unchanged.
func TestReanchorMayChangeSuite(t *testing.T) { func TestReanchorMayChangeSuite(t *testing.T) {
era1, err := BootstrapWithSuite( era1, err := BootstrapWithSuite(
hash.NewPulsarSHA3(), hash.NewCoronaSHA3(),
3, 3,
[]string{"a", "b", "c"}, []string{"a", "b", "c"},
0, 1, 0, 1,
@@ -189,7 +189,7 @@ func TestReanchorMayChangeSuite(t *testing.T) {
era2, err := ReanchorWithSuite( era2, err := ReanchorWithSuite(
era1, era1,
hash.NewPulsarBLAKE3(), hash.NewCoronaBLAKE3(),
3, 3,
[]string{"d", "e", "f"}, []string{"d", "e", "f"},
0, 0,
+25 -25
View File
@@ -1,7 +1,7 @@
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved. // Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package keyera is the lifecycle wrapper for a Pulsar group lineage. // Package keyera is the lifecycle wrapper for a Corona group lineage.
// //
// One KeyEra is opened by Bootstrap (a one-time foundation MPC ceremony // One KeyEra is opened by Bootstrap (a one-time foundation MPC ceremony
// at chain genesis or governance-gated Reanchor). The trust is confined // at chain genesis or governance-gated Reanchor). The trust is confined
@@ -18,7 +18,7 @@
// //
// BLS lane: each validator has its OWN keypair. // BLS lane: each validator has its OWN keypair.
// ML-DSA lane: each validator has its OWN keypair. // ML-DSA lane: each validator has its OWN keypair.
// Pulsar lane: each validator has a SHARE of one group key. // Corona lane: each validator has a SHARE of one group key.
// //
// Within a key era: // Within a key era:
// //
@@ -60,17 +60,17 @@ var (
ErrMissingShare = errors.New("keyera: share missing for validator") ErrMissingShare = errors.New("keyera: share missing for validator")
) )
// PulsarKeyEraID is a monotonically increasing identifier for a key era. // CoronaKeyEraID is a monotonically increasing identifier for a key era.
// Bumped only on Reanchor (rare governance event). All resharings // Bumped only on Reanchor (rare governance event). All resharings
// within an era keep the same era ID. // within an era keep the same era ID.
type PulsarKeyEraID uint64 type CoronaKeyEraID uint64
// PulsarGroupID identifies one Pulsar group for grouped Quasar setups // CoronaGroupID identifies one Corona group for grouped Quasar setups
// where validator sets are partitioned into smaller groups, each with // where validator sets are partitioned into smaller groups, each with
// its own GroupKey lineage. For the single-group case it is zero. // its own GroupKey lineage. For the single-group case it is zero.
type PulsarGroupID uint64 type CoronaGroupID uint64
// KeyEra is one Pulsar group lineage. The GroupKey (A, bTilde) is set at // KeyEra is one Corona group lineage. The GroupKey (A, bTilde) is set at
// Bootstrap and persists across every Reshare within the era. State is // Bootstrap and persists across every Reshare within the era. State is
// the current epoch's share distribution; it rotates each Reshare. // the current epoch's share distribution; it rotates each Reshare.
// //
@@ -81,8 +81,8 @@ type PulsarGroupID uint64
// field is read-only after Bootstrap returns; Reshare propagates it // field is read-only after Bootstrap returns; Reshare propagates it
// without parameterisation. // without parameterisation.
type KeyEra struct { type KeyEra struct {
EraID PulsarKeyEraID EraID CoronaKeyEraID
GroupID PulsarGroupID GroupID CoronaGroupID
GroupKey *threshold.GroupKey GroupKey *threshold.GroupKey
GenesisEpoch uint64 GenesisEpoch uint64
HashSuiteID string HashSuiteID string
@@ -96,12 +96,12 @@ type KeyEra struct {
// Three lineage fields, kept distinct (do not collapse — they mean // Three lineage fields, kept distinct (do not collapse — they mean
// different things): // different things):
// //
// - KeyEraID: Pulsar group-key lineage. Bumps only at Reanchor (fresh // - KeyEraID: Corona group-key lineage. Bumps only at Reanchor (fresh
// GroupKey). // GroupKey).
// - Generation: LSS resharing version within this key era. Bumps // - Generation: LSS resharing version within this key era. Bumps
// every Refresh / Reshare under the same GroupKey. Aligns with // every Refresh / Reshare under the same GroupKey. Aligns with
// LSS's Generation field; managed by threshold/protocols/lss when // LSS's Generation field; managed by threshold/protocols/lss when
// this state is driven through the LSS-Pulsar adapter. // this state is driven through the LSS-Corona adapter.
// - RollbackFrom: nonzero only when this state descends from a // - RollbackFrom: nonzero only when this state descends from a
// Rollback (= the prior Generation that was reverted from). Zero // Rollback (= the prior Generation that was reverted from). Zero
// on ordinary forward transitions. // on ordinary forward transitions.
@@ -136,7 +136,7 @@ type EpochShareState struct {
// //
// The trust is confined to genesis of the key era: someone (the dealer) // The trust is confined to genesis of the key era: someone (the dealer)
// momentarily knows the master secret s while constructing the shares. // momentarily knows the master secret s while constructing the shares.
// If s is retained, copied, or exfiltrated, the long-lived Pulsar group // If s is retained, copied, or exfiltrated, the long-lived Corona group
// key is compromised. Foundation MUST coordinate Bootstrap as a // key is compromised. Foundation MUST coordinate Bootstrap as a
// publicly observable MPC ceremony at chain launch — the entropy MUST // publicly observable MPC ceremony at chain launch — the entropy MUST
// come from a verifiable commit-and-reveal among the genesis // come from a verifiable commit-and-reveal among the genesis
@@ -152,10 +152,10 @@ type EpochShareState struct {
// ceremony source is provided. Tests pass a deterministic source for // ceremony source is provided. Tests pass a deterministic source for
// KAT replay. // KAT replay.
// //
// Bootstrap pins the production HashSuite (Pulsar-SHA3). Use // Bootstrap pins the production HashSuite (Corona-SHA3). Use
// BootstrapWithSuite to open an era under the legacy Pulsar-BLAKE3 // BootstrapWithSuite to open an era under the legacy Corona-BLAKE3
// profile (for cross-suite KAT replay only — NOT for production). // profile (for cross-suite KAT replay only — NOT for production).
func Bootstrap(t int, validators []string, groupID PulsarGroupID, eraID PulsarKeyEraID, entropy io.Reader) (*KeyEra, error) { func Bootstrap(t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
return BootstrapWithSuite(hash.Default(), t, validators, groupID, eraID, entropy) return BootstrapWithSuite(hash.Default(), t, validators, groupID, eraID, entropy)
} }
@@ -163,8 +163,8 @@ func Bootstrap(t int, validators []string, groupID PulsarGroupID, eraID PulsarKe
// the hash profile this era will run under. The supplied suite is // the hash profile this era will run under. The supplied suite is
// recorded on the returned KeyEra and propagates unchanged through // recorded on the returned KeyEra and propagates unchanged through
// every Reshare; Reanchor opens a fresh era and MAY pin a different // every Reshare; Reanchor opens a fresh era and MAY pin a different
// suite. Pass nil to use the production default (Pulsar-SHA3). // suite. Pass nil to use the production default (Corona-SHA3).
func BootstrapWithSuite(suite hash.HashSuite, t int, validators []string, groupID PulsarGroupID, eraID PulsarKeyEraID, entropy io.Reader) (*KeyEra, error) { func BootstrapWithSuite(suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, eraID CoronaKeyEraID, entropy io.Reader) (*KeyEra, error) {
if len(validators) == 0 { if len(validators) == 0 {
return nil, ErrEmptyValidators return nil, ErrEmptyValidators
} }
@@ -244,7 +244,7 @@ func BootstrapWithSuite(suite hash.HashSuite, t int, validators []string, groupI
// The bare Shamir kernel runs in-process; for distributed deployments // The bare Shamir kernel runs in-process; for distributed deployments
// the consensus layer wraps this in the full Verifiable Secret Resharing // the consensus layer wraps this in the full Verifiable Secret Resharing
// (VSR) exchange (commits, complaints, activation cert) defined in // (VSR) exchange (commits, complaints, activation cert) defined in
// pulsar/reshare. This kernel exists to (a) drive the cryptographic core, // corona/reshare. This kernel exists to (a) drive the cryptographic core,
// (b) be reused as the trusted-collaborator path for single-process // (b) be reused as the trusted-collaborator path for single-process
// integration tests, and (c) provide a reference against which the // integration tests, and (c) provide a reference against which the
// distributed protocol can be byte-equality checked. // distributed protocol can be byte-equality checked.
@@ -342,12 +342,12 @@ func (era *KeyEra) Reshare(newValidators []string, newThreshold int, randSource
// is not a routine operation. // is not a routine operation.
// //
// Reanchor inherits the prior era's HashSuiteID. To migrate to a // Reanchor inherits the prior era's HashSuiteID. To migrate to a
// different suite (e.g. moving from legacy Pulsar-BLAKE3 to production // different suite (e.g. moving from legacy Corona-BLAKE3 to production
// Pulsar-SHA3) call ReanchorWithSuite. // Corona-SHA3) call ReanchorWithSuite.
func Reanchor(prev *KeyEra, t int, validators []string, groupID PulsarGroupID, entropy io.Reader) (*KeyEra, error) { func Reanchor(prev *KeyEra, t int, validators []string, groupID CoronaGroupID, entropy io.Reader) (*KeyEra, error) {
var suite hash.HashSuite var suite hash.HashSuite
if prev != nil && prev.HashSuiteID == hash.LegacyBLAKE3ID { if prev != nil && prev.HashSuiteID == hash.LegacyBLAKE3ID {
suite = hash.NewPulsarBLAKE3() suite = hash.NewCoronaBLAKE3()
} else { } else {
suite = hash.Default() suite = hash.Default()
} }
@@ -359,8 +359,8 @@ func Reanchor(prev *KeyEra, t int, validators []string, groupID PulsarGroupID, e
// that may pin a hash profile different from the prior era's // that may pin a hash profile different from the prior era's
// (Reshare cannot — that is enforced by Reshare not accepting a suite // (Reshare cannot — that is enforced by Reshare not accepting a suite
// parameter). nil suite resolves to the production default. // parameter). nil suite resolves to the production default.
func ReanchorWithSuite(prev *KeyEra, suite hash.HashSuite, t int, validators []string, groupID PulsarGroupID, entropy io.Reader) (*KeyEra, error) { func ReanchorWithSuite(prev *KeyEra, suite hash.HashSuite, t int, validators []string, groupID CoronaGroupID, entropy io.Reader) (*KeyEra, error) {
var nextEraID PulsarKeyEraID var nextEraID CoronaKeyEraID
var nextEpoch uint64 var nextEpoch uint64
if prev != nil { if prev != nil {
nextEraID = prev.EraID + 1 nextEraID = prev.EraID + 1
@@ -458,7 +458,7 @@ func computeFullCommitteeLagrange(r *ring.Ring, n int) []ring.Poly {
// //
// In a single-process simulation the material is freshly drawn from // In a single-process simulation the material is freshly drawn from
// randSource. In a distributed deployment the consensus layer overrides // randSource. In a distributed deployment the consensus layer overrides
// this with authenticated pairwise KEX from pulsar/reshare/pairwise.go, // this with authenticated pairwise KEX from corona/reshare/pairwise.go,
// ensuring both endpoints derive the same value without a shared // ensuring both endpoints derive the same value without a shared
// trusted dealer. // trusted dealer.
func derivePairwiseMaterial(K int, randSource io.Reader) (map[int][][]byte, []map[int][]byte) { func derivePairwiseMaterial(K int, randSource io.Reader) (map[int][][]byte, []map[int][]byte) {
+1 -1
View File
@@ -15,7 +15,7 @@ import (
// TestBootstrapBuildsAndSigns confirms that Bootstrap returns a complete // TestBootstrapBuildsAndSigns confirms that Bootstrap returns a complete
// KeyShare set that can produce a verifying signature under the produced // KeyShare set that can produce a verifying signature under the produced
// GroupKey. We exercise t = n (every validator in the active signing // GroupKey. We exercise t = n (every validator in the active signing
// set) here; pulsar's signing protocol assumes the full committee // set) here; corona's signing protocol assumes the full committee
// participates in each Sign invocation. // participates in each Sign invocation.
func TestBootstrapBuildsAndSigns(t *testing.T) { func TestBootstrapBuildsAndSigns(t *testing.T) {
const tThr, n = 3, 3 const tThr, n = 3, 3
+8 -8
View File
@@ -39,8 +39,8 @@ func must(op string, err error) {
// 2026-05-03 in coordination with the C++ port at luxcpp/crypto). // 2026-05-03 in coordination with the C++ port at luxcpp/crypto).
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). Output bytes differ between Pulsar-SHA3 and // default (Corona-SHA3). Output bytes differ between Corona-SHA3 and
// Pulsar-BLAKE3 — this is the F22 cross-profile separation. // Corona-BLAKE3 — this is the F22 cross-profile separation.
func PRNGKey(suite hash.HashSuite, skShare structs.Vector[ring.Poly]) []byte { func PRNGKey(suite hash.HashSuite, skShare structs.Vector[ring.Poly]) []byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -59,7 +59,7 @@ func PRNGKey(suite hash.HashSuite, skShare structs.Vector[ring.Poly]) []byte {
// Domain tag distinguishes from any other future per-share keying. // Domain tag distinguishes from any other future per-share keying.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func PRNGKeyForRound(suite hash.HashSuite, skShare structs.Vector[ring.Poly], sid int64) []byte { func PRNGKeyForRound(suite hash.HashSuite, skShare structs.Vector[ring.Poly], sid int64) []byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
skBuf := new(bytes.Buffer) skBuf := new(bytes.Buffer)
@@ -76,7 +76,7 @@ func PRNGKeyForRound(suite hash.HashSuite, skShare structs.Vector[ring.Poly], si
// GenerateMAC generates a MAC for a given TildeD matrix and mask. // GenerateMAC generates a MAC for a given TildeD matrix and mask.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func GenerateMAC(suite hash.HashSuite, TildeD structs.Matrix[ring.Poly], MACKey []byte, partyID int, sid int, T []int, otherParty int, verify bool) []byte { func GenerateMAC(suite hash.HashSuite, TildeD structs.Matrix[ring.Poly], MACKey []byte, partyID int, sid int, T []int, otherParty int, verify bool) []byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -101,7 +101,7 @@ func GenerateMAC(suite hash.HashSuite, TildeD structs.Matrix[ring.Poly], MACKey
// GaussianHash hashes parameters to a Gaussian distribution. // GaussianHash hashes parameters to a Gaussian distribution.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func GaussianHash(suite hash.HashSuite, r *ring.Ring, hashIn []byte, mu string, sigmaU float64, boundU float64, length int) structs.Vector[ring.Poly] { func GaussianHash(suite hash.HashSuite, r *ring.Ring, hashIn []byte, mu string, sigmaU float64, boundU float64, length int) structs.Vector[ring.Poly] {
s := hash.Resolve(suite) s := hash.Resolve(suite)
transcript := new(bytes.Buffer) transcript := new(bytes.Buffer)
@@ -120,7 +120,7 @@ func GaussianHash(suite hash.HashSuite, r *ring.Ring, hashIn []byte, mu string,
// PRF generates pseudorandom ring elements. // PRF generates pseudorandom ring elements.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func PRF(suite hash.HashSuite, r *ring.Ring, sd_ij []byte, PRFKey []byte, mu string, hashIn []byte, n int) structs.Vector[ring.Poly] { func PRF(suite hash.HashSuite, r *ring.Ring, sd_ij []byte, PRFKey []byte, mu string, hashIn []byte, n int) structs.Vector[ring.Poly] {
s := hash.Resolve(suite) s := hash.Resolve(suite)
msg := new(bytes.Buffer) msg := new(bytes.Buffer)
@@ -139,7 +139,7 @@ func PRF(suite hash.HashSuite, r *ring.Ring, sd_ij []byte, PRFKey []byte, mu str
// Hash hashes precomputable values. // Hash hashes precomputable values.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func Hash(suite hash.HashSuite, A structs.Matrix[ring.Poly], b structs.Vector[ring.Poly], D map[int]structs.Matrix[ring.Poly], sid int, T []int) []byte { func Hash(suite hash.HashSuite, A structs.Matrix[ring.Poly], b structs.Vector[ring.Poly], D map[int]structs.Matrix[ring.Poly], sid int, T []int) []byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -168,7 +168,7 @@ func Hash(suite hash.HashSuite, A structs.Matrix[ring.Poly], b structs.Vector[ri
// LowNormHash hashes to low norm ring elements. // LowNormHash hashes to low norm ring elements.
// //
// `suite` selects the hash profile. nil resolves to the production // `suite` selects the hash profile. nil resolves to the production
// default (Pulsar-SHA3). // default (Corona-SHA3).
func LowNormHash(suite hash.HashSuite, r *ring.Ring, A structs.Matrix[ring.Poly], b structs.Vector[ring.Poly], h structs.Vector[ring.Poly], mu string, kappa int) ring.Poly { func LowNormHash(suite hash.HashSuite, r *ring.Ring, A structs.Matrix[ring.Poly], b structs.Vector[ring.Poly], h structs.Vector[ring.Poly], mu string, kappa int) ring.Poly {
s := hash.Resolve(suite) s := hash.Resolve(suite)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
+9 -9
View File
@@ -260,17 +260,17 @@ func TestGenerateRandomSeed(t *testing.T) {
} }
} }
// TestPulsarSHA3VsBLAKE3_DistinctOutput is the F22 fix surfaced as a test: // TestCoronaSHA3VsBLAKE3_DistinctOutput is the F22 fix surfaced as a test:
// the same byte-identical inputs must produce different bytes under // the same byte-identical inputs must produce different bytes under
// Pulsar-SHA3 and Pulsar-BLAKE3 across every Sign-path primitive. If two // Corona-SHA3 and Corona-BLAKE3 across every Sign-path primitive. If two
// suites collide here, customization tags or framing are broken. // suites collide here, customization tags or framing are broken.
func TestPulsarSHA3VsBLAKE3_DistinctOutput(t *testing.T) { func TestCoronaSHA3VsBLAKE3_DistinctOutput(t *testing.T) {
r, err := ring.NewRing(256, []uint64{8380417}) r, err := ring.NewRing(256, []uint64{8380417})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
sha3 := hash.NewPulsarSHA3() sha3 := hash.NewCoronaSHA3()
bl3 := hash.NewPulsarBLAKE3() bl3 := hash.NewCoronaBLAKE3()
prng, _ := sampling.NewPRNG() prng, _ := sampling.NewPRNG()
sampler := ring.NewUniformSampler(prng, r) sampler := ring.NewUniformSampler(prng, r)
@@ -402,15 +402,15 @@ func TestPulsarSHA3VsBLAKE3_DistinctOutput(t *testing.T) {
// //
// The legacy BLAKE3 KATs in cmd/corona_oracle_v2/ historically reflected // The legacy BLAKE3 KATs in cmd/corona_oracle_v2/ historically reflected
// raw blake3.New() framing in primitives/hash.go. After the suite // raw blake3.New() framing in primitives/hash.go. After the suite
// refactor, primitives now uses pulsarBLAKE3.PRF / pulsarBLAKE3.Hu / // refactor, primitives now uses coronaBLAKE3.PRF / coronaBLAKE3.Hu /
// pulsarBLAKE3.MAC which prepend customization tags and length-prefix — // coronaBLAKE3.MAC which prepend customization tags and length-prefix —
// so the BLAKE3 oracle output_hex no longer byte-matches pre-refactor // so the BLAKE3 oracle output_hex no longer byte-matches pre-refactor
// transcripts. New Pulsar-SHA3 KATs are not yet emitted. // transcripts. New Corona-SHA3 KATs are not yet emitted.
// //
// This is documented in pulsar/CHANGELOG.md as follow-up work. The test // This is documented in pulsar/CHANGELOG.md as follow-up work. The test
// here is a guard rail: it fails if anyone hand-edits the legacy BLAKE3 // here is a guard rail: it fails if anyone hand-edits the legacy BLAKE3
// JSON in tree before regeneration, so the C++ port team gets a loud // JSON in tree before regeneration, so the C++ port team gets a loud
// signal. // signal.
func TestKATsRegenerated(t *testing.T) { func TestKATsRegenerated(t *testing.T) {
t.Skip("legacy BLAKE3 KATs and new Pulsar-SHA3 KATs land as follow-up; see pulsar/CHANGELOG.md") t.Skip("legacy BLAKE3 KATs and new Corona-SHA3 KATs land as follow-up; see pulsar/CHANGELOG.md")
} }
+4 -4
View File
@@ -16,7 +16,7 @@ package reshare
// Activation message canonical bytes (signed by the new committee // Activation message canonical bytes (signed by the new committee
// under the UNCHANGED GroupKey): // under the UNCHANGED GroupKey):
// //
// "QUASAR-PULSAR-ACTIVATE-v1" || // "QUASAR-CORONA-ACTIVATE-v1" ||
// transcript_hash (32 bytes; from TranscriptInputs.Hash) // transcript_hash (32 bytes; from TranscriptInputs.Hash)
// reshare_transcript_hash (32 bytes; from ReshareTranscript.Hash) // reshare_transcript_hash (32 bytes; from ReshareTranscript.Hash)
@@ -61,7 +61,7 @@ func buildExchangeTranscriptParts(rt *ReshareTranscript) [][]byte {
return b[:] return b[:]
} }
parts := [][]byte{ parts := [][]byte{
[]byte("pulsar.reshare.exchange-transcript.v1"), []byte("corona.reshare.exchange-transcript.v1"),
} }
commitParties := make([]int, 0, len(rt.CommitDigests)) commitParties := make([]int, 0, len(rt.CommitDigests))
@@ -101,7 +101,7 @@ func buildExchangeTranscriptParts(rt *ReshareTranscript) [][]byte {
// threshold-signs to produce an activation cert. // threshold-signs to produce an activation cert.
func (a *ActivationMessage) SignableBytes(suite hash.HashSuite) []byte { func (a *ActivationMessage) SignableBytes(suite hash.HashSuite) []byte {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("QUASAR-PULSAR-ACTIVATE-v1") buf.WriteString("QUASAR-CORONA-ACTIVATE-v1")
t := a.Transcript.Hash(suite) t := a.Transcript.Hash(suite)
buf.Write(t[:]) buf.Write(t[:])
rth := a.ReshareTranscript.Hash(suite) rth := a.ReshareTranscript.Hash(suite)
@@ -123,7 +123,7 @@ var (
) )
// VerifyActivation runs the chain-level activation check. // VerifyActivation runs the chain-level activation check.
// suite=nil resolves to the production default (Pulsar-SHA3). // suite=nil resolves to the production default (Corona-SHA3).
func VerifyActivation( func VerifyActivation(
cert *ActivationCert, cert *ActivationCert,
localTranscriptHash [32]byte, localTranscriptHash [32]byte,
+1 -1
View File
@@ -31,7 +31,7 @@ func TestActivationMessageSignableBytesStable(t *testing.T) {
t.Fatal("SignableBytes non-deterministic") t.Fatal("SignableBytes non-deterministic")
} }
// Format: 25-byte personalization prefix + 32 + 32 = 89 bytes. // Format: 25-byte personalization prefix + 32 + 32 = 89 bytes.
const wantLen = len("QUASAR-PULSAR-ACTIVATE-v1") + 32 + 32 const wantLen = len("QUASAR-CORONA-ACTIVATE-v1") + 32 + 32
if len(b1) != wantLen { if len(b1) != wantLen {
t.Fatalf("unexpected SignableBytes length: %d (want %d)", len(b1), wantLen) t.Fatalf("unexpected SignableBytes length: %d (want %d)", len(b1), wantLen)
} }
+7 -7
View File
@@ -16,7 +16,7 @@ package reshare
// C_{i,k} = A_R · NTT(c_{i,k}) + B_R · NTT(r_{i,k}) // C_{i,k} = A_R · NTT(c_{i,k}) + B_R · NTT(r_{i,k})
// //
// The matrices A, B are derived from nothing-up-my-sleeve domain-separated // The matrices A, B are derived from nothing-up-my-sleeve domain-separated
// tags via the canonical Pulsar HashSuite XOF (cSHAKE256 under Pulsar-SHA3, // tags via the canonical Corona HashSuite XOF (cSHAKE256 under Corona-SHA3,
// BLAKE3 under the legacy suite). // BLAKE3 under the legacy suite).
import ( import (
@@ -40,8 +40,8 @@ import (
// from dkg2's tags so a DKG commit cannot be repurposed as a reshare // from dkg2's tags so a DKG commit cannot be repurposed as a reshare
// commit (and vice versa). // commit (and vice versa).
var ( var (
tagReshareA = []byte("pulsar.reshare.A.v1") tagReshareA = []byte("corona.reshare.A.v1")
tagReshareB = []byte("pulsar.reshare.B.v1") tagReshareB = []byte("corona.reshare.B.v1")
) )
// CommitParams holds the public matrices used to commit to and verify // CommitParams holds the public matrices used to commit to and verify
@@ -55,9 +55,9 @@ type CommitParams struct {
// NewCommitParams derives the commitment matrices from the canonical // NewCommitParams derives the commitment matrices from the canonical
// tags using the supplied HashSuite. suite=nil resolves to the // tags using the supplied HashSuite. suite=nil resolves to the
// production default (Pulsar-SHA3). Two suites with distinct IDs derive // production default (Corona-SHA3). Two suites with distinct IDs derive
// distinct matrices, so legacy BLAKE3 KATs cannot be replayed as // distinct matrices, so legacy BLAKE3 KATs cannot be replayed as
// Pulsar-SHA3 transcripts. // Corona-SHA3 transcripts.
func NewCommitParams(suite hash.HashSuite) (*CommitParams, error) { func NewCommitParams(suite hash.HashSuite) (*CommitParams, error) {
s := hash.Resolve(suite) s := hash.Resolve(suite)
r, err := ring.NewRing(1<<sign.LogN, []uint64{sign.Q}) r, err := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
@@ -235,11 +235,11 @@ func polyMulScalarNTTOnly(r *ring.Ring, p ring.Poly, s, q *big.Int) {
// CommitDigest returns the canonical 32-byte digest over a commit // CommitDigest returns the canonical 32-byte digest over a commit
// vector under the supplied HashSuite. suite=nil resolves to the // vector under the supplied HashSuite. suite=nil resolves to the
// production default (Pulsar-SHA3). // production default (Corona-SHA3).
func CommitDigest(commits []structs.Vector[ring.Poly], suite hash.HashSuite) [32]byte { func CommitDigest(commits []structs.Vector[ring.Poly], suite hash.HashSuite) [32]byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
parts := make([][]byte, 0, 1+len(commits)) parts := make([][]byte, 0, 1+len(commits))
parts = append(parts, []byte("pulsar.reshare.commit-digest.v1")) parts = append(parts, []byte("corona.reshare.commit-digest.v1"))
for _, v := range commits { for _, v := range commits {
var buf bytes.Buffer var buf bytes.Buffer
_, _ = v.WriteTo(&buf) _, _ = v.WriteTo(&buf)
+1 -1
View File
@@ -33,7 +33,7 @@ func TestCommitToPolyAndVerify(t *testing.T) {
const tThr = 3 const tThr = 3
// Sample (c_k, r_k) for k = 0..t-1 from a Gaussian PRNG. We use // Sample (c_k, r_k) for k = 0..t-1 from a Gaussian PRNG. We use
// the same Gaussian as Pulsar secrets. // the same Gaussian as Corona secrets.
prng, _ := sampling.NewKeyedPRNG([]byte("commit-test-prng")) prng, _ := sampling.NewKeyedPRNG([]byte("commit-test-prng"))
gauss := ring.NewGaussianSampler(prng, params.R, gauss := ring.NewGaussianSampler(prng, params.R,
ring.DiscreteGaussian{Sigma: sign.SigmaE, Bound: sign.BoundE}, false) ring.DiscreteGaussian{Sigma: sign.SigmaE, Bound: sign.BoundE}, false)
+3 -3
View File
@@ -136,11 +136,11 @@ type Complaint struct {
// //
// Format: // Format:
// //
// "pulsar.reshare.complaint.v1" || transcript || sender_id_be32 || // "corona.reshare.complaint.v1" || transcript || sender_id_be32 ||
// complainer_id_be32 || reason_u8 || evidence_len_be32 || evidence // complainer_id_be32 || reason_u8 || evidence_len_be32 || evidence
func (c *Complaint) Bytes() []byte { func (c *Complaint) Bytes() []byte {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("pulsar.reshare.complaint.v1") buf.WriteString("corona.reshare.complaint.v1")
buf.Write(c.TranscriptHash[:]) buf.Write(c.TranscriptHash[:])
var b4 [4]byte var b4 [4]byte
binary.BigEndian.PutUint32(b4[:], uint32(c.SenderID)) binary.BigEndian.PutUint32(b4[:], uint32(c.SenderID))
@@ -184,7 +184,7 @@ func (c *Complaint) Verify() error {
// final disqualification result. // final disqualification result.
func ComplaintHash(c *Complaint) [32]byte { func ComplaintHash(c *Complaint) [32]byte {
h := blake3.New() h := blake3.New()
_, _ = h.Write([]byte("pulsar.reshare.complaint-hash.v1")) _, _ = h.Write([]byte("corona.reshare.complaint-hash.v1"))
_, _ = h.Write(c.Bytes()) _, _ = h.Write(c.Bytes())
_, _ = h.Write(c.Signature) _, _ = h.Write(c.Signature)
var out [32]byte var out [32]byte
+5 -5
View File
@@ -7,7 +7,7 @@
// The activation cert is the post-reshare circuit-breaker; it must be // The activation cert is the post-reshare circuit-breaker; it must be
// produced over a deterministic byte stream that uniquely binds the // produced over a deterministic byte stream that uniquely binds the
// resharing transcript hash and the protocol-domain prefix // resharing transcript hash and the protocol-domain prefix
// "QUASAR-PULSAR-ACTIVATE-v1". This harness exercises the // "QUASAR-CORONA-ACTIVATE-v1". This harness exercises the
// SignableBytes encoder against arbitrary mutated inputs. // SignableBytes encoder against arbitrary mutated inputs.
package reshare package reshare
@@ -25,7 +25,7 @@ import (
// 1. SignableBytes never panics on arbitrary inputs. // 1. SignableBytes never panics on arbitrary inputs.
// 2. Output is deterministic: same struct → same bytes across calls. // 2. Output is deterministic: same struct → same bytes across calls.
// 3. The output begins with the canonical protocol prefix // 3. The output begins with the canonical protocol prefix
// "QUASAR-PULSAR-ACTIVATE-v1". // "QUASAR-CORONA-ACTIVATE-v1".
// 4. The output length is exactly len(prefix) + 32 + 32 = 89 bytes, // 4. The output length is exactly len(prefix) + 32 + 32 = 89 bytes,
// irrespective of how large the input fields are. // irrespective of how large the input fields are.
func FuzzActivationMessageSignableBytes(f *testing.F) { func FuzzActivationMessageSignableBytes(f *testing.F) {
@@ -35,8 +35,8 @@ func FuzzActivationMessageSignableBytes(f *testing.F) {
f.Add(seedActivationBytes("lux-testnet", "g0", 1, 2, "refresh", 5, 0, 1)) f.Add(seedActivationBytes("lux-testnet", "g0", 1, 2, "refresh", 5, 0, 1))
f.Add(seedActivationBytes("", "", 0, 0, "", 0, 0, 0)) f.Add(seedActivationBytes("", "", 0, 0, "", 0, 0, 0))
prefix := []byte("QUASAR-PULSAR-ACTIVATE-v1") prefix := []byte("QUASAR-CORONA-ACTIVATE-v1")
const wantLen = len("QUASAR-PULSAR-ACTIVATE-v1") + 32 + 32 const wantLen = len("QUASAR-CORONA-ACTIVATE-v1") + 32 + 32
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
msg := decodeFuzzActivation(raw) msg := decodeFuzzActivation(raw)
@@ -68,7 +68,7 @@ func FuzzActivationMessageSignableBytes(f *testing.F) {
// TestFuzzCorpus_ActivationReplay re-runs the seed corpus // TestFuzzCorpus_ActivationReplay re-runs the seed corpus
// deterministically for CI replay. // deterministically for CI replay.
func TestFuzzCorpus_ActivationReplay(t *testing.T) { func TestFuzzCorpus_ActivationReplay(t *testing.T) {
prefix := []byte("QUASAR-PULSAR-ACTIVATE-v1") prefix := []byte("QUASAR-CORONA-ACTIVATE-v1")
seeds := [][]byte{ seeds := [][]byte{
seedActivationBytes("lux-mainnet", "quasar-pq", 100, 101, "reshare", 3, 2, 0), seedActivationBytes("lux-mainnet", "quasar-pq", 100, 101, "reshare", 3, 2, 0),
seedActivationBytes("lux-testnet", "g0", 1, 2, "refresh", 5, 0, 1), seedActivationBytes("lux-testnet", "g0", 1, 2, "refresh", 5, 0, 1),
+4 -4
View File
@@ -56,7 +56,7 @@ func fuzzCommitDigestRecover(raw []byte) (err error) {
// strict: rejects truncated inputs, oversized evidence claims, and // strict: rejects truncated inputs, oversized evidence claims, and
// version-tag mismatches with a clean error rather than a panic. // version-tag mismatches with a clean error rather than a panic.
func parseComplaintBytes(raw []byte) (*Complaint, error) { func parseComplaintBytes(raw []byte) (*Complaint, error) {
const versionTag = "pulsar.reshare.complaint.v1" const versionTag = "corona.reshare.complaint.v1"
if len(raw) < len(versionTag)+32+4+4+1+4 { if len(raw) < len(versionTag)+32+4+4+1+4 {
return nil, fmt.Errorf("truncated: %d bytes", len(raw)) return nil, fmt.Errorf("truncated: %d bytes", len(raw))
} }
@@ -91,8 +91,8 @@ func addSmallSeeds(f *testing.F) {
f.Add([]byte{}) f.Add([]byte{})
f.Add([]byte{0x00}) f.Add([]byte{0x00})
f.Add(bytes.Repeat([]byte{0xff}, 32)) f.Add(bytes.Repeat([]byte{0xff}, 32))
f.Add(append([]byte("pulsar.reshare.complaint.v1"), bytes.Repeat([]byte{0x00}, 41)...)) f.Add(append([]byte("corona.reshare.complaint.v1"), bytes.Repeat([]byte{0x00}, 41)...))
f.Add(append([]byte("pulsar.reshare.complaint.v1"), f.Add(append([]byte("corona.reshare.complaint.v1"),
append(bytes.Repeat([]byte{0x00}, 41), append(bytes.Repeat([]byte{0x00}, 41),
[]byte{0xff, 0xff, 0xff, 0xff}...)...)) // huge evidence claim []byte{0xff, 0xff, 0xff, 0xff}...)...)) // huge evidence claim
} }
@@ -137,7 +137,7 @@ func TestFuzzCorpus_ReshareComplaintReplay(t *testing.T) {
for _, raw := range [][]byte{ for _, raw := range [][]byte{
{}, {},
{0x00}, {0x00},
append([]byte("pulsar.reshare.complaint.v1"), bytes.Repeat([]byte{0x00}, 41)...), append([]byte("corona.reshare.complaint.v1"), bytes.Repeat([]byte{0x00}, 41)...),
} { } {
_, _ = parseComplaintBytes(raw) _, _ = parseComplaintBytes(raw)
} }
+5 -5
View File
@@ -4,7 +4,7 @@
// Package reshare fuzz harness for transcript binding. // Package reshare fuzz harness for transcript binding.
// //
// Property anchor: proofs/definitions/transcript-binding.tex // Property anchor: proofs/definitions/transcript-binding.tex
// Definition ref:pulsar-transcript ("Pulsar TranscriptInputs.Hash") — // Definition ref:pulsar-transcript ("Corona TranscriptInputs.Hash") —
// the canonical TranscriptHash is collision-resistant, and any two // the canonical TranscriptHash is collision-resistant, and any two
// distinct field tuples yield distinct hashes by collision resistance // distinct field tuples yield distinct hashes by collision resistance
// of TupleHash256. // of TupleHash256.
@@ -41,8 +41,8 @@ func FuzzTranscriptInputsHash(f *testing.F) {
// Seed corpus: a few representative TranscriptInputs values pulled // Seed corpus: a few representative TranscriptInputs values pulled
// from the activation_oracle KAT shape. Each is encoded as a flat // from the activation_oracle KAT shape. Each is encoded as a flat
// byte stream the harness decodes into a TranscriptInputs. // byte stream the harness decodes into a TranscriptInputs.
f.Add(seedTranscriptBytes("lux-mainnet", "quasar-pq", 1, 100, 101, 11, 11, "Pulsar-SHA3", "v1", "reshare")) f.Add(seedTranscriptBytes("lux-mainnet", "quasar-pq", 1, 100, 101, 11, 11, "Corona-SHA3", "v1", "reshare"))
f.Add(seedTranscriptBytes("lux-testnet", "g0", 0, 1, 2, 3, 3, "Pulsar-BLAKE3", "v1", "refresh")) f.Add(seedTranscriptBytes("lux-testnet", "g0", 0, 1, 2, 3, 3, "Corona-BLAKE3", "v1", "refresh"))
f.Add(seedTranscriptBytes("", "", 0, 0, 0, 0, 0, "", "", "")) f.Add(seedTranscriptBytes("", "", 0, 0, 0, 0, 0, "", "", ""))
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
@@ -89,8 +89,8 @@ func FuzzTranscriptInputsHash(f *testing.F) {
// hash to stable values without invoking the native fuzzer. // hash to stable values without invoking the native fuzzer.
func TestFuzzCorpus_TranscriptReplay(t *testing.T) { func TestFuzzCorpus_TranscriptReplay(t *testing.T) {
seeds := [][]byte{ seeds := [][]byte{
seedTranscriptBytes("lux-mainnet", "quasar-pq", 1, 100, 101, 11, 11, "Pulsar-SHA3", "v1", "reshare"), seedTranscriptBytes("lux-mainnet", "quasar-pq", 1, 100, 101, 11, 11, "Corona-SHA3", "v1", "reshare"),
seedTranscriptBytes("lux-testnet", "g0", 0, 1, 2, 3, 3, "Pulsar-BLAKE3", "v1", "refresh"), seedTranscriptBytes("lux-testnet", "g0", 0, 1, 2, 3, 3, "Corona-BLAKE3", "v1", "refresh"),
seedTranscriptBytes("", "", 0, 0, 0, 0, 0, "", "", ""), seedTranscriptBytes("", "", 0, 0, 0, 0, 0, "", "", ""),
} }
for i, s := range seeds { for i, s := range seeds {
+8 -8
View File
@@ -3,7 +3,7 @@
package reshare package reshare
// KeyShare regeneration for Corona / Pulsar integration. // KeyShare regeneration for Corona / Corona integration.
// //
// The Reshare and Refresh kernels operate on bare Shamir shares — the // The Reshare and Refresh kernels operate on bare Shamir shares — the
// SkShare field of the production-grade // SkShare field of the production-grade
@@ -19,13 +19,13 @@ package reshare
// GroupKey *GroupKey // GroupKey *GroupKey
// } // }
// //
// All KDF derivations use the canonical Pulsar HashSuite (KMAC256 // All KDF derivations use the canonical Corona HashSuite (KMAC256
// under Pulsar-SHA3, keyed BLAKE3 under the legacy suite). Domain- // under Corona-SHA3, keyed BLAKE3 under the legacy suite). Domain-
// separation tags: // separation tags:
// //
// "pulsar.reshare.prf-seed.v1" — for Seeds // "corona.reshare.prf-seed.v1" — for Seeds
// "pulsar.reshare.mac-key.v1" — for MACKeys // "corona.reshare.mac-key.v1" — for MACKeys
// "pulsar.reshare.lambda-bind.v1" — bound into Lambda derivation when // "corona.reshare.lambda-bind.v1" — bound into Lambda derivation when
// the committee computes Lambdas // the committee computes Lambdas
// from a shared transcript hash. // from a shared transcript hash.
@@ -41,7 +41,7 @@ import (
"github.com/luxfi/lattice/v7/utils/structs" "github.com/luxfi/lattice/v7/utils/structs"
) )
// PartyKeyShare is the Pulsar-internal mirror of // PartyKeyShare is the Corona-internal mirror of
// corona/threshold.KeyShare. // corona/threshold.KeyShare.
type PartyKeyShare struct { type PartyKeyShare struct {
Index int Index int
@@ -120,7 +120,7 @@ func PartyKeyShareFromShare(
// KDFOutput derives a fixed-length output from a keying material under // KDFOutput derives a fixed-length output from a keying material under
// the supplied HashSuite's pairwise KDF. suite=nil resolves to the // the supplied HashSuite's pairwise KDF. suite=nil resolves to the
// production default (Pulsar-SHA3). // production default (Corona-SHA3).
// //
// The tag is folded into the chainID label with a `|` separator so two // The tag is folded into the chainID label with a `|` separator so two
// callers with distinct tags but the same remaining inputs always // callers with distinct tags but the same remaining inputs always
+4 -4
View File
@@ -1,7 +1,7 @@
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved. // Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package reshare — Gate 4 negative-transcript tests for the Pulsar // Package reshare — Gate 4 negative-transcript tests for the Corona
// VSR transcript and activation message (Mar-3-2026 PQ Consensus // VSR transcript and activation message (Mar-3-2026 PQ Consensus
// Architecture Freeze). // Architecture Freeze).
// //
@@ -63,7 +63,7 @@ func baselineTranscriptInputs() TranscriptInputs {
ThresholdNew: 13, ThresholdNew: 13,
GroupPublicKeyHash: [32]byte{0xa0, 0xa1, 0xa2, 0xa3, 0xa4}, GroupPublicKeyHash: [32]byte{0xa0, 0xa1, 0xa2, 0xa3, 0xa4},
NebulaRoot: [32]byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4}, NebulaRoot: [32]byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4},
HashSuiteID: "Pulsar-SHA3", HashSuiteID: "Corona-SHA3",
ImplementationVersion: "pulsar-go-1.0.0", ImplementationVersion: "pulsar-go-1.0.0",
Variant: "reshare", Variant: "reshare",
} }
@@ -88,7 +88,7 @@ func baselineActivationMessage() ActivationMessage {
} }
// honestThresholdVerifier returns a verifier closure that mimics the // honestThresholdVerifier returns a verifier closure that mimics the
// behaviour of a real Pulsar.Verify under an unchanged GroupKey: it // behaviour of a real Corona.Verify under an unchanged GroupKey: it
// accepts iff the bytes-to-be-signed equal the baseline activation // accepts iff the bytes-to-be-signed equal the baseline activation
// message's bytes-to-be-signed, and rejects everything else. // message's bytes-to-be-signed, and rejects everything else.
// //
@@ -139,7 +139,7 @@ func mutateTranscriptField(t *testing.T, base ActivationMessage, field string) A
case "nebula_root": case "nebula_root":
m.Transcript.NebulaRoot = [32]byte{0xee, 0xee, 0xee, 0xee} m.Transcript.NebulaRoot = [32]byte{0xee, 0xee, 0xee, 0xee}
case "hash_suite_id": case "hash_suite_id":
m.Transcript.HashSuiteID = "Pulsar-BLAKE3" m.Transcript.HashSuiteID = "Corona-BLAKE3"
case "implementation_version": case "implementation_version":
m.Transcript.ImplementationVersion = "pulsar-rs-2.0.0" m.Transcript.ImplementationVersion = "pulsar-rs-2.0.0"
case "variant": case "variant":
+9 -9
View File
@@ -20,7 +20,7 @@ package reshare
// //
// We use X25519 + Ed25519 here as the kernel KEX. The auth_kex_ij is // We use X25519 + Ed25519 here as the kernel KEX. The auth_kex_ij is
// derived via a transcript-bound mix of the X25519 output produced by // derived via a transcript-bound mix of the X25519 output produced by
// the canonical Pulsar HashSuite (cSHAKE256 under Pulsar-SHA3, BLAKE3 // the canonical Corona HashSuite (cSHAKE256 under Corona-SHA3, BLAKE3
// under the legacy suite). For the hybrid post-quantum mode, swap // under the legacy suite). For the hybrid post-quantum mode, swap
// X25519 for ML-KEM-768 + X25519 — out of scope for this kernel. // X25519 for ML-KEM-768 + X25519 — out of scope for this kernel.
@@ -75,7 +75,7 @@ func X25519Pair(privA, pubB []byte) ([]byte, error) {
// returns the auth_kex_ij value. The signed ephemeral protects against // returns the auth_kex_ij value. The signed ephemeral protects against
// active man-in-the-middle. // active man-in-the-middle.
// //
// suite=nil resolves to the production default (Pulsar-SHA3). // suite=nil resolves to the production default (Corona-SHA3).
func AuthenticatedKex( func AuthenticatedKex(
privIEph []byte, privIEph []byte,
pubJEph []byte, pubJEph []byte,
@@ -84,7 +84,7 @@ func AuthenticatedKex(
transcriptHash [32]byte, transcriptHash [32]byte,
suite hash.HashSuite, suite hash.HashSuite,
) ([]byte, error) { ) ([]byte, error) {
signedMsg := append([]byte("pulsar.reshare.kex-bind.v1"), transcriptHash[:]...) signedMsg := append([]byte("corona.reshare.kex-bind.v1"), transcriptHash[:]...)
signedMsg = append(signedMsg, pubJEph...) signedMsg = append(signedMsg, pubJEph...)
if !ed25519.Verify(jStaticKey, signedMsg, sigJEph) { if !ed25519.Verify(jStaticKey, signedMsg, sigJEph) {
return nil, errors.New("reshare: peer ephemeral signature invalid") return nil, errors.New("reshare: peer ephemeral signature invalid")
@@ -97,7 +97,7 @@ func AuthenticatedKex(
s := hash.Resolve(suite) s := hash.Resolve(suite)
out := s.TranscriptHash( out := s.TranscriptHash(
[]byte("pulsar.reshare.auth-kex.v1"), []byte("corona.reshare.auth-kex.v1"),
transcriptHash[:], transcriptHash[:],
shared, shared,
) )
@@ -111,13 +111,13 @@ func SignEphemeral(
pubEph []byte, pubEph []byte,
transcriptHash [32]byte, transcriptHash [32]byte,
) []byte { ) []byte {
signedMsg := append([]byte("pulsar.reshare.kex-bind.v1"), transcriptHash[:]...) signedMsg := append([]byte("corona.reshare.kex-bind.v1"), transcriptHash[:]...)
signedMsg = append(signedMsg, pubEph...) signedMsg = append(signedMsg, pubEph...)
return ed25519.Sign(priv, signedMsg) return ed25519.Sign(priv, signedMsg)
} }
// DeriveSeeds populates the per-pair PRF seed map for a committee of // DeriveSeeds populates the per-pair PRF seed map for a committee of
// size K. suite=nil resolves to the production default (Pulsar-SHA3). // size K. suite=nil resolves to the production default (Corona-SHA3).
// //
// eraID and generation are passed through to the suite's DerivePairwise. // eraID and generation are passed through to the suite's DerivePairwise.
// For pre-Bucket-B callsites that only have a single epochID, fold it // For pre-Bucket-B callsites that only have a single epochID, fold it
@@ -149,7 +149,7 @@ func DeriveSeeds(
} }
out[pair] = KDFOutput( out[pair] = KDFOutput(
suite, suite,
"pulsar.reshare.prf-seed.v1", "corona.reshare.prf-seed.v1",
keyMat, keyMat,
chainID, groupID, chainID, groupID,
eraID, generation, eraID, generation,
@@ -161,7 +161,7 @@ func DeriveSeeds(
return out, nil return out, nil
} }
// DeriveMACKeys mirrors DeriveSeeds with the "pulsar.reshare.mac-key.v1" // DeriveMACKeys mirrors DeriveSeeds with the "corona.reshare.mac-key.v1"
// tag and only off-diagonal entries (a party never MACs to itself). // tag and only off-diagonal entries (a party never MACs to itself).
// suite=nil resolves to the production default. // suite=nil resolves to the production default.
func DeriveMACKeys( func DeriveMACKeys(
@@ -182,7 +182,7 @@ func DeriveMACKeys(
} }
out[pair] = KDFOutput( out[pair] = KDFOutput(
suite, suite,
"pulsar.reshare.mac-key.v1", "corona.reshare.mac-key.v1",
keyMat, keyMat,
chainID, groupID, chainID, groupID,
eraID, generation, eraID, generation,
+5 -5
View File
@@ -2,7 +2,7 @@
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package reshare implements two distinct proactive secret-sharing // Package reshare implements two distinct proactive secret-sharing
// primitives for Pulsar lattice threshold signatures over the ring // primitives for Corona lattice threshold signatures over the ring
// R_q = Z_q[X]/(X^N+1): // R_q = Z_q[X]/(X^N+1):
// //
// 1. Refresh — same-committee zero-polynomial proactive update // 1. Refresh — same-committee zero-polynomial proactive update
@@ -39,7 +39,7 @@
// boundaries (`protocol/quasar/epoch.go: ReshareEpoch`). // boundaries (`protocol/quasar/epoch.go: ReshareEpoch`).
// //
// Both primitives leave the public key b = A·s + e (and its rounded // Both primitives leave the public key b = A·s + e (and its rounded
// form b̃) UNCHANGED. The genesis values (A, b, e) — and Pulsar's // form b̃) UNCHANGED. The genesis values (A, b, e) — and Corona's
// `bTilde` and Corona's `GroupKey` — are persistent for the entire // `bTilde` and Corona's `GroupKey` — are persistent for the entire
// group lineage. Only the share distribution changes. This is the // group lineage. Only the share distribution changes. This is the
// fundamental property that lets Quasar avoid running a full DKG on // fundamental property that lets Quasar avoid running a full DKG on
@@ -71,7 +71,7 @@
// quorum logic, deterministic disqualification of // quorum logic, deterministic disqualification of
// misbehaving senders. // misbehaving senders.
// - keyshare.go — Wraps reshared SkShare values into complete // - keyshare.go — Wraps reshared SkShare values into complete
// Corona/Pulsar `KeyShare` instances by // Corona/Corona `KeyShare` instances by
// regenerating Lambda, Seeds, MACKeys, and // regenerating Lambda, Seeds, MACKeys, and
// attaching the unchanged GroupKey pointer. // attaching the unchanged GroupKey pointer.
// - pairwise.go — Authenticated pairwise KEX (X25519 / ML-KEM // - pairwise.go — Authenticated pairwise KEX (X25519 / ML-KEM
@@ -160,7 +160,7 @@ type Share = structs.Vector[ring.Poly]
// //
// Reshare is deterministic given a deterministic randSource, which lets // Reshare is deterministic given a deterministic randSource, which lets
// the cmd/reshare_oracle/main.go KAT path reproduce results across // the cmd/reshare_oracle/main.go KAT path reproduce results across
// implementations (Go and luxcpp/crypto/pulsar/reshare). // implementations (Go and luxcpp/crypto/corona/reshare).
func Reshare( func Reshare(
r *ring.Ring, r *ring.Ring,
oldShares map[int]Share, oldShares map[int]Share,
@@ -428,7 +428,7 @@ func Refresh(
// within each c_{i,d} we draw the nVec polynomials in 0..nVec-1 // within each c_{i,d} we draw the nVec polynomials in 0..nVec-1
// order, and within each polynomial we draw the N coefficients in // order, and within each polynomial we draw the N coefficients in
// 0..N-1 order. This iteration order is locked by the Go reference // 0..N-1 order. This iteration order is locked by the Go reference
// and the C++ port at luxcpp/crypto/pulsar/reshare/. // and the C++ port at luxcpp/crypto/corona/reshare/.
for _, i := range parties { for _, i := range parties {
_ = i _ = i
// z_i has degree (t-1) and constant term 0. We store // z_i has degree (t-1) and constant term 0. We store
+1 -1
View File
@@ -515,7 +515,7 @@ func TestReshareWithSignGenShares(t *testing.T) {
secret := pickSecret(r, "primitives-bridge", testNVec) secret := pickSecret(r, "primitives-bridge", testNVec)
// Use the pulsar primitives' standard Shamir variant. // Use the corona primitives' standard Shamir variant.
tOld, nOld := 3, 5 tOld, nOld := 3, 5
primSharesMap := primitives.ShamirSecretSharingGeneral( primSharesMap := primitives.ShamirSecretSharingGeneral(
r, secret, tOld, nOld, r, secret, tOld, nOld,
+8 -8
View File
@@ -17,10 +17,10 @@ package reshare
// this hash. // this hash.
// //
// Domain separation. Every input field is unambiguously length-prefixed // Domain separation. Every input field is unambiguously length-prefixed
// (TupleHash framing under Pulsar-SHA3, hand-rolled length prefixes // (TupleHash framing under Corona-SHA3, hand-rolled length prefixes
// under the legacy Pulsar-BLAKE3 suite). The customization tag is // under the legacy Corona-BLAKE3 suite). The customization tag is
// "PULSAR-TRANSCRIPT-v1" — distinct from any DKG transcript tag (e.g. // "CORONA-TRANSCRIPT-v1" — distinct from any DKG transcript tag (e.g.
// "pulsar.dkg2.commit.v1") and from any Pulsar Sign transcript tag. // "corona.dkg2.commit.v1") and from any Corona Sign transcript tag.
import ( import (
"bytes" "bytes"
@@ -33,7 +33,7 @@ import (
// string. Kept as an exported symbol because some external consumers // string. Kept as an exported symbol because some external consumers
// (cross-language KAT loaders) reference it; the canonical transcript // (cross-language KAT loaders) reference it; the canonical transcript
// binding is now produced via the HashSuite layer. // binding is now produced via the HashSuite layer.
const TranscriptPersonalization = "pulsar.reshare.transcript.v1" const TranscriptPersonalization = "corona.reshare.transcript.v1"
// TranscriptInputs holds the public binding fields for one resharing // TranscriptInputs holds the public binding fields for one resharing
// invocation. All fields are mandatory; the transcript hash is well- // invocation. All fields are mandatory; the transcript hash is well-
@@ -60,7 +60,7 @@ type TranscriptInputs struct {
// Hash returns the canonical 32-byte transcript binding for the inputs // Hash returns the canonical 32-byte transcript binding for the inputs
// under the supplied HashSuite. nil resolves to the production default // under the supplied HashSuite. nil resolves to the production default
// (Pulsar-SHA3); pass hash.NewPulsarBLAKE3() to reproduce legacy bytes. // (Corona-SHA3); pass hash.NewCoronaBLAKE3() to reproduce legacy bytes.
func (t *TranscriptInputs) Hash(suite hash.HashSuite) [32]byte { func (t *TranscriptInputs) Hash(suite hash.HashSuite) [32]byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
parts := buildTranscriptParts(t) parts := buildTranscriptParts(t)
@@ -102,7 +102,7 @@ func buildTranscriptParts(t *TranscriptInputs) [][]byte {
} }
// ValidatorSetHash returns the canonical hash of a validator set. // ValidatorSetHash returns the canonical hash of a validator set.
// suite=nil resolves to the production default (Pulsar-SHA3). // suite=nil resolves to the production default (Corona-SHA3).
func ValidatorSetHash(publicKeys [][]byte, suite hash.HashSuite) [32]byte { func ValidatorSetHash(publicKeys [][]byte, suite hash.HashSuite) [32]byte {
s := hash.Resolve(suite) s := hash.Resolve(suite)
sorted := make([][]byte, len(publicKeys)) sorted := make([][]byte, len(publicKeys))
@@ -113,7 +113,7 @@ func ValidatorSetHash(publicKeys [][]byte, suite hash.HashSuite) [32]byte {
} }
} }
parts := make([][]byte, 0, 2+len(sorted)) parts := make([][]byte, 0, 2+len(sorted))
parts = append(parts, []byte("pulsar.reshare.validator-set.v1")) parts = append(parts, []byte("corona.reshare.validator-set.v1"))
var nBuf [4]byte var nBuf [4]byte
binary.BigEndian.PutUint32(nBuf[:], uint32(len(sorted))) binary.BigEndian.PutUint32(nBuf[:], uint32(len(sorted)))
parts = append(parts, nBuf[:]) parts = append(parts, nBuf[:])
+1 -1
View File
@@ -101,7 +101,7 @@ func TestValidatorSetHashUniqueness(t *testing.T) {
} }
// TestTranscriptHashCrossLanguageCompatibility — emits the // TestTranscriptHashCrossLanguageCompatibility — emits the
// canonical bytes the C++ port at luxcpp/crypto/pulsar/reshare/ MUST // canonical bytes the C++ port at luxcpp/crypto/corona/reshare/ MUST
// reproduce. Useful as a fixed-vector smoke test when porting. // reproduce. Useful as a fixed-vector smoke test when porting.
func TestTranscriptHashFixedVector(t *testing.T) { func TestTranscriptHashFixedVector(t *testing.T) {
in := TranscriptInputs{ in := TranscriptInputs{
+5 -5
View File
@@ -16,8 +16,8 @@ import (
// Party struct holds all state and methods for a party in the protocol. // Party struct holds all state and methods for a party in the protocol.
// //
// Suite is the hash profile this party uses for every primitives.* call. // Suite is the hash profile this party uses for every primitives.* call.
// NewParty defaults it to hash.Default() (Pulsar-SHA3). Operators that need // NewParty defaults it to hash.Default() (Corona-SHA3). Operators that need
// to interoperate with old transcripts can override with NewPulsarBLAKE3(). // to interoperate with old transcripts can override with NewCoronaBLAKE3().
type Party struct { type Party struct {
ID int ID int
Ring *ring.Ring Ring *ring.Ring
@@ -37,7 +37,7 @@ type Party struct {
} }
// NewParty initializes a new Party instance with the production hash suite // NewParty initializes a new Party instance with the production hash suite
// (Pulsar-SHA3). To use a different suite, set Party.Suite after construction // (Corona-SHA3). To use a different suite, set Party.Suite after construction
// or call NewPartyWithSuite. // or call NewPartyWithSuite.
func NewParty(id int, r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, sampler *ring.UniformSampler) *Party { func NewParty(id int, r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, sampler *ring.UniformSampler) *Party {
return NewPartyWithSuite(id, r, r_xi, r_nu, sampler, hash.Default()) return NewPartyWithSuite(id, r, r_xi, r_nu, sampler, hash.Default())
@@ -286,14 +286,14 @@ func (party *Party) SignFinalize(z map[int]structs.Vector[ring.Poly], A structs.
} }
// Verify verifies the correctness of the signature using the production // Verify verifies the correctness of the signature using the production
// hash suite (Pulsar-SHA3). For non-default suites use VerifyWithSuite. // hash suite (Corona-SHA3). For non-default suites use VerifyWithSuite.
// Note: This function does not modify its inputs - it creates copies where needed. // Note: This function does not modify its inputs - it creates copies where needed.
func Verify(r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, z structs.Vector[ring.Poly], A structs.Matrix[ring.Poly], mu string, bTilde structs.Vector[ring.Poly], c ring.Poly, roundedDelta structs.Vector[ring.Poly]) bool { func Verify(r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, z structs.Vector[ring.Poly], A structs.Matrix[ring.Poly], mu string, bTilde structs.Vector[ring.Poly], c ring.Poly, roundedDelta structs.Vector[ring.Poly]) bool {
return VerifyWithSuite(nil, r, r_xi, r_nu, z, A, mu, bTilde, c, roundedDelta) return VerifyWithSuite(nil, r, r_xi, r_nu, z, A, mu, bTilde, c, roundedDelta)
} }
// VerifyWithSuite is the suite-explicit form of Verify. suite=nil resolves // VerifyWithSuite is the suite-explicit form of Verify. suite=nil resolves
// to the production default (Pulsar-SHA3). // to the production default (Corona-SHA3).
func VerifyWithSuite(suite hash.HashSuite, r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, z structs.Vector[ring.Poly], A structs.Matrix[ring.Poly], mu string, bTilde structs.Vector[ring.Poly], c ring.Poly, roundedDelta structs.Vector[ring.Poly]) bool { func VerifyWithSuite(suite hash.HashSuite, r *ring.Ring, r_xi *ring.Ring, r_nu *ring.Ring, z structs.Vector[ring.Poly], A structs.Matrix[ring.Poly], mu string, bTilde structs.Vector[ring.Poly], c ring.Poly, roundedDelta structs.Vector[ring.Poly]) bool {
// Make a copy of z to avoid modifying the input signature // Make a copy of z to avoid modifying the input signature
zCopy := make(structs.Vector[ring.Poly], len(z)) zCopy := make(structs.Vector[ring.Poly], len(z))
+6 -6
View File
@@ -17,7 +17,7 @@ import (
// TestSignProtocolRoundTripUnderBothSuites runs Gen → SignRound1 → // TestSignProtocolRoundTripUnderBothSuites runs Gen → SignRound1 →
// SignRound2Preprocess → SignRound2 → SignFinalize → Verify under each // SignRound2Preprocess → SignRound2 → SignFinalize → Verify under each
// available HashSuite (Pulsar-SHA3 and Pulsar-BLAKE3) and asserts the // available HashSuite (Corona-SHA3 and Corona-BLAKE3) and asserts the
// resulting signature verifies. The Sign and Verify paths must thread the // resulting signature verifies. The Sign and Verify paths must thread the
// same suite end-to-end; mixing suites must fail verification. // same suite end-to-end; mixing suites must fail verification.
func TestSignProtocolRoundTripUnderBothSuites(t *testing.T) { func TestSignProtocolRoundTripUnderBothSuites(t *testing.T) {
@@ -31,8 +31,8 @@ func TestSignProtocolRoundTripUnderBothSuites(t *testing.T) {
name string name string
suite hash.HashSuite suite hash.HashSuite
}{ }{
{"Pulsar-SHA3", hash.NewPulsarSHA3()}, {"Corona-SHA3", hash.NewCoronaSHA3()},
{"Pulsar-BLAKE3", hash.NewPulsarBLAKE3()}, {"Corona-BLAKE3", hash.NewCoronaBLAKE3()},
} }
for _, sc := range suites { for _, sc := range suites {
@@ -44,13 +44,13 @@ func TestSignProtocolRoundTripUnderBothSuites(t *testing.T) {
// Cross-suite mixing: Sign under SHA3, Verify under BLAKE3 → must fail. // Cross-suite mixing: Sign under SHA3, Verify under BLAKE3 → must fail.
t.Run("CrossSuiteMixingRejected", func(t *testing.T) { t.Run("CrossSuiteMixingRejected", func(t *testing.T) {
sig, A, mu, b, c, delta, r, rXi, rNu := signOnly(t, hash.NewPulsarSHA3()) sig, A, mu, b, c, delta, r, rXi, rNu := signOnly(t, hash.NewCoronaSHA3())
// Same suite → ok. // Same suite → ok.
if !VerifyWithSuite(hash.NewPulsarSHA3(), r, rXi, rNu, sig, A, mu, b, c, delta) { if !VerifyWithSuite(hash.NewCoronaSHA3(), r, rXi, rNu, sig, A, mu, b, c, delta) {
t.Fatal("SHA3 self-verify must succeed") t.Fatal("SHA3 self-verify must succeed")
} }
// Different suite → must reject. // Different suite → must reject.
if VerifyWithSuite(hash.NewPulsarBLAKE3(), r, rXi, rNu, sig, A, mu, b, c, delta) { if VerifyWithSuite(hash.NewCoronaBLAKE3(), r, rXi, rNu, sig, A, mu, b, c, delta) {
t.Fatal("cross-suite verify (SHA3 sign / BLAKE3 verify) must reject — F22 separation violated") t.Fatal("cross-suite verify (SHA3 sign / BLAKE3 verify) must reject — F22 separation violated")
} }
}) })
+27 -27
View File
@@ -1,15 +1,15 @@
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved. // Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Pulsar threshold-kernel wire-format fuzz harnesses. // Corona threshold-kernel wire-format fuzz harnesses.
// //
// Each FuzzPulsar* harness fuzzes one external wire surface of the // Each FuzzCorona* harness fuzzes one external wire surface of the
// pulsar/threshold kernel: // corona/threshold kernel:
// //
// - FuzzPulsarSign1Round1Data — Round1Data.D matrix bytes (sign-1) // - FuzzCoronaSign1Round1Data — Round1Data.D matrix bytes (sign-1)
// - FuzzPulsarSign2Round2Data — Round2Data.Z vector bytes (sign-2) // - FuzzCoronaSign2Round2Data — Round2Data.Z vector bytes (sign-2)
// - FuzzPulsarKeyShareSerialize — KeyShare.SkShare wire bytes // - FuzzCoronaKeyShareSerialize — KeyShare.SkShare wire bytes
// - FuzzPulsarGroupKeySerialize — GroupKey.A,BTilde wire bytes // - FuzzCoronaGroupKeySerialize — GroupKey.A,BTilde wire bytes
// //
// Property: the corresponding decoder NEVER panics on arbitrary input. // Property: the corresponding decoder NEVER panics on arbitrary input.
// Companion TestFuzzCorpus_*Replay tests deterministically replay the // Companion TestFuzzCorpus_*Replay tests deterministically replay the
@@ -33,9 +33,9 @@ import (
// maxLatticeUintSliceLen mirrors warp/pulsar.MaxLatticeUintSliceLen and // maxLatticeUintSliceLen mirrors warp/pulsar.MaxLatticeUintSliceLen and
// bounds every length field a lattigo wire frame can declare. A // bounds every length field a lattigo wire frame can declare. A
// canonical Pulsar Poly has 256 coefficients per level; a Vector/Matrix // canonical Corona Poly has 256 coefficients per level; a Vector/Matrix
// has at most M*N = 8*32 = 256 polys. This cap is structural — frames // has at most M*N = 8*32 = 256 polys. This cap is structural — frames
// that declare more are not legitimate Pulsar protocol bytes. // that declare more are not legitimate Corona protocol bytes.
// //
// IMPORTANT: this duplicate cap exists because the upstream lattice // IMPORTANT: this duplicate cap exists because the upstream lattice
// library has TWO DoS surfaces: // library has TWO DoS surfaces:
@@ -45,7 +45,7 @@ import (
// NOT addressed by PR #3. A 9-byte input // NOT addressed by PR #3. A 9-byte input
// `\xad\x93\xd8\x5a\x00\x04\x00\x00\\` reads size=0x40005AD893AD // `\xad\x93\xd8\x5a\x00\x04\x00\x00\\` reads size=0x40005AD893AD
// (~70T entries) and OOMs the goroutine before the slice reader // (~70T entries) and OOMs the goroutine before the slice reader
// runs. Found by FuzzPulsarSign1Round1Data on 2026-05-04. // runs. Found by FuzzCoronaSign1Round1Data on 2026-05-04.
// //
// The walker below pre-validates the wire frame BEFORE handing it to // The walker below pre-validates the wire frame BEFORE handing it to
// lattigo, mirroring warp/pulsar.validateVectorPolyFrame. // lattigo, mirroring warp/pulsar.validateVectorPolyFrame.
@@ -102,7 +102,7 @@ func makeEmptyPolyVector(r *ring.Ring, length int) structs.Vector[ring.Poly] {
// fuzzMaxRawSize bounds the raw input handed to the lattigo decoder. // fuzzMaxRawSize bounds the raw input handed to the lattigo decoder.
// //
// We use 1024 bytes — much tighter than warp/pulsar's // We use 1024 bytes — much tighter than warp/corona's
// MaxPulseFrameSize=32KB — because Go's recover() cannot catch the // MaxPulseFrameSize=32KB — because Go's recover() cannot catch the
// runtime-fatal "goroutine stack exceeds 1000000000-byte limit" // runtime-fatal "goroutine stack exceeds 1000000000-byte limit"
// kill that an unpatched lattigo v7.0.1 produces on the // kill that an unpatched lattigo v7.0.1 produces on the
@@ -246,10 +246,10 @@ func addSmallSeeds(f *testing.F) {
f.Add(append([]byte{0x10, 0x00, 0x00, 0x00}, bytes.Repeat([]byte{0xcc}, 16)...)) f.Add(append([]byte{0x10, 0x00, 0x00, 0x00}, bytes.Repeat([]byte{0xcc}, 16)...))
} }
// FuzzPulsarSign1Round1Data fuzzes the Vector[Poly] decoder used to // FuzzCoronaSign1Round1Data fuzzes the Vector[Poly] decoder used to
// reconstruct a peer's Round-1 D matrix row. A panic here corresponds // reconstruct a peer's Round-1 D matrix row. A panic here corresponds
// to a malicious peer being able to take down a Round-1 receiver. // to a malicious peer being able to take down a Round-1 receiver.
func FuzzPulsarSign1Round1Data(f *testing.F) { func FuzzCoronaSign1Round1Data(f *testing.F) {
addSmallSeeds(f) addSmallSeeds(f)
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
@@ -264,10 +264,10 @@ func FuzzPulsarSign1Round1Data(f *testing.F) {
}) })
} }
// FuzzPulsarSign2Round2Data fuzzes the Vector[Poly] decoder used to // FuzzCoronaSign2Round2Data fuzzes the Vector[Poly] decoder used to
// reconstruct a peer's Round-2 Z vector. Matches the Round-1 surface // reconstruct a peer's Round-2 Z vector. Matches the Round-1 surface
// but exercises the smaller payload. // but exercises the smaller payload.
func FuzzPulsarSign2Round2Data(f *testing.F) { func FuzzCoronaSign2Round2Data(f *testing.F) {
addSmallSeeds(f) addSmallSeeds(f)
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
@@ -275,10 +275,10 @@ func FuzzPulsarSign2Round2Data(f *testing.F) {
}) })
} }
// FuzzPulsarKeyShareSerialize fuzzes the KeyShare.SkShare wire decoder. // FuzzCoronaKeyShareSerialize fuzzes the KeyShare.SkShare wire decoder.
// A KeyShare is the persisted output of DKG; corrupted on-disk shares // A KeyShare is the persisted output of DKG; corrupted on-disk shares
// must surface as errors, not panics. // must surface as errors, not panics.
func FuzzPulsarKeyShareSerialize(f *testing.F) { func FuzzCoronaKeyShareSerialize(f *testing.F) {
addSmallSeeds(f) addSmallSeeds(f)
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
@@ -286,9 +286,9 @@ func FuzzPulsarKeyShareSerialize(f *testing.F) {
}) })
} }
// FuzzPulsarGroupKeySerialize fuzzes the GroupKey.BTilde wire decoder // FuzzCoronaGroupKeySerialize fuzzes the GroupKey.BTilde wire decoder
// (the persistent public key portion of a group key). // (the persistent public key portion of a group key).
func FuzzPulsarGroupKeySerialize(f *testing.F) { func FuzzCoronaGroupKeySerialize(f *testing.F) {
addSmallSeeds(f) addSmallSeeds(f)
f.Fuzz(func(t *testing.T, raw []byte) { f.Fuzz(func(t *testing.T, raw []byte) {
@@ -296,11 +296,11 @@ func FuzzPulsarGroupKeySerialize(f *testing.F) {
}) })
} }
// TestFuzzCorpus_PulsarSign1Replay replays the canonical seed // TestFuzzCorpus_CoronaSign1Replay replays the canonical seed
// deterministically without invoking the fuzz engine. The Sign1 seed // deterministically without invoking the fuzz engine. The Sign1 seed
// is a serialized Matrix[Poly]; reading it into a fresh Matrix[Poly] // is a serialized Matrix[Poly]; reading it into a fresh Matrix[Poly]
// must succeed and return a non-zero byte count. // must succeed and return a non-zero byte count.
func TestFuzzCorpus_PulsarSign1Replay(t *testing.T) { func TestFuzzCorpus_CoronaSign1Replay(t *testing.T) {
mustKernelCeremony(t) mustKernelCeremony(t)
if len(kSignSeed) == 0 { if len(kSignSeed) == 0 {
t.Fatal("empty Sign1 seed") t.Fatal("empty Sign1 seed")
@@ -316,8 +316,8 @@ func TestFuzzCorpus_PulsarSign1Replay(t *testing.T) {
} }
} }
// TestFuzzCorpus_PulsarSign2Replay replays the Round-2 seed. // TestFuzzCorpus_CoronaSign2Replay replays the Round-2 seed.
func TestFuzzCorpus_PulsarSign2Replay(t *testing.T) { func TestFuzzCorpus_CoronaSign2Replay(t *testing.T) {
mustKernelCeremony(t) mustKernelCeremony(t)
if len(kRound2) == 0 { if len(kRound2) == 0 {
t.Fatal("empty Sign2 seed") t.Fatal("empty Sign2 seed")
@@ -332,9 +332,9 @@ func TestFuzzCorpus_PulsarSign2Replay(t *testing.T) {
} }
} }
// TestFuzzCorpus_PulsarKeyShareReplay confirms the KeyShare decoder // TestFuzzCorpus_CoronaKeyShareReplay confirms the KeyShare decoder
// accepts the canonical share bytes. // accepts the canonical share bytes.
func TestFuzzCorpus_PulsarKeyShareReplay(t *testing.T) { func TestFuzzCorpus_CoronaKeyShareReplay(t *testing.T) {
mustKernelCeremony(t) mustKernelCeremony(t)
var b bytes.Buffer var b bytes.Buffer
if _, err := kShares[0].SkShare.WriteTo(&b); err != nil { if _, err := kShares[0].SkShare.WriteTo(&b); err != nil {
@@ -350,9 +350,9 @@ func TestFuzzCorpus_PulsarKeyShareReplay(t *testing.T) {
} }
} }
// TestFuzzCorpus_PulsarGroupKeyReplay confirms the GroupKey decoder // TestFuzzCorpus_CoronaGroupKeyReplay confirms the GroupKey decoder
// accepts the canonical bytes. // accepts the canonical bytes.
func TestFuzzCorpus_PulsarGroupKeyReplay(t *testing.T) { func TestFuzzCorpus_CoronaGroupKeyReplay(t *testing.T) {
mustKernelCeremony(t) mustKernelCeremony(t)
var b bytes.Buffer var b bytes.Buffer
if _, err := kGroupKey.BTilde.WriteTo(&b); err != nil { if _, err := kGroupKey.BTilde.WriteTo(&b); err != nil {
+6 -6
View File
@@ -1,16 +1,16 @@
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved. // Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
// Package wire is pulsar's wire-format hardening boundary. // Package wire is corona's wire-format hardening boundary.
// //
// LP-107 Phase 4: pulsar consumes luxfi/math/codec for bounded // LP-107 Phase 4: corona consumes luxfi/math/codec for bounded
// decoding. Untrusted lattice wire data — Vector[Poly] frames from // decoding. Untrusted lattice wire data — Vector[Poly] frames from
// network peers, threshold-share blobs from disk, KAT replays — // network peers, threshold-share blobs from disk, KAT replays —
// flows through luxfi/math/codec.Reader so the bounded-decode contract // flows through luxfi/math/codec.Reader so the bounded-decode contract
// is centralised: no recursion, no hidden growth, no unbounded // is centralised: no recursion, no hidden growth, no unbounded
// allocation. // allocation.
// //
// Before this package, pulsar had its own validateVectorPolyFrame // Before this package, corona had its own validateVectorPolyFrame
// walker in threshold/fuzz_round_test.go (test-only). This package // walker in threshold/fuzz_round_test.go (test-only). This package
// replaces that with a production-grade equivalent that consumes the // replaces that with a production-grade equivalent that consumes the
// shared luxfi/math/codec substrate. // shared luxfi/math/codec substrate.
@@ -23,16 +23,16 @@ import (
"github.com/luxfi/math/codec" "github.com/luxfi/math/codec"
) )
// MaxLatticeUintSliceLen is pulsar's cap on lattigo Vector[Poly] / // MaxLatticeUintSliceLen is corona's cap on lattigo Vector[Poly] /
// Poly inner slice lengths — matches the value warp/pulsar.go already // Poly inner slice lengths — matches the value warp/pulsar.go already
// enforces and the cap at threshold/fuzz_round_test.go:52. // enforces and the cap at threshold/fuzz_round_test.go:52.
// //
// Pulsar canonical N = 256 and Q ≈ 2^48 (one-prime); a reasonable // Corona canonical N = 256 and Q ≈ 2^48 (one-prime); a reasonable
// vector cap is K_max * 1 levels * 256 coeffs = bounded under the // vector cap is K_max * 1 levels * 256 coeffs = bounded under the
// math/codec MaxFrameBytes. // math/codec MaxFrameBytes.
const MaxLatticeUintSliceLen = 4096 const MaxLatticeUintSliceLen = 4096
// LatticeWireLimits is the codec.Limits configuration pulsar uses for // LatticeWireLimits is the codec.Limits configuration corona uses for
// every lattice Vector[Poly] frame on the wire. // every lattice Vector[Poly] frame on the wire.
var LatticeWireLimits = codec.Limits{ var LatticeWireLimits = codec.Limits{
MaxFrameBytes: 16 * 1024 * 1024, MaxFrameBytes: 16 * 1024 * 1024,