exchange_test.go raw

   1  package crypto
   2  
   3  import (
   4  	"testing"
   5  
   6  	"git.mleku.dev/mleku/dendrite/pkg/axiom"
   7  	"git.mleku.dev/mleku/dendrite/pkg/lattice"
   8  	"git.mleku.dev/mleku/dendrite/pkg/ratio"
   9  	"git.mleku.dev/mleku/dendrite/pkg/spore"
  10  )
  11  
  12  func buildSporeWithTags(tags []string, nodesPerTag int) *spore.Spore {
  13  	l := lattice.New()
  14  	for _, tag := range tags {
  15  		var nodes []*lattice.Node
  16  		for range nodesPerTag {
  17  			n := l.AddNode([]axiom.Constraint{tagConstraint{tag}})
  18  			nodes = append(nodes, n)
  19  		}
  20  		for i := range nodes {
  21  			l.Connect(nodes[i], nodes[(i+1)%len(nodes)])
  22  		}
  23  	}
  24  	return spore.Extract(l)
  25  }
  26  
  27  func TestExchangeSameStructure(t *testing.T) {
  28  	tags := []string{"word", "punct", "space"}
  29  	s1 := buildSporeWithTags(tags, 10)
  30  	s2 := buildSporeWithTags(tags, 10)
  31  
  32  	ss, err := Exchange(s1, s2)
  33  	if err != nil {
  34  		t.Fatalf("Exchange: %v", err)
  35  	}
  36  
  37  	if len(ss.CommonTags) != 3 {
  38  		t.Errorf("common tags = %d, want 3", len(ss.CommonTags))
  39  	}
  40  	if !ss.Confidence.Equal(ratio.One) {
  41  		t.Errorf("confidence = %s, want 1/1", ss.Confidence)
  42  	}
  43  	if ss.Secret == (Hamadryad{}) {
  44  		t.Error("secret should not be zero")
  45  	}
  46  }
  47  
  48  func TestExchangePartialOverlap(t *testing.T) {
  49  	s1 := buildSporeWithTags([]string{"word", "punct"}, 10)
  50  	s2 := buildSporeWithTags([]string{"punct", "space"}, 10)
  51  
  52  	ss, err := Exchange(s1, s2)
  53  	if err != nil {
  54  		t.Fatalf("Exchange: %v", err)
  55  	}
  56  
  57  	if len(ss.CommonTags) != 1 {
  58  		t.Errorf("common tags = %d, want 1 (punct)", len(ss.CommonTags))
  59  	}
  60  	if ss.CommonTags[0] != "punct" {
  61  		t.Errorf("common tag = %q, want punct", ss.CommonTags[0])
  62  	}
  63  	// Confidence: 1 common / max(2, 2) = 1/2
  64  	if !ss.Confidence.Equal(ratio.Half) {
  65  		t.Errorf("confidence = %s, want 1/2", ss.Confidence)
  66  	}
  67  }
  68  
  69  func TestExchangeNoOverlap(t *testing.T) {
  70  	s1 := buildSporeWithTags([]string{"word"}, 10)
  71  	s2 := buildSporeWithTags([]string{"punct"}, 10)
  72  
  73  	ss, err := Exchange(s1, s2)
  74  	if err != nil {
  75  		t.Fatalf("Exchange: %v", err)
  76  	}
  77  
  78  	if len(ss.CommonTags) != 0 {
  79  		t.Errorf("common tags = %d, want 0", len(ss.CommonTags))
  80  	}
  81  	if !ss.Confidence.Equal(ratio.Zero) {
  82  		t.Errorf("confidence = %s, want 0", ss.Confidence)
  83  	}
  84  }
  85  
  86  func TestExchangeSymmetry(t *testing.T) {
  87  	s1 := buildSporeWithTags([]string{"word", "punct"}, 8)
  88  	s2 := buildSporeWithTags([]string{"word", "punct", "space"}, 6)
  89  
  90  	ss1, err := Exchange(s1, s2)
  91  	if err != nil {
  92  		t.Fatalf("Exchange(1,2): %v", err)
  93  	}
  94  	ss2, err := Exchange(s2, s1)
  95  	if err != nil {
  96  		t.Fatalf("Exchange(2,1): %v", err)
  97  	}
  98  
  99  	if ss1.Secret != ss2.Secret {
 100  		t.Error("exchange should be symmetric: both parties derive the same secret")
 101  	}
 102  	if !ss1.Confidence.Equal(ss2.Confidence) {
 103  		t.Error("confidence should be symmetric")
 104  	}
 105  }
 106  
 107  func TestExchangeNilSpore(t *testing.T) {
 108  	s := buildSporeWithTags([]string{"word"}, 5)
 109  	_, err := Exchange(nil, s)
 110  	if err == nil {
 111  		t.Error("expected error with nil spore")
 112  	}
 113  	_, err = Exchange(s, nil)
 114  	if err == nil {
 115  		t.Error("expected error with nil spore")
 116  	}
 117  }
 118  
 119  func TestDeriveKey(t *testing.T) {
 120  	ss := &SharedSecret{
 121  		CommonTags: []string{"word"},
 122  		Secret:     Hamadryad{1, 2, 3},
 123  		Confidence: ratio.One,
 124  	}
 125  
 126  	key16 := DeriveKey(ss, "aes-128", 16)
 127  	if len(key16) != 16 {
 128  		t.Errorf("key length = %d, want 16", len(key16))
 129  	}
 130  
 131  	key32 := DeriveKey(ss, "aes-256", 32)
 132  	if len(key32) != 32 {
 133  		t.Errorf("key length = %d, want 32", len(key32))
 134  	}
 135  
 136  	key64 := DeriveKey(ss, "extended", 64)
 137  	if len(key64) != 64 {
 138  		t.Errorf("key length = %d, want 64", len(key64))
 139  	}
 140  
 141  	// Different contexts should produce different keys.
 142  	keyA := DeriveKey(ss, "context-a", 32)
 143  	keyB := DeriveKey(ss, "context-b", 32)
 144  	if string(keyA) == string(keyB) {
 145  		t.Error("different contexts should produce different keys")
 146  	}
 147  }
 148  
 149  func TestDeriveKeyDeterministic(t *testing.T) {
 150  	ss := &SharedSecret{
 151  		CommonTags: []string{"word"},
 152  		Secret:     Hamadryad{42},
 153  		Confidence: ratio.One,
 154  	}
 155  
 156  	key1 := DeriveKey(ss, "test", 32)
 157  	key2 := DeriveKey(ss, "test", 32)
 158  	if string(key1) != string(key2) {
 159  		t.Error("same inputs should produce same key")
 160  	}
 161  }
 162