recognizer_test.go raw

   1  package ring
   2  
   3  import "testing"
   4  
   5  // TestMatchBitEquality verifies single-bit equality test.
   6  func TestMatchBitEquality(t *testing.T) {
   7  	kp := DefaultHEParams()
   8  	pk, sk, _ := HEKeyGen(kp)
   9  
  10  	for _, a := range []int{0, 1} {
  11  		for _, b := range []int{0, 1} {
  12  			ctA := HEEncrypt(pk, a)
  13  			ctB := HEEncrypt(pk, b)
  14  			ctMatch := MatchBit(ctA, ctB)
  15  			got := HEDecrypt(sk, ctMatch)
  16  
  17  			want := 0
  18  			if a == b {
  19  				want = 1
  20  			}
  21  			if got != want {
  22  				t.Fatalf("MatchBit(%d, %d): got %d, want %d", a, b, got, want)
  23  			}
  24  		}
  25  	}
  26  }
  27  
  28  // TestEncryptDecryptBits verifies bit vector round-trip.
  29  func TestEncryptDecryptBits(t *testing.T) {
  30  	kp := DefaultHEParams()
  31  	pk, sk, _ := HEKeyGen(kp)
  32  
  33  	data := []byte{0xAB, 0xCD}
  34  	enc := EncryptBits(pk, data)
  35  	dec := DecryptBits(sk, enc)
  36  
  37  	if len(dec) != len(data) {
  38  		t.Fatalf("length mismatch: got %d, want %d", len(dec), len(data))
  39  	}
  40  	for i := range data {
  41  		if dec[i] != data[i] {
  42  			t.Fatalf("byte[%d]: got %02x, want %02x", i, dec[i], data[i])
  43  		}
  44  	}
  45  }
  46  
  47  // TestMatchByteSingle verifies per-pair AND for byte matching.
  48  func TestMatchByteSingle(t *testing.T) {
  49  	kp := DefaultHEParams()
  50  	pk, sk, rlk := HEKeyGen(kp)
  51  
  52  	// Matching bytes: all pair-ANDs should be 1.
  53  	dataByte := byte(0xA5)
  54  	patByte := byte(0xA5)
  55  
  56  	dataBits := make([]*HECiphertext, 8)
  57  	patBits := make([]*HECiphertext, 8)
  58  	for i := range 8 {
  59  		dataBits[i] = HEEncrypt(pk, int((dataByte>>i)&1))
  60  		patBits[i] = HEEncrypt(pk, int((patByte>>i)&1))
  61  	}
  62  
  63  	pairs := MatchByteSingle(dataBits, patBits, rlk)
  64  	if len(pairs) != 4 {
  65  		t.Fatalf("expected 4 pair results, got %d", len(pairs))
  66  	}
  67  
  68  	for i, ct := range pairs {
  69  		got := HEDecrypt(sk, ct)
  70  		if got != 1 {
  71  			t.Fatalf("pair[%d]: got %d, want 1 (matching bytes)", i, got)
  72  		}
  73  	}
  74  }
  75  
  76  // TestMatchByteSingleMismatch verifies that a different byte produces
  77  // at least one pair with value 0.
  78  func TestMatchByteSingleMismatch(t *testing.T) {
  79  	kp := DefaultHEParams()
  80  	pk, sk, rlk := HEKeyGen(kp)
  81  
  82  	dataByte := byte(0xA5)
  83  	patByte := byte(0x5A) // every bit is different
  84  
  85  	dataBits := make([]*HECiphertext, 8)
  86  	patBits := make([]*HECiphertext, 8)
  87  	for i := range 8 {
  88  		dataBits[i] = HEEncrypt(pk, int((dataByte>>i)&1))
  89  		patBits[i] = HEEncrypt(pk, int((patByte>>i)&1))
  90  	}
  91  
  92  	pairs := MatchByteSingle(dataBits, patBits, rlk)
  93  
  94  	anyZero := false
  95  	for _, ct := range pairs {
  96  		if HEDecrypt(sk, ct) == 0 {
  97  			anyZero = true
  98  			break
  99  		}
 100  	}
 101  	if !anyZero {
 102  		t.Fatal("all pairs returned 1 for mismatched bytes")
 103  	}
 104  }
 105  
 106  // TestRecognize verifies the full searchable encryption pipeline.
 107  func TestRecognize(t *testing.T) {
 108  	kp := DefaultHEParams()
 109  	pk, sk, rlk := HEKeyGen(kp)
 110  
 111  	// Search for pattern in data.
 112  	data := []byte("hello")
 113  	pattern := []byte("ll")
 114  
 115  	matches := Recognize(pk, sk, rlk, data, pattern)
 116  
 117  	t.Logf("data=%q, pattern=%q, matches=%v", data, pattern, matches)
 118  
 119  	// "ll" should appear at position 2 in "hello".
 120  	found := false
 121  	for _, pos := range matches {
 122  		if pos == 2 {
 123  			found = true
 124  		}
 125  	}
 126  	if !found {
 127  		t.Fatalf("expected match at position 2, got %v", matches)
 128  	}
 129  }
 130  
 131  // TestRecognizeNoMatch verifies no false positives.
 132  func TestRecognizeNoMatch(t *testing.T) {
 133  	kp := DefaultHEParams()
 134  	pk, sk, rlk := HEKeyGen(kp)
 135  
 136  	data := []byte("abc")
 137  	pattern := []byte("xy")
 138  
 139  	matches := Recognize(pk, sk, rlk, data, pattern)
 140  
 141  	if len(matches) > 0 {
 142  		t.Fatalf("expected no matches, got %v", matches)
 143  	}
 144  }
 145  
 146  // TestRecognizeSingleByte verifies single-byte pattern search.
 147  func TestRecognizeSingleByte(t *testing.T) {
 148  	kp := DefaultHEParams()
 149  	pk, sk, rlk := HEKeyGen(kp)
 150  
 151  	data := []byte{0x42, 0x43, 0x42, 0x44}
 152  	pattern := []byte{0x42}
 153  
 154  	matches := Recognize(pk, sk, rlk, data, pattern)
 155  
 156  	t.Logf("pattern=0x42, matches=%v", matches)
 157  
 158  	// 0x42 appears at positions 0 and 2.
 159  	if len(matches) != 2 {
 160  		t.Fatalf("expected 2 matches, got %d: %v", len(matches), matches)
 161  	}
 162  }
 163  
 164  func BenchmarkMatchBit(b *testing.B) {
 165  	kp := DefaultHEParams()
 166  	pk, _, _ := HEKeyGen(kp)
 167  	ct0 := HEEncrypt(pk, 0)
 168  	ct1 := HEEncrypt(pk, 1)
 169  	b.ResetTimer()
 170  	for range b.N {
 171  		MatchBit(ct0, ct1)
 172  	}
 173  }
 174  
 175  func BenchmarkMatchByteSingle(b *testing.B) {
 176  	kp := DefaultHEParams()
 177  	pk, _, rlk := HEKeyGen(kp)
 178  	dataBits := make([]*HECiphertext, 8)
 179  	patBits := make([]*HECiphertext, 8)
 180  	for i := range 8 {
 181  		dataBits[i] = HEEncrypt(pk, i%2)
 182  		patBits[i] = HEEncrypt(pk, i%2)
 183  	}
 184  	b.ResetTimer()
 185  	for range b.N {
 186  		MatchByteSingle(dataBits, patBits, rlk)
 187  	}
 188  }
 189