integration_test.go raw
1 package gnarlring
2
3 import (
4 "crypto/rand"
5 "testing"
6
7 "git.smesh.lol/gnarl-hamadryad/crypto"
8 )
9
10 func TestFullDistributedKeyGeneration(t *testing.T) {
11 // === Phase 1: Key Generation ===
12 // Coordinator generates its NTRU keypair.
13 coordPK, coordSK := NTRUKeyGen()
14 t.Logf("phase 1: coordinator key generated (norm=%d)", Normalize(coordPK.H))
15
16 // Each of k children generates an NTRU keypair.
17 k := 5 // demonstration with 5 members (production: 27)
18 childKeys := make([]*NTRUPublicKey, k)
19 for i := 0; i < k; i++ {
20 childKeys[i], _ = NTRUKeyGen()
21 childKeys[i] = (func(pk *NTRUPublicKey) *NTRUPublicKey { return pk })(childKeys[i])
22 }
23 t.Logf("phase 1: %d child keys generated", k)
24
25 // === Phase 2: Commitments ===
26 es := StartEpoch(1, coordPK)
27 for i := 0; i < k; i++ {
28 cc := NewChildCommitment(i, childKeys[i], rand.Reader)
29 if err := es.AddCommitment(cc); err != nil {
30 t.Fatalf("add commitment %d: %v", i, err)
31 }
32 }
33 // Fill remaining slots (k..26) to make epoch complete.
34 for i := k; i < N; i++ {
35 cc := NewChildCommitment(i, childKeys[0], rand.Reader)
36 es.AddCommitment(cc)
37 }
38 t.Log("phase 2: all 27 commitments collected")
39
40 // === Phase 3: Finalize ===
41 msg := []byte("epoch-1-group-formation")
42 if err := es.Finalize(coordSK, msg); err != nil {
43 t.Fatal(err)
44 }
45 t.Logf("phase 3: epoch finalized, sig=%d bytes", len(es.RootSig.MarshalBinary()))
46
47 // === Phase 4: Verify ===
48 if !es.Verify(coordPK, msg) {
49 t.Fatal("local verification failed")
50 }
51 t.Log("phase 4: local verification OK")
52
53 // === Phase 5: Wire Format ===
54 // Marshal epoch frame for broadcast.
55 epochWire := MarshalEpochFrame(es)
56 if len(epochWire) != EpochFrameSize {
57 t.Fatalf("epoch frame size %d, want %d", len(epochWire), EpochFrameSize)
58 }
59 // Deserialize.
60 es2 := UnmarshalEpochFrame(epochWire)
61 if es2 == nil {
62 t.Fatal("unmarshal epoch failed")
63 }
64 if !es2.Verify(coordPK, msg) {
65 t.Fatal("verification after unwire failed")
66 }
67 t.Logf("phase 5: wire round-trip OK (%d bytes)", len(epochWire))
68
69 // Marshal compact check frame for relay verification.
70 checkWire := MarshalEpochCheckFrame(es)
71 cf, err := UnmarshalEpochCheckFrame(checkWire)
72 if err != nil {
73 t.Fatal(err)
74 }
75 if cf.Counter != es.Counter {
76 t.Fatal("check frame counter mismatch")
77 }
78 t.Logf("phase 5: check frame round-trip OK (%d bytes)", len(checkWire))
79
80 // === Phase 6: GnarlWire Transport ===
81 var secret crypto.Hamadryad
82 rand.Read(secret[:])
83 var identity crypto.GnarlMid
84 rand.Read(identity[:])
85 var nonce [crypto.GnarlNonceLen]byte
86 rand.Read(nonce[:])
87
88 // Seal epoch frame.
89 pkt := SealEpoch(secret, identity, nonce, es)
90 es3, err := OpenEpoch(secret, pkt)
91 if err != nil {
92 t.Fatal(err)
93 }
94 if !es3.Verify(coordPK, msg) {
95 t.Fatal("verify after GnarlWire round-trip failed")
96 }
97
98 // Tampered packet should fail.
99 pkt.Ciphertext[0] ^= 0xFF
100 if _, err := OpenEpoch(secret, pkt); err == nil {
101 t.Fatal("tampered packet accepted")
102 }
103 t.Log("phase 6: GnarlWire transport OK, tampering detected")
104
105 // === Phase 7: Membership Rotation ===
106 // Rotate child 3's commitment.
107 newChild := NewChildCommitment(3, childKeys[3], rand.Reader)
108 es.RotateMember(3, newChild)
109 if es.IsFinalized() {
110 t.Fatal("epoch should be unfinalized after rotation")
111 }
112 if err := es.Finalize(coordSK, msg); err != nil {
113 t.Fatal(err)
114 }
115 if !es.Verify(coordPK, msg) {
116 t.Fatal("verify after rotation failed")
117 }
118 t.Log("phase 7: member rotation OK")
119
120 // === Phase 8: LWE Private Channel ===
121 lwePK, lweSK := LWEKeyGen()
122 for _, bit := range []int{0, 1} {
123 ct := LWEEncrypt(lwePK, bit)
124 if dec := LWEDecrypt(lweSK, ct); dec != bit {
125 t.Errorf("LWE: bit=%d dec=%d", bit, dec)
126 }
127 }
128 t.Log("phase 8: Ring-LWE encryption OK")
129
130 // === Phase 9: Consensus Voting ===
131 tally := NewVoteTally()
132 nYes := 8
133 for i := 0; i < nYes; i++ {
134 tally.Add(CastVote(1), true)
135 }
136 for i := 0; i < 5; i++ {
137 tally.Add(CastVote(-1), false)
138 }
139 if !tally.ConsensusResult(7) {
140 t.Fatal("consensus threshold 7 should pass with 8 yes")
141 }
142 if tally.ConsensusResult(9) {
143 t.Fatal("consensus threshold 9 should fail with 8 yes")
144 }
145 t.Logf("phase 9: consensus voting OK (8 yes / 5 no)")
146
147 t.Log("=== FULL DISTRIBUTED KEY GENERATION: PASSED ===")
148 t.Logf(" ring: n=%d q=%d", N, Q)
149 t.Logf(" signature: %d bytes", len(coordSK.PK.H.MarshalBinary())+47)
150 t.Logf(" epoch frame: %d bytes", EpochFrameSize)
151 t.Logf(" check frame: %d bytes", EpochCheckFrameSize)
152 t.Logf(" commitment frame: %d bytes", CommitmentFrameSize)
153 }
154
155 func Normalize(p *Poly27) uint16 { return Norm(p) }
156