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