gnarl_hash_test.go raw

   1  package crypto
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  )
   7  
   8  func TestGnarlHashDeterministic(t *testing.T) {
   9  	msg := []byte("determinism test message")
  10  	h1 := GHash(msg)
  11  	h2 := GHash(msg)
  12  	if h1 != h2 {
  13  		t.Fatalf("GHash not deterministic: %x != %x", h1, h2)
  14  	}
  15  
  16  	m1 := GMid(msg)
  17  	m2 := GMid(msg)
  18  	if m1 != m2 {
  19  		t.Fatalf("GMid not deterministic")
  20  	}
  21  
  22  	s1 := GShard(msg)
  23  	s2 := GShard(msg)
  24  	if s1 != s2 {
  25  		t.Fatalf("GShard not deterministic")
  26  	}
  27  }
  28  
  29  func TestGnarlHashDistinct(t *testing.T) {
  30  	h1 := GHash([]byte("message one"))
  31  	h2 := GHash([]byte("message two"))
  32  	if h1 == h2 {
  33  		t.Fatalf("GHash collision on distinct messages")
  34  	}
  35  
  36  	m1 := GMid([]byte("message one"))
  37  	m2 := GMid([]byte("message two"))
  38  	if m1 == m2 {
  39  		t.Fatalf("GMid collision on distinct messages")
  40  	}
  41  }
  42  
  43  func TestGnarlHashEmpty(t *testing.T) {
  44  	h := GHash([]byte{})
  45  	if h.IsZero() {
  46  		t.Fatalf("GHash of empty input is zero")
  47  	}
  48  }
  49  
  50  func TestGnarlHashLong(t *testing.T) {
  51  	// Message longer than one block (41 bytes).
  52  	msg := make([]byte, 500)
  53  	for i := range msg {
  54  		msg[i] = byte(i % 256)
  55  	}
  56  	h1 := GHash(msg)
  57  	if h1.IsZero() {
  58  		t.Fatalf("GHash of 500-byte message is zero")
  59  	}
  60  
  61  	// Flip one bit: hash should change.
  62  	msg[250] ^= 0x01
  63  	h2 := GHash(msg)
  64  	if h1 == h2 {
  65  		t.Fatalf("GHash did not change after bit flip")
  66  	}
  67  }
  68  
  69  func TestGnarlHashMultiBlock(t *testing.T) {
  70  	// Messages that span exactly 1, 2, and 3 blocks.
  71  	for _, size := range []int{30, 60, 120} {
  72  		msg := make([]byte, size)
  73  		for i := range msg {
  74  			msg[i] = byte(i*7 + 13)
  75  		}
  76  		h := GHash(msg)
  77  		if h.IsZero() {
  78  			t.Fatalf("GHash of %d-byte message is zero", size)
  79  		}
  80  	}
  81  }
  82  
  83  func TestGnarlPackUnpack243(t *testing.T) {
  84  	// Pack and unpack should be inverse operations.
  85  	var coeffs [GnarlN]uint16
  86  	for i := range GnarlN {
  87  		coeffs[i] = uint16((i*37 + 13) % GnarlP)
  88  	}
  89  
  90  	packed := packCoeffs243(coeffs)
  91  	unpacked := unpackCoeffs243(packed)
  92  
  93  	for i := range GnarlN {
  94  		if unpacked[i] != coeffs[i] {
  95  			t.Fatalf("pack/unpack243 mismatch at %d: got %d, want %d",
  96  				i, unpacked[i], coeffs[i])
  97  		}
  98  	}
  99  }
 100  
 101  func TestGnarlPackUnpack243MaxValue(t *testing.T) {
 102  	// All coefficients at max value (270).
 103  	var coeffs [GnarlN]uint16
 104  	for i := range GnarlN {
 105  		coeffs[i] = 270
 106  	}
 107  
 108  	packed := packCoeffs243(coeffs)
 109  	unpacked := unpackCoeffs243(packed)
 110  
 111  	for i := range GnarlN {
 112  		if unpacked[i] != 270 {
 113  			t.Fatalf("max value pack/unpack243 mismatch at %d: got %d, want 270",
 114  				i, unpacked[i])
 115  		}
 116  	}
 117  }
 118  
 119  func TestGnarlMidFromHash(t *testing.T) {
 120  	msg := []byte("tier extraction test")
 121  	h := GHash(msg)
 122  	m := GMid(msg)
 123  
 124  	// Mid extracted from GnarlHash should match GMid computed directly.
 125  	mFromH := h.Mid()
 126  	if m != mFromH {
 127  		t.Fatalf("GMid from GHash differs from direct GMid")
 128  	}
 129  }
 130  
 131  func TestGnarlShardFromHash(t *testing.T) {
 132  	msg := []byte("shard extraction test")
 133  	h := GHash(msg)
 134  	s := GShard(msg)
 135  
 136  	sFromH := h.Shard()
 137  	if s != sFromH {
 138  		t.Fatalf("GShard from GHash differs from direct GShard")
 139  	}
 140  }
 141  
 142  func TestGnarlShardValues(t *testing.T) {
 143  	// Each trit in the shard should be 0, 1, or 2.
 144  	msg := []byte("trit value test")
 145  	s := GShard(msg)
 146  
 147  	bitPos := 0
 148  	for i := range GnarlN {
 149  		var v byte
 150  		for b := range 2 {
 151  			byteIdx := bitPos / 8
 152  			bitIdx := uint(bitPos % 8)
 153  			if s[byteIdx]&(1<<bitIdx) != 0 {
 154  				v |= 1 << uint(b)
 155  			}
 156  			bitPos++
 157  		}
 158  		if v > 2 {
 159  			t.Fatalf("trit %d has value %d (should be 0, 1, or 2)", i, v)
 160  		}
 161  	}
 162  }
 163  
 164  func TestGnarlHashSum(t *testing.T) {
 165  	h1 := GHash([]byte("alpha"))
 166  	h2 := GHash([]byte("beta"))
 167  	sum := h1.Sum(h2)
 168  	if sum.IsZero() {
 169  		t.Fatalf("sum of two hashes is zero")
 170  	}
 171  	// Sum should be commutative.
 172  	sum2 := h2.Sum(h1)
 173  	if sum != sum2 {
 174  		t.Fatalf("GnarlHash.Sum is not commutative")
 175  	}
 176  }
 177  
 178  func TestGnarlHashSizes(t *testing.T) {
 179  	if GnarlHashBytes != 31 {
 180  		t.Fatalf("GnarlHashBytes = %d, want 31", GnarlHashBytes)
 181  	}
 182  	if GnarlMidBytes != 27 {
 183  		t.Fatalf("GnarlMidBytes = %d, want 27", GnarlMidBytes)
 184  	}
 185  	if GnarlShardBytes != 7 {
 186  		t.Fatalf("GnarlShardBytes = %d, want 7", GnarlShardBytes)
 187  	}
 188  }
 189  
 190  func TestGnarlHashDispersion(t *testing.T) {
 191  	// Hash many messages and check that all bytes of the GnarlMid are exercised.
 192  	var orBits [GnarlMidBytes]byte
 193  	for i := range 100 {
 194  		msg := []byte{byte(i), byte(i >> 8), byte(i >> 16)}
 195  		m := GMid(msg)
 196  		for j := range GnarlMidBytes {
 197  			orBits[j] |= m[j]
 198  		}
 199  	}
 200  	for i, b := range orBits {
 201  		if b == 0 {
 202  			t.Fatalf("byte %d of GnarlMid never set across 100 hashes", i)
 203  		}
 204  	}
 205  }
 206  
 207  func TestGnarlLengthExtension(t *testing.T) {
 208  	// Different-length messages with same prefix should hash differently.
 209  	short := []byte("short")
 210  	long := append([]byte("short"), bytes.Repeat([]byte{0}, 50)...)
 211  
 212  	h1 := GHash(short)
 213  	h2 := GHash(long)
 214  	if h1 == h2 {
 215  		t.Fatalf("length extension: same hash for different-length messages")
 216  	}
 217  }
 218