package gnarlring import ( "crypto/rand" "testing" "git.smesh.lol/gnarl-hamadryad/crypto" ) func TestFullDistributedKeyGeneration(t *testing.T) { // === Phase 1: Key Generation === // Coordinator generates its NTRU keypair. coordPK, coordSK := NTRUKeyGen() t.Logf("phase 1: coordinator key generated (norm=%d)", Normalize(coordPK.H)) // Each of k children generates an NTRU keypair. k := 5 // demonstration with 5 members (production: 27) childKeys := make([]*NTRUPublicKey, k) for i := 0; i < k; i++ { childKeys[i], _ = NTRUKeyGen() childKeys[i] = (func(pk *NTRUPublicKey) *NTRUPublicKey { return pk })(childKeys[i]) } t.Logf("phase 1: %d child keys generated", k) // === Phase 2: Commitments === es := StartEpoch(1, coordPK) for i := 0; i < k; i++ { cc := NewChildCommitment(i, childKeys[i], rand.Reader) if err := es.AddCommitment(cc); err != nil { t.Fatalf("add commitment %d: %v", i, err) } } // Fill remaining slots (k..26) to make epoch complete. for i := k; i < N; i++ { cc := NewChildCommitment(i, childKeys[0], rand.Reader) es.AddCommitment(cc) } t.Log("phase 2: all 27 commitments collected") // === Phase 3: Finalize === msg := []byte("epoch-1-group-formation") if err := es.Finalize(coordSK, msg); err != nil { t.Fatal(err) } t.Logf("phase 3: epoch finalized, sig=%d bytes", len(es.RootSig.MarshalBinary())) // === Phase 4: Verify === if !es.Verify(coordPK, msg) { t.Fatal("local verification failed") } t.Log("phase 4: local verification OK") // === Phase 5: Wire Format === // Marshal epoch frame for broadcast. epochWire := MarshalEpochFrame(es) if len(epochWire) != EpochFrameSize { t.Fatalf("epoch frame size %d, want %d", len(epochWire), EpochFrameSize) } // Deserialize. es2 := UnmarshalEpochFrame(epochWire) if es2 == nil { t.Fatal("unmarshal epoch failed") } if !es2.Verify(coordPK, msg) { t.Fatal("verification after unwire failed") } t.Logf("phase 5: wire round-trip OK (%d bytes)", len(epochWire)) // Marshal compact check frame for relay verification. checkWire := MarshalEpochCheckFrame(es) cf, err := UnmarshalEpochCheckFrame(checkWire) if err != nil { t.Fatal(err) } if cf.Counter != es.Counter { t.Fatal("check frame counter mismatch") } t.Logf("phase 5: check frame round-trip OK (%d bytes)", len(checkWire)) // === Phase 6: GnarlWire Transport === var secret crypto.Hamadryad rand.Read(secret[:]) var identity crypto.GnarlMid rand.Read(identity[:]) var nonce [crypto.GnarlNonceLen]byte rand.Read(nonce[:]) // Seal epoch frame. pkt := SealEpoch(secret, identity, nonce, es) es3, err := OpenEpoch(secret, pkt) if err != nil { t.Fatal(err) } if !es3.Verify(coordPK, msg) { t.Fatal("verify after GnarlWire round-trip failed") } // Tampered packet should fail. pkt.Ciphertext[0] ^= 0xFF if _, err := OpenEpoch(secret, pkt); err == nil { t.Fatal("tampered packet accepted") } t.Log("phase 6: GnarlWire transport OK, tampering detected") // === Phase 7: Membership Rotation === // Rotate child 3's commitment. newChild := NewChildCommitment(3, childKeys[3], rand.Reader) es.RotateMember(3, newChild) if es.IsFinalized() { t.Fatal("epoch should be unfinalized after rotation") } if err := es.Finalize(coordSK, msg); err != nil { t.Fatal(err) } if !es.Verify(coordPK, msg) { t.Fatal("verify after rotation failed") } t.Log("phase 7: member rotation OK") // === Phase 8: LWE Private Channel === lwePK, lweSK := LWEKeyGen() for _, bit := range []int{0, 1} { ct := LWEEncrypt(lwePK, bit) if dec := LWEDecrypt(lweSK, ct); dec != bit { t.Errorf("LWE: bit=%d dec=%d", bit, dec) } } t.Log("phase 8: Ring-LWE encryption OK") // === Phase 9: Consensus Voting === tally := NewVoteTally() nYes := 8 for i := 0; i < nYes; i++ { tally.Add(CastVote(1), true) } for i := 0; i < 5; i++ { tally.Add(CastVote(-1), false) } if !tally.ConsensusResult(7) { t.Fatal("consensus threshold 7 should pass with 8 yes") } if tally.ConsensusResult(9) { t.Fatal("consensus threshold 9 should fail with 8 yes") } t.Logf("phase 9: consensus voting OK (8 yes / 5 no)") t.Log("=== FULL DISTRIBUTED KEY GENERATION: PASSED ===") t.Logf(" ring: n=%d q=%d", N, Q) t.Logf(" signature: %d bytes", len(coordSK.PK.H.MarshalBinary())+47) t.Logf(" epoch frame: %d bytes", EpochFrameSize) t.Logf(" check frame: %d bytes", EpochCheckFrameSize) t.Logf(" commitment frame: %d bytes", CommitmentFrameSize) } func Normalize(p *Poly27) uint16 { return Norm(p) }