mirror of
https://github.com/luxfi/corona.git
synced 2026-09-24 00:25:54 +00:00
style: gofmt -s across cmd/ + reshare/ + dkg2/ + keyera/ + threshold/
Whitespace-only struct-tag alignment changes. Closes the CI Lint gate that was failing across multiple recent commits on otherwise-green build+test runs.
This commit is contained in:
@@ -180,7 +180,7 @@ func emitGaussian(outDir string) error {
|
||||
|
||||
out := struct {
|
||||
Description string `json:"description"`
|
||||
Modulus uint64 `json:"modulus"`
|
||||
Modulus uint64 `json:"modulus"`
|
||||
N int `json:"n"`
|
||||
Entries []gaussianEntry `json:"entries"`
|
||||
}{
|
||||
@@ -362,23 +362,23 @@ func emitMontgomeryNTT(outDir string) error {
|
||||
|
||||
sub := r.SubRings[0]
|
||||
out := struct {
|
||||
Description string `json:"description"`
|
||||
Modulus uint64 `json:"modulus"`
|
||||
N int `json:"n"`
|
||||
BRedConstant [2]uint64 `json:"bred_constant"`
|
||||
MRedConstant uint64 `json:"mred_constant"`
|
||||
Mask uint64 `json:"mask"`
|
||||
PrimitiveRoot uint64 `json:"primitive_root"`
|
||||
NthRoot uint64 `json:"nth_root"`
|
||||
NInvMontgomery uint64 `json:"n_inv_montgomery"`
|
||||
RootsForward string `json:"roots_forward_hex"`
|
||||
RootsBackward string `json:"roots_backward_hex"`
|
||||
MForm []mformEntry `json:"mform"`
|
||||
IMForm []mformEntry `json:"imform"`
|
||||
NTT []mformEntry `json:"ntt"`
|
||||
INTT []mformEntry `json:"intt"`
|
||||
Mul []mulEntry `json:"mul_coeffs_montgomery"`
|
||||
MulAdd []mulAccumEntry `json:"mul_coeffs_montgomery_then_add"`
|
||||
Description string `json:"description"`
|
||||
Modulus uint64 `json:"modulus"`
|
||||
N int `json:"n"`
|
||||
BRedConstant [2]uint64 `json:"bred_constant"`
|
||||
MRedConstant uint64 `json:"mred_constant"`
|
||||
Mask uint64 `json:"mask"`
|
||||
PrimitiveRoot uint64 `json:"primitive_root"`
|
||||
NthRoot uint64 `json:"nth_root"`
|
||||
NInvMontgomery uint64 `json:"n_inv_montgomery"`
|
||||
RootsForward string `json:"roots_forward_hex"`
|
||||
RootsBackward string `json:"roots_backward_hex"`
|
||||
MForm []mformEntry `json:"mform"`
|
||||
IMForm []mformEntry `json:"imform"`
|
||||
NTT []mformEntry `json:"ntt"`
|
||||
INTT []mformEntry `json:"intt"`
|
||||
Mul []mulEntry `json:"mul_coeffs_montgomery"`
|
||||
MulAdd []mulAccumEntry `json:"mul_coeffs_montgomery_then_add"`
|
||||
}{
|
||||
Description: "Lattigo Ring at Q=0x1000000004A01, N=256. Constants and " +
|
||||
"forward/inverse NTT + Montgomery transform KATs. Inputs are uint64 " +
|
||||
@@ -488,10 +488,10 @@ func emitMatrixWire(outDir string) error {
|
||||
// ---------- KAT 5: BLAKE3 transcripts (Hash, LowNormHash, MAC, GaussianHash, PRF) ----------
|
||||
|
||||
type transcriptEntry struct {
|
||||
Name string `json:"name"`
|
||||
InputDesc string `json:"input_desc"`
|
||||
OutputHex string `json:"output_hex"`
|
||||
Extras []kvHex `json:"extras,omitempty"`
|
||||
Name string `json:"name"`
|
||||
InputDesc string `json:"input_desc"`
|
||||
OutputHex string `json:"output_hex"`
|
||||
Extras []kvHex `json:"extras,omitempty"`
|
||||
}
|
||||
|
||||
func emitTranscripts(outDir string) error {
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
// the C++ test is the "Go → C++" direction of the gate.
|
||||
//
|
||||
// Reverse direction (C++ → Go) is handled by:
|
||||
// * luxcpp/crypto/pulsar/cmd/cross_runtime_oracle/ (C++ writer)
|
||||
// * lux/pulsar/cmd/cross_runtime_verify/ (Go reader)
|
||||
// - luxcpp/crypto/pulsar/cmd/cross_runtime_oracle/ (C++ writer)
|
||||
// - lux/pulsar/cmd/cross_runtime_verify/ (Go reader)
|
||||
//
|
||||
// Determinism is required. Two runs with the same MasterSeed produce
|
||||
// byte-equal manifests AND byte-equal individual KAT JSON files.
|
||||
@@ -76,9 +76,9 @@ func main() {
|
||||
// reshare_oracle and dkg2_oracle don't expose --out; they write to
|
||||
// hardcoded locations in luxcpp/crypto. For the cross-runtime gate
|
||||
// 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"
|
||||
dkg2Path := "/Users/z/work/luxcpp/crypto/pulsar/dkg2/test/kat/dkg2_kat.json"
|
||||
dkg2Path := "/Users/z/work/luxcpp/crypto/pulsar/dkg2/test/kat/dkg2_kat.json"
|
||||
|
||||
files := []struct {
|
||||
name string
|
||||
|
||||
+22
-22
@@ -6,27 +6,27 @@
|
||||
//
|
||||
// For each (t, n) configuration in the catalogue, the oracle:
|
||||
//
|
||||
// 1. Derives a master 32-byte seed from MasterSeed | tag(n,t) via BLAKE3.
|
||||
// 2. From that master seed, derives one 32-byte sub-seed per party
|
||||
// (BLAKE3(master || "party" || BE32(i))).
|
||||
// 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" /
|
||||
// b"pulsar.dkg2.B.v1" via BLAKE3-XOF).
|
||||
// 4. Each party calls Round1WithSeed(party_seed[i]) to produce its
|
||||
// Commits, Shares (per recipient), and Blinds (per recipient).
|
||||
// 5. Each party calls Round2 with the assembled shares/blinds/commits.
|
||||
// 1. Derives a master 32-byte seed from MasterSeed | tag(n,t) via BLAKE3.
|
||||
// 2. From that master seed, derives one 32-byte sub-seed per party
|
||||
// (BLAKE3(master || "party" || BE32(i))).
|
||||
// 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" /
|
||||
// b"pulsar.dkg2.B.v1" via BLAKE3-XOF).
|
||||
// 4. Each party calls Round1WithSeed(party_seed[i]) to produce its
|
||||
// Commits, Shares (per recipient), and Blinds (per recipient).
|
||||
// 5. Each party calls Round2 with the assembled shares/blinds/commits.
|
||||
//
|
||||
// The KAT pins, per entry:
|
||||
// - For each party i: seed_hex[i] (sub-seed)
|
||||
// - For each party i: SHA-256(Commits[k].WriteTo) × t (commitment-vector hash)
|
||||
// - For each party i: BLAKE3(serialize(Commits)) (Round 1.5 digest)
|
||||
// - For each party i, each recipient j:
|
||||
// SHA-256(Shares[j].WriteTo)
|
||||
// SHA-256(Blinds[j].WriteTo)
|
||||
// SHA-256(Shares[j].WriteTo)
|
||||
// SHA-256(Blinds[j].WriteTo)
|
||||
// - For each party j (the recipient running Round2):
|
||||
// SHA-256(s_j.WriteTo)
|
||||
// SHA-256(u_j.WriteTo)
|
||||
// SHA-256(b_ped_j.WriteTo)
|
||||
// SHA-256(s_j.WriteTo)
|
||||
// SHA-256(u_j.WriteTo)
|
||||
// SHA-256(b_ped_j.WriteTo)
|
||||
//
|
||||
// Replay invariant: every party agrees on b_ped, so all b_ped hashes inside
|
||||
// one entry must be identical. The KAT records all n of them so the C++
|
||||
@@ -105,14 +105,14 @@ func hashMatrix(m structs.Matrix[ring.Poly]) string {
|
||||
// PartyEntry is one party's contribution to the KAT.
|
||||
type PartyEntry struct {
|
||||
PartyID int `json:"party_id"`
|
||||
SeedHex string `json:"seed_hex"` // Round1WithSeed input
|
||||
CommitsHashHex []string `json:"commits_hash_hex"` // length t
|
||||
CommitDigestHex string `json:"commit_digest_hex"` // BLAKE3 over serialized commits
|
||||
SharesHashHex []string `json:"shares_hash_hex"` // length n; index j = share to party j
|
||||
BlindsHashHex []string `json:"blinds_hash_hex"` // length n; index j = blind to party j
|
||||
SecretShareHash string `json:"secret_share_hash"` // SHA-256(s_j) after Round2
|
||||
BlindShareHash string `json:"blind_share_hash"` // SHA-256(u_j) after Round2
|
||||
BPedHash string `json:"b_ped_hash"` // SHA-256(b_ped) after Round2
|
||||
SeedHex string `json:"seed_hex"` // Round1WithSeed input
|
||||
CommitsHashHex []string `json:"commits_hash_hex"` // length t
|
||||
CommitDigestHex string `json:"commit_digest_hex"` // BLAKE3 over serialized commits
|
||||
SharesHashHex []string `json:"shares_hash_hex"` // length n; index j = share to party j
|
||||
BlindsHashHex []string `json:"blinds_hash_hex"` // length n; index j = blind to party j
|
||||
SecretShareHash string `json:"secret_share_hash"` // SHA-256(s_j) after Round2
|
||||
BlindShareHash string `json:"blind_share_hash"` // SHA-256(u_j) after Round2
|
||||
BPedHash string `json:"b_ped_hash"` // SHA-256(b_ped) after Round2
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
|
||||
+20
-20
@@ -6,14 +6,14 @@
|
||||
//
|
||||
// For each (t, n) configuration in the catalogue, the oracle:
|
||||
//
|
||||
// 1. Derives a master 32-byte seed from MasterSeed | tag(n,t) via BLAKE3.
|
||||
// 2. From that master seed, derives one 32-byte sub-seed per party
|
||||
// (BLAKE3(master || "party" || BE32(i))).
|
||||
// 3. Constructs n DKGSessions (party 0..n-1) — they all share the same
|
||||
// deterministic A matrix (seedKey = zeros, mirroring dkg.go:99-105).
|
||||
// 4. Each party calls Round1WithSeed(party_seed[i]) to produce its
|
||||
// Commits and per-recipient Shares.
|
||||
// 5. Each party calls Round2 with the assembled shares/commits.
|
||||
// 1. Derives a master 32-byte seed from MasterSeed | tag(n,t) via BLAKE3.
|
||||
// 2. From that master seed, derives one 32-byte sub-seed per party
|
||||
// (BLAKE3(master || "party" || BE32(i))).
|
||||
// 3. Constructs n DKGSessions (party 0..n-1) — they all share the same
|
||||
// deterministic A matrix (seedKey = zeros, mirroring dkg.go:99-105).
|
||||
// 4. Each party calls Round1WithSeed(party_seed[i]) to produce its
|
||||
// Commits and per-recipient Shares.
|
||||
// 5. Each party calls Round2 with the assembled shares/commits.
|
||||
//
|
||||
// The KAT pins, per entry:
|
||||
// - For each party i: seed_hex[i] (sub-seed)
|
||||
@@ -21,8 +21,8 @@
|
||||
// - For each party i, each recipient j: SHA-256(Shares[j].WriteTo) — the
|
||||
// share that party i computed for party j.
|
||||
// - For each party j (the recipient running Round2):
|
||||
// SHA-256(secret_share_j.WriteTo)
|
||||
// SHA-256(bTilde_j.WriteTo)
|
||||
// SHA-256(secret_share_j.WriteTo)
|
||||
// SHA-256(bTilde_j.WriteTo)
|
||||
//
|
||||
// Replay invariant: every party agrees on bTilde, so all bTilde hashes
|
||||
// inside one entry must be identical. The KAT records all n of them so
|
||||
@@ -91,19 +91,19 @@ func hashVectorBytes(r *ring.Ring, v structs.Vector[ring.Poly]) (string, []byte)
|
||||
// PartyEntry is one party's contribution to the KAT.
|
||||
type PartyEntry struct {
|
||||
PartyID int `json:"party_id"`
|
||||
SeedHex string `json:"seed_hex"` // Round1WithSeed input
|
||||
CommitsHashHex []string `json:"commits_hash_hex"` // length t
|
||||
SharesHashHex []string `json:"shares_hash_hex"` // length n; index j = share for party j
|
||||
SecretShareHash string `json:"secret_share_hash"` // SHA-256 of secret_share_j (after Round2)
|
||||
BTildeHash string `json:"btilde_hash"` // SHA-256 of bTilde_j (after Round2)
|
||||
SeedHex string `json:"seed_hex"` // Round1WithSeed input
|
||||
CommitsHashHex []string `json:"commits_hash_hex"` // length t
|
||||
SharesHashHex []string `json:"shares_hash_hex"` // length n; index j = share for party j
|
||||
SecretShareHash string `json:"secret_share_hash"` // SHA-256 of secret_share_j (after Round2)
|
||||
BTildeHash string `json:"btilde_hash"` // SHA-256 of bTilde_j (after Round2)
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
MasterSeedHex string `json:"master_seed_hex"`
|
||||
AHashHex string `json:"a_hash_hex"` // SHA-256(A.WriteTo) — same for all parties
|
||||
Parties []PartyEntry `json:"parties"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
MasterSeedHex string `json:"master_seed_hex"`
|
||||
AHashHex string `json:"a_hash_hex"` // SHA-256(A.WriteTo) — same for all parties
|
||||
Parties []PartyEntry `json:"parties"`
|
||||
}
|
||||
|
||||
type OracleOut struct {
|
||||
|
||||
+20
-20
@@ -31,8 +31,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Q uint64 = 0x1000000004A01
|
||||
BsquareDecStr = "184960669042442604975662780477"
|
||||
Q uint64 = 0x1000000004A01
|
||||
BsquareDecStr = "184960669042442604975662780477"
|
||||
)
|
||||
|
||||
func qBig() *big.Int { return new(big.Int).SetUint64(Q) }
|
||||
@@ -181,18 +181,18 @@ type L2Entry struct {
|
||||
}
|
||||
|
||||
type L2OracleOut struct {
|
||||
Description string `json:"description"`
|
||||
Q uint64 `json:"q"`
|
||||
BsquareDec string `json:"bsquare_dec"`
|
||||
Entries []L2Entry `json:"entries"`
|
||||
Description string `json:"description"`
|
||||
Q uint64 `json:"q"`
|
||||
BsquareDec string `json:"bsquare_dec"`
|
||||
Entries []L2Entry `json:"entries"`
|
||||
}
|
||||
|
||||
type FrcEntry struct {
|
||||
Name string `json:"name"`
|
||||
Rows int `json:"rows"`
|
||||
Cols int `json:"cols"`
|
||||
Matrix [][]uint64 `json:"matrix"`
|
||||
FullRank bool `json:"full_rank"`
|
||||
Name string `json:"name"`
|
||||
Rows int `json:"rows"`
|
||||
Cols int `json:"cols"`
|
||||
Matrix [][]uint64 `json:"matrix"`
|
||||
FullRank bool `json:"full_rank"`
|
||||
}
|
||||
|
||||
type FrcOracleOut struct {
|
||||
@@ -216,16 +216,16 @@ func emitL2Norm(outDir string) error {
|
||||
}{
|
||||
{"empty", 0, 0},
|
||||
{"single_zero", 1, 0},
|
||||
{"small_random", 64, 0}, // pure deterministic-random
|
||||
{"small_random", 64, 0}, // pure deterministic-random
|
||||
{"medium_random", 256, 0},
|
||||
{"large_random", 1024, 0},
|
||||
{"under_bound_64", 64, 1 << 30}, // ~ 64 * 2^60 = 2^66, well under
|
||||
{"under_bound_256", 256, 1 << 30}, // ~ 2^68, still well under
|
||||
{"under_bound_3840", 3840, 1 << 25}, // typical Sign vector size; under
|
||||
{"near_bound", 3840, 1 << 31}, // 3840 * 2^62 = 2^74, way under still
|
||||
{"over_bound_small", 4, Q / 2}, // each squared ~Q²/4, 4 of them ≈ Q²
|
||||
{"under_bound_64", 64, 1 << 30}, // ~ 64 * 2^60 = 2^66, well under
|
||||
{"under_bound_256", 256, 1 << 30}, // ~ 2^68, still well under
|
||||
{"under_bound_3840", 3840, 1 << 25}, // typical Sign vector size; under
|
||||
{"near_bound", 3840, 1 << 31}, // 3840 * 2^62 = 2^74, way under still
|
||||
{"over_bound_small", 4, Q / 2}, // each squared ~Q²/4, 4 of them ≈ Q²
|
||||
{"over_bound", 1024, Q / 2},
|
||||
{"max_centered", 8, Q / 2}, // edge: 8 * (Q/2)^2 ≈ 2^97, near bound
|
||||
{"max_centered", 8, Q / 2}, // edge: 8 * (Q/2)^2 ≈ 2^97, near bound
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -265,11 +265,11 @@ func emitFullRankCheck(outDir string) error {
|
||||
{"square_5x5", 5, 5, false},
|
||||
{"square_8x8", 8, 8, false},
|
||||
{"wide_5x9", 5, 9, false},
|
||||
{"wide_8x47", 8, 47, false}, // typical Corona shape (M × Dbar, but ignore col-0 for total cols)
|
||||
{"wide_8x47", 8, 47, false}, // typical Corona shape (M × Dbar, but ignore col-0 for total cols)
|
||||
{"wide_8x12", 8, 12, false},
|
||||
{"deficient_5x5", 5, 5, true},
|
||||
{"deficient_8x10", 8, 10, true},
|
||||
{"tall_3x2", 3, 2, false}, // taller than wide — likely deficient by Q-rank
|
||||
{"tall_3x2", 3, 2, false}, // taller than wide — likely deficient by Q-rank
|
||||
{"identity_4", 4, 4, false},
|
||||
}
|
||||
|
||||
|
||||
@@ -50,17 +50,17 @@ import (
|
||||
// ReadOp is one (n) read from the precomputed buffer; the captured bytes
|
||||
// are the expected output.
|
||||
type ReadOp struct {
|
||||
N int `json:"n"` // number of bytes pulled
|
||||
OutHex string `json:"out_hex"` // 2*n hex chars
|
||||
N int `json:"n"` // number of bytes pulled
|
||||
OutHex string `json:"out_hex"` // 2*n hex chars
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Name string `json:"name"`
|
||||
KeyHex string `json:"key_hex"` // BLAKE3 input bytes
|
||||
BufferSize int `json:"buffer_size"` // total precomputed bytes
|
||||
BufferHex string `json:"buffer_hex"` // full buffer (sanity-check)
|
||||
Reads []ReadOp `json:"reads"`
|
||||
FinalIndex int `json:"final_index"` // sum of all n_i (must == buffer cursor at end)
|
||||
Name string `json:"name"`
|
||||
KeyHex string `json:"key_hex"` // BLAKE3 input bytes
|
||||
BufferSize int `json:"buffer_size"` // total precomputed bytes
|
||||
BufferHex string `json:"buffer_hex"` // full buffer (sanity-check)
|
||||
Reads []ReadOp `json:"reads"`
|
||||
FinalIndex int `json:"final_index"` // sum of all n_i (must == buffer cursor at end)
|
||||
}
|
||||
|
||||
type OracleOut struct {
|
||||
|
||||
@@ -50,8 +50,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Q uint64 = 0x1000000004A01
|
||||
N = 256
|
||||
Q uint64 = 0x1000000004A01
|
||||
N = 256
|
||||
// QByteLen is the number of bytes returned by big.Int.Bytes() for Q.
|
||||
// Q = 0x1000000004A01 has 7 bytes (high byte 0x01).
|
||||
QByteLen = 7
|
||||
|
||||
@@ -35,10 +35,10 @@ const (
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Name string `json:"name"`
|
||||
SeedHex string `json:"seed_hex"`
|
||||
NumPolys int `json:"num_polys"`
|
||||
PolysHex []string `json:"polys_hex"`
|
||||
Name string `json:"name"`
|
||||
SeedHex string `json:"seed_hex"`
|
||||
NumPolys int `json:"num_polys"`
|
||||
PolysHex []string `json:"polys_hex"`
|
||||
}
|
||||
|
||||
type OracleOut struct {
|
||||
@@ -87,10 +87,10 @@ func main() {
|
||||
{"single_poly_beta", "beta", 1},
|
||||
{"two_polys_a", "two_a", 2},
|
||||
{"two_polys_b", "two_b", 2},
|
||||
{"five_polys", "five", 5}, // exercises buffer refill (5 * 256 * 8 = 10240B > 1024B buffer)
|
||||
{"five_polys", "five", 5}, // exercises buffer refill (5 * 256 * 8 = 10240B > 1024B buffer)
|
||||
{"eight_polys", "eight", 8},
|
||||
{"thirteen_polys", "thirteen", 13}, // larger refill count
|
||||
{"matrix_7x8", "matrix_7x8", 56}, // M*N_vec scale
|
||||
{"matrix_7x8", "matrix_7x8", 56}, // M*N_vec scale
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
+23
-23
@@ -12,14 +12,14 @@
|
||||
// - t_old, n_old, t_new, n_new : protocol parameters
|
||||
// - old_set, new_set : 1-indexed party IDs
|
||||
// - secret_polys_hex [poly_count] : the master secret s before sharing
|
||||
// (4096 hex chars = 256 uint64 BE per poly)
|
||||
// (4096 hex chars = 256 uint64 BE per poly)
|
||||
// - old_shamir_seed_hex : deterministic seed for the
|
||||
// old Shamir random a_1..a_{t_old-1}
|
||||
// (BLAKE3 XOF, q_byte_len bytes per draw)
|
||||
// old Shamir random a_1..a_{t_old-1}
|
||||
// (BLAKE3 XOF, q_byte_len bytes per draw)
|
||||
// - reshare_rng_seed_hex : deterministic seed for the
|
||||
// reshare random higher-degree
|
||||
// coefficients (SHA-256 counter
|
||||
// mode, see counterRand)
|
||||
// reshare random higher-degree
|
||||
// coefficients (SHA-256 counter
|
||||
// mode, see counterRand)
|
||||
// - old_shares_hex [n_old] : Shamir shares of s for old set
|
||||
// - new_shares_hex [n_new] : reshared shares for new set
|
||||
// - old_share_sha256_hex [n_old] : sha256 of each old party's full share
|
||||
@@ -118,21 +118,21 @@ func (c *counterRand) Read(p []byte) (int, error) {
|
||||
var _ io.Reader = (*counterRand)(nil)
|
||||
|
||||
type Entry struct {
|
||||
Label string `json:"label"`
|
||||
Variant string `json:"variant"` // "reshare" or "refresh"
|
||||
TOld int `json:"t_old"`
|
||||
NOld int `json:"n_old"`
|
||||
TNew int `json:"t_new"`
|
||||
NNew int `json:"n_new"`
|
||||
OldSet []int `json:"old_set"`
|
||||
NewSet []int `json:"new_set"`
|
||||
SecretPolysHex []string `json:"secret_polys_hex"`
|
||||
OldShamirSeedHex string `json:"old_shamir_seed_hex"`
|
||||
ReshareRngSeedHex string `json:"reshare_rng_seed_hex"`
|
||||
OldSharesHex []string `json:"old_shares_hex"`
|
||||
NewSharesHex []string `json:"new_shares_hex"`
|
||||
OldShareSHA256Hex []string `json:"old_share_sha256_hex"`
|
||||
NewShareSHA256Hex []string `json:"new_share_sha256_hex"`
|
||||
Label string `json:"label"`
|
||||
Variant string `json:"variant"` // "reshare" or "refresh"
|
||||
TOld int `json:"t_old"`
|
||||
NOld int `json:"n_old"`
|
||||
TNew int `json:"t_new"`
|
||||
NNew int `json:"n_new"`
|
||||
OldSet []int `json:"old_set"`
|
||||
NewSet []int `json:"new_set"`
|
||||
SecretPolysHex []string `json:"secret_polys_hex"`
|
||||
OldShamirSeedHex string `json:"old_shamir_seed_hex"`
|
||||
ReshareRngSeedHex string `json:"reshare_rng_seed_hex"`
|
||||
OldSharesHex []string `json:"old_shares_hex"`
|
||||
NewSharesHex []string `json:"new_shares_hex"`
|
||||
OldShareSHA256Hex []string `json:"old_share_sha256_hex"`
|
||||
NewShareSHA256Hex []string `json:"new_share_sha256_hex"`
|
||||
}
|
||||
|
||||
type OracleOut struct {
|
||||
@@ -156,8 +156,8 @@ type config struct {
|
||||
variant string // "reshare" (default) or "refresh"
|
||||
tOld int
|
||||
nOld int
|
||||
tNew int // ignored for refresh (= tOld)
|
||||
newSet []int // 1-indexed party IDs (for refresh, this is the unchanged committee)
|
||||
tNew int // ignored for refresh (= tOld)
|
||||
newSet []int // 1-indexed party IDs (for refresh, this is the unchanged committee)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
+2
-2
@@ -126,8 +126,8 @@ func (r ComplaintReason) String() string {
|
||||
// malformed structure.
|
||||
type Complaint struct {
|
||||
TranscriptHash [32]byte
|
||||
SenderID int // 0-indexed party ID of the misbehaving dealer
|
||||
ComplainerID int // 0-indexed party ID of the complainer
|
||||
SenderID int // 0-indexed party ID of the misbehaving dealer
|
||||
ComplainerID int // 0-indexed party ID of the complainer
|
||||
Reason ComplaintReason
|
||||
Evidence []byte // canonical-serialized evidence (see above)
|
||||
Signature []byte // Ed25519 signature over Bytes() under ComplainerID's wire key
|
||||
|
||||
+5
-5
@@ -373,11 +373,11 @@ func (d *DKGSession) Round1() (*Round1Output, error) {
|
||||
//
|
||||
// Sampling order (BYTE-PINNED — must match the C++ port):
|
||||
//
|
||||
// 1. KeyedPRNG(seed) → Gaussian sampler (σ_E, β_E).
|
||||
// 2. For k = 0..t-1: sample c_{i,k} (Nvec polys, standard form).
|
||||
// 3. For k = 0..t-1: sample r_{i,k} (Nvec polys, standard form).
|
||||
// 4. Compute commits[k] = A*NTT(c_k) + B*NTT(r_k).
|
||||
// 5. Compute shares[j], blinds[j] via Horner over the (j+1) point.
|
||||
// 1. KeyedPRNG(seed) → Gaussian sampler (σ_E, β_E).
|
||||
// 2. For k = 0..t-1: sample c_{i,k} (Nvec polys, standard form).
|
||||
// 3. For k = 0..t-1: sample r_{i,k} (Nvec polys, standard form).
|
||||
// 4. Compute commits[k] = A*NTT(c_k) + B*NTT(r_k).
|
||||
// 5. Compute shares[j], blinds[j] via Horner over the (j+1) point.
|
||||
//
|
||||
// The two-pass sample order (all c's first, then all r's) matters for byte
|
||||
// equality across the Go reference and the C++ port.
|
||||
|
||||
@@ -7,23 +7,23 @@
|
||||
// These tests pin the era-pinning property of the HashSuite (per
|
||||
// proofs/pulsar/hash-suite-separation.tex Remark on era-pinning):
|
||||
//
|
||||
// A. Bootstrap pins suite. The HashSuiteID recorded on a freshly
|
||||
// bootstrapped era equals exactly the ID of the supplied
|
||||
// HashSuite, and the field is read-only on the returned KeyEra.
|
||||
// B. Reshare cannot change suite. The Reshare API does not accept a
|
||||
// HashSuite parameter (verified at the type level by reading
|
||||
// reshare API), and the propagated state's HashSuiteID equals the
|
||||
// era's HashSuiteID byte-for-byte.
|
||||
// C. Reanchor MAY change suite. ReanchorWithSuite opens a new era
|
||||
// with a fresh GroupKey and a fresh HashSuiteID; the prior era's
|
||||
// HashSuiteID is unchanged.
|
||||
// A. Bootstrap pins suite. The HashSuiteID recorded on a freshly
|
||||
// bootstrapped era equals exactly the ID of the supplied
|
||||
// HashSuite, and the field is read-only on the returned KeyEra.
|
||||
// B. Reshare cannot change suite. The Reshare API does not accept a
|
||||
// HashSuite parameter (verified at the type level by reading
|
||||
// reshare API), and the propagated state's HashSuiteID equals the
|
||||
// era's HashSuiteID byte-for-byte.
|
||||
// C. Reanchor MAY change suite. ReanchorWithSuite opens a new era
|
||||
// with a fresh GroupKey and a fresh HashSuiteID; the prior era's
|
||||
// HashSuiteID is unchanged.
|
||||
//
|
||||
// Citations (canonical proof bucket):
|
||||
//
|
||||
// proofs/definitions/transcript-binding.tex
|
||||
// Definition ref:pulsar-transcript
|
||||
// proofs/pulsar/hash-suite-separation.tex
|
||||
// Theorem ref:hash-suite-separation
|
||||
// proofs/definitions/transcript-binding.tex
|
||||
// Definition ref:pulsar-transcript
|
||||
// proofs/pulsar/hash-suite-separation.tex
|
||||
// Theorem ref:hash-suite-separation
|
||||
package keyera
|
||||
|
||||
import (
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestReshareTranscriptHashStable(t *testing.T) {
|
||||
1: {0x11},
|
||||
5: {0x55},
|
||||
},
|
||||
ComplaintHashes: [][32]byte{{0x99}, {0x88}},
|
||||
ComplaintHashes: [][32]byte{{0x99}, {0x88}},
|
||||
DisqualifiedSenders: []int{4, 2},
|
||||
QualifiedQuorum: []int{1, 3, 5},
|
||||
}
|
||||
|
||||
@@ -123,11 +123,11 @@ func (r ComplaintReason) String() string {
|
||||
// - For ComplaintMalformedCommit: the malformed commit vector.
|
||||
type Complaint struct {
|
||||
TranscriptHash [32]byte
|
||||
SenderID int // 1-indexed party ID of the misbehaving dealer
|
||||
ComplainerID int // 1-indexed party ID of the complainer
|
||||
SenderID int // 1-indexed party ID of the misbehaving dealer
|
||||
ComplainerID int // 1-indexed party ID of the complainer
|
||||
Reason ComplaintReason
|
||||
Evidence []byte // canonical-serialized evidence (see above)
|
||||
Signature []byte // Ed25519 signature over Bytes() under ComplainerID's wire key
|
||||
Evidence []byte // canonical-serialized evidence (see above)
|
||||
Signature []byte // Ed25519 signature over Bytes() under ComplainerID's wire key
|
||||
ComplainerKey ed25519.PublicKey // public key for verification
|
||||
}
|
||||
|
||||
|
||||
@@ -23,19 +23,19 @@ import (
|
||||
// TestFullReshareThenKeyShareThenSign — production-relevance integration
|
||||
// test that exercises Correction 3:
|
||||
//
|
||||
// 1. Genesis: create (A, s, e, b̃) and standard-Shamir shares for an
|
||||
// OLD committee.
|
||||
// 2. The OLD committee runs sign once with bTilde to confirm the
|
||||
// genesis is sound.
|
||||
// 3. Resharing: rotate the share distribution onto a NEW committee
|
||||
// via Reshare. NO trusted dealer.
|
||||
// 4. The NEW committee establishes pairwise KEX → DeriveSeeds /
|
||||
// DeriveMACKeys and constructs full PartyKeyShare instances via
|
||||
// PartyKeyShareFromShare (Lambda + Seeds + MACKeys + GroupKey
|
||||
// pointer all populated).
|
||||
// 5. The NEW committee drives sign.SignRound1/Round2/Finalize,
|
||||
// and the resulting signature MUST verify against the
|
||||
// UNCHANGED bTilde from step 1.
|
||||
// 1. Genesis: create (A, s, e, b̃) and standard-Shamir shares for an
|
||||
// OLD committee.
|
||||
// 2. The OLD committee runs sign once with bTilde to confirm the
|
||||
// genesis is sound.
|
||||
// 3. Resharing: rotate the share distribution onto a NEW committee
|
||||
// via Reshare. NO trusted dealer.
|
||||
// 4. The NEW committee establishes pairwise KEX → DeriveSeeds /
|
||||
// DeriveMACKeys and constructs full PartyKeyShare instances via
|
||||
// PartyKeyShareFromShare (Lambda + Seeds + MACKeys + GroupKey
|
||||
// pointer all populated).
|
||||
// 5. The NEW committee drives sign.SignRound1/Round2/Finalize,
|
||||
// and the resulting signature MUST verify against the
|
||||
// UNCHANGED bTilde from step 1.
|
||||
//
|
||||
// This is the test that proves the brief's Correction 3 is satisfied:
|
||||
// resharing only SkShare is not enough — Lambda, Seeds, MACKeys, and
|
||||
@@ -122,8 +122,8 @@ func TestFullReshareThenKeyShareThenSign(t *testing.T) {
|
||||
wireKeys[i] = wirePub[i]
|
||||
}
|
||||
tIn := TranscriptInputs{
|
||||
ChainID: []byte("lux-mainnet-test"),
|
||||
GroupID: []byte("quasar-pq-test"),
|
||||
ChainID: []byte("lux-mainnet-test"),
|
||||
GroupID: []byte("quasar-pq-test"),
|
||||
OldEpochID: 1, NewEpochID: 2,
|
||||
OldSetHash: ValidatorSetHash(wireKeys, nil), NewSetHash: ValidatorSetHash(wireKeys, nil),
|
||||
ThresholdOld: tOld, ThresholdNew: tNew,
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
//
|
||||
// The 17 fields covered are:
|
||||
//
|
||||
// chain_id network_id group_id
|
||||
// key_era_id old_generation new_generation
|
||||
// old_epoch_id new_epoch_id old_set_hash
|
||||
// new_set_hash threshold_old threshold_new
|
||||
// group_public_key_hash nebula_root hash_suite_id
|
||||
// implementation_version variant
|
||||
// chain_id network_id group_id
|
||||
// key_era_id old_generation new_generation
|
||||
// old_epoch_id new_epoch_id old_set_hash
|
||||
// new_set_hash threshold_old threshold_new
|
||||
// group_public_key_hash nebula_root hash_suite_id
|
||||
// implementation_version variant
|
||||
//
|
||||
// The "unchanged GroupKey" is modelled by a fixed test-side oracle
|
||||
// that, given the original activation bytes, returns true for the
|
||||
@@ -31,10 +31,10 @@
|
||||
//
|
||||
// Citations (canonical proof bucket):
|
||||
//
|
||||
// proofs/definitions/transcript-binding.tex
|
||||
// Definition ref:pulsar-transcript
|
||||
// proofs/pulsar/hash-suite-separation.tex
|
||||
// Theorem ref:hash-suite-separation
|
||||
// proofs/definitions/transcript-binding.tex
|
||||
// Definition ref:pulsar-transcript
|
||||
// proofs/pulsar/hash-suite-separation.tex
|
||||
// Theorem ref:hash-suite-separation
|
||||
package reshare
|
||||
|
||||
import (
|
||||
|
||||
@@ -74,13 +74,13 @@ package reshare
|
||||
// - Threshold may differ from previous epoch (Reshare allows
|
||||
// t_old → t_new transitions). Refresh keeps t unchanged.
|
||||
type EpochShareState struct {
|
||||
Epoch uint64
|
||||
ValidatorSet []string // ordered validator wire-identity strings
|
||||
Threshold int // new threshold t_new (= t_old for Refresh)
|
||||
Shares map[string]*PartyKeyShare // validator-ID → share
|
||||
GroupKey *PartyGroupKey // POINTER to persistent group key
|
||||
TranscriptHash [32]byte // bind to TranscriptInputs.Hash()
|
||||
ActivationCert *ActivationCert // proof the chain accepted this epoch
|
||||
Epoch uint64
|
||||
ValidatorSet []string // ordered validator wire-identity strings
|
||||
Threshold int // new threshold t_new (= t_old for Refresh)
|
||||
Shares map[string]*PartyKeyShare // validator-ID → share
|
||||
GroupKey *PartyGroupKey // POINTER to persistent group key
|
||||
TranscriptHash [32]byte // bind to TranscriptInputs.Hash()
|
||||
ActivationCert *ActivationCert // proof the chain accepted this epoch
|
||||
}
|
||||
|
||||
// ReshareEpochInputs is the parameter bundle for one resharing round.
|
||||
@@ -112,14 +112,14 @@ type ReshareEpochInputs struct {
|
||||
// Refresh round. The committee and threshold are unchanged, only the
|
||||
// share distribution rotates.
|
||||
type RefreshEpochInputs struct {
|
||||
ChainID []byte
|
||||
GroupID []byte
|
||||
OldEpochID uint64
|
||||
NewEpochID uint64
|
||||
Validators []string // unchanged committee
|
||||
Shares map[int]Share // 1-indexed party ID → share
|
||||
Threshold int // unchanged threshold
|
||||
GroupKeyHash [32]byte
|
||||
ChainID []byte
|
||||
GroupID []byte
|
||||
OldEpochID uint64
|
||||
NewEpochID uint64
|
||||
Validators []string // unchanged committee
|
||||
Shares map[int]Share // 1-indexed party ID → share
|
||||
Threshold int // unchanged threshold
|
||||
GroupKeyHash [32]byte
|
||||
}
|
||||
|
||||
// BuildTranscript converts ReshareEpochInputs into the
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
// We build (t, n)-Shamir shares of a planted secret, run Refresh, and
|
||||
// verify:
|
||||
//
|
||||
// 1. Old shares interpolate to the planted secret.
|
||||
// 2. New shares interpolate to the SAME planted secret.
|
||||
// 3. New shares ≠ old shares (Hamming/byte distance positive — the
|
||||
// probability all coordinates collide is < 2^-48 per coordinate
|
||||
// times nVec*N coordinates ≈ 2^-48 * 7 * 256 ≈ negligible).
|
||||
// 1. Old shares interpolate to the planted secret.
|
||||
// 2. New shares interpolate to the SAME planted secret.
|
||||
// 3. New shares ≠ old shares (Hamming/byte distance positive — the
|
||||
// probability all coordinates collide is < 2^-48 per coordinate
|
||||
// times nVec*N coordinates ≈ 2^-48 * 7 * 256 ≈ negligible).
|
||||
//
|
||||
// This is the canonical test that proves Refresh implements the
|
||||
// HJKY97 zero-polynomial pattern correctly.
|
||||
|
||||
+26
-24
@@ -25,13 +25,13 @@
|
||||
// and privately delivers g_i(β_j) to each new party j ∈ N. Each
|
||||
// new party j computes
|
||||
//
|
||||
// s'_j = Σ_{i ∈ Q} λ^Q_i · g_i(β_j)
|
||||
// s'_j = Σ_{i ∈ Q} λ^Q_i · g_i(β_j)
|
||||
//
|
||||
// where λ^Q_i are the Lagrange coefficients for the quorum Q
|
||||
// evaluated at 0. Define G(X) = Σ_{i ∈ Q} λ^Q_i · g_i(X). Then
|
||||
// deg(G) = t_new − 1 with overwhelming probability, and
|
||||
//
|
||||
// G(0) = Σ_{i ∈ Q} λ^Q_i · g_i(0) = Σ_{i ∈ Q} λ^Q_i · s_i = s
|
||||
// G(0) = Σ_{i ∈ Q} λ^Q_i · g_i(0) = Σ_{i ∈ Q} λ^Q_i · s_i = s
|
||||
//
|
||||
// by Lagrange interpolation over Q at X=0. The {G(β_j) : j ∈ N}
|
||||
// are therefore valid (t_new, |N|)-Shamir shares of the SAME master
|
||||
@@ -61,27 +61,27 @@
|
||||
// files in this package:
|
||||
//
|
||||
// - commit.go — Pedersen-style polynomial commitments to f_i
|
||||
// (Refresh) and g_i (Reshare); recipients verify
|
||||
// each share against the committed polynomial.
|
||||
// (Refresh) and g_i (Reshare); recipients verify
|
||||
// each share against the committed polynomial.
|
||||
// - transcript.go — Domain-separated transcript binding all
|
||||
// resharing messages, complaints, and the
|
||||
// (chain_id, epoch, group_id, sets, thresholds,
|
||||
// group_pk_hash) tuple.
|
||||
// resharing messages, complaints, and the
|
||||
// (chain_id, epoch, group_id, sets, thresholds,
|
||||
// group_pk_hash) tuple.
|
||||
// - complaint.go — Complaint format, signed evidence, complaint
|
||||
// quorum logic, deterministic disqualification of
|
||||
// misbehaving senders.
|
||||
// quorum logic, deterministic disqualification of
|
||||
// misbehaving senders.
|
||||
// - keyshare.go — Wraps reshared SkShare values into complete
|
||||
// Corona/Pulsar `KeyShare` instances by
|
||||
// regenerating Lambda, Seeds, MACKeys, and
|
||||
// attaching the unchanged GroupKey pointer.
|
||||
// Corona/Pulsar `KeyShare` instances by
|
||||
// regenerating Lambda, Seeds, MACKeys, and
|
||||
// attaching the unchanged GroupKey pointer.
|
||||
// - pairwise.go — Authenticated pairwise KEX (X25519 / ML-KEM
|
||||
// hybrid) → KDF derivation of new Seeds, MACKeys
|
||||
// under domain-separated tags.
|
||||
// hybrid) → KDF derivation of new Seeds, MACKeys
|
||||
// under domain-separated tags.
|
||||
// - activation.go — Post-reshare activation certificate: the new
|
||||
// committee threshold-signs the resharing
|
||||
// transcript hash under the unchanged GroupKey.
|
||||
// The chain accepts the new epoch only when this
|
||||
// activation cert verifies.
|
||||
// committee threshold-signs the resharing
|
||||
// transcript hash under the unchanged GroupKey.
|
||||
// The chain accepts the new epoch only when this
|
||||
// activation cert verifies.
|
||||
//
|
||||
// All of the above MUST be wired together at the Quasar consensus
|
||||
// layer (`protocol/quasar/epoch.go`) — see `quasar_integration.go` for
|
||||
@@ -307,12 +307,14 @@ func Reshare(
|
||||
// 1. Each party i samples a random degree-(t-1) polynomial z_i(X)
|
||||
// over R_q^{Nvec} with z_i(0) = 0 (i.e. z_i has no constant term;
|
||||
// coefficients of degree 1..t-1 are uniformly random).
|
||||
//
|
||||
// 2. Each party i privately delivers z_i(α_j) to every party j in the
|
||||
// committee, where α_j is the Shamir evaluation point of party j
|
||||
// (here α_j = j, the 1-indexed party ID).
|
||||
//
|
||||
// 3. Each party j updates its share:
|
||||
//
|
||||
// s'_j = s_j + Σ_i z_i(α_j) (mod q)
|
||||
// s'_j = s_j + Σ_i z_i(α_j) (mod q)
|
||||
//
|
||||
// Correctness. Define Z(X) = Σ_i z_i(X). Z is a degree-(t-1) random
|
||||
// polynomial over R_q with Z(0) = 0. The new shares are evaluations of
|
||||
@@ -327,13 +329,13 @@ func Reshare(
|
||||
//
|
||||
// - r — the canonical R_q ring (must match sign.Gen).
|
||||
// - shares — partyID → share, the current committee's shares.
|
||||
// The map MUST contain every party in the committee
|
||||
// (refresh has no quorum semantics; ALL parties must
|
||||
// participate, since the zero-polynomial deltas must
|
||||
// be computed at every evaluation point).
|
||||
// The map MUST contain every party in the committee
|
||||
// (refresh has no quorum semantics; ALL parties must
|
||||
// participate, since the zero-polynomial deltas must
|
||||
// be computed at every evaluation point).
|
||||
// - threshold — the unchanged reconstruction threshold t.
|
||||
// - randSource — randomness source for the fresh polynomial
|
||||
// coefficients (nil → crypto/rand.Reader).
|
||||
// coefficients (nil → crypto/rand.Reader).
|
||||
//
|
||||
// Returns the refreshed share map, keyed by the SAME party IDs as the
|
||||
// input. The output shares are valid (threshold, |committee|)-Shamir
|
||||
|
||||
@@ -19,9 +19,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
testLogN = 8
|
||||
testQ = sign.Q
|
||||
testNVec = 3
|
||||
testLogN = 8
|
||||
testQ = sign.Q
|
||||
testNVec = 3
|
||||
)
|
||||
|
||||
// fakeRand is a deterministic PRNG built from a SHA-256 counter mode.
|
||||
|
||||
@@ -39,13 +39,13 @@ import (
|
||||
//
|
||||
// IMPORTANT: this duplicate cap exists because the upstream lattice
|
||||
// library has TWO DoS surfaces:
|
||||
// 1. ReadUintNSlice unbounded recursion — fixed by luxfi/lattice#3
|
||||
// (open as of 2026-05-04).
|
||||
// 2. Vector.ReadFrom calling make([]T, size) before any bound check —
|
||||
// NOT addressed by PR #3. A 9-byte input
|
||||
// `\xad\x93\xd8\x5a\x00\x04\x00\x00\\` reads size=0x40005AD893AD
|
||||
// (~70T entries) and OOMs the goroutine before the slice reader
|
||||
// runs. Found by FuzzPulsarSign1Round1Data on 2026-05-04.
|
||||
// 1. ReadUintNSlice unbounded recursion — fixed by luxfi/lattice#3
|
||||
// (open as of 2026-05-04).
|
||||
// 2. Vector.ReadFrom calling make([]T, size) before any bound check —
|
||||
// NOT addressed by PR #3. A 9-byte input
|
||||
// `\xad\x93\xd8\x5a\x00\x04\x00\x00\\` reads size=0x40005AD893AD
|
||||
// (~70T entries) and OOMs the goroutine before the slice reader
|
||||
// runs. Found by FuzzPulsarSign1Round1Data on 2026-05-04.
|
||||
//
|
||||
// The walker below pre-validates the wire frame BEFORE handing it to
|
||||
// lattigo, mirroring warp/pulsar.validateVectorPolyFrame.
|
||||
|
||||
Reference in New Issue
Block a user