gnarl_wire_test.go raw

   1  package crypto
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  )
   7  
   8  // gnarlTestSecret creates a Hamadryad secret from a seed string for testing.
   9  func gnarlTestSecret(seed string) Hamadryad {
  10  	var secret Hamadryad
  11  	mid := GMid([]byte(seed))
  12  	copy(secret[:], mid[:])
  13  	return secret
  14  }
  15  
  16  func TestGnarlHeaderLen(t *testing.T) {
  17  	if GnarlHeaderLen != 64 {
  18  		t.Fatalf("GnarlHeaderLen = %d, want 64", GnarlHeaderLen)
  19  	}
  20  	if GnarlNonceLen != 10 {
  21  		t.Fatalf("GnarlNonceLen = %d, want 10", GnarlNonceLen)
  22  	}
  23  }
  24  
  25  func TestGnarlSealOpenRoundTrip(t *testing.T) {
  26  	var secret Hamadryad
  27  	h := GHash([]byte("test-shared-secret"))
  28  	copy(secret[:], h[:])
  29  
  30  	identity := GMid([]byte("sender-spore-fingerprint"))
  31  	nonce := GnarlNonceFromCounter(0, 1)
  32  	plaintext := []byte("hello from the Bethe lattice")
  33  
  34  	pkt := GnarlSeal(secret, identity, nonce, plaintext)
  35  
  36  	if pkt.Nonce != nonce {
  37  		t.Fatal("nonce mismatch")
  38  	}
  39  	if pkt.Identity != identity {
  40  		t.Fatal("identity mismatch")
  41  	}
  42  	if len(pkt.Ciphertext) != len(plaintext) {
  43  		t.Fatalf("ciphertext len = %d, want %d", len(pkt.Ciphertext), len(plaintext))
  44  	}
  45  	if bytes.Equal(pkt.Ciphertext, plaintext) {
  46  		t.Fatal("ciphertext == plaintext")
  47  	}
  48  
  49  	got, err := GnarlOpen(secret, pkt)
  50  	if err != nil {
  51  		t.Fatalf("GnarlOpen: %v", err)
  52  	}
  53  	if !bytes.Equal(got, plaintext) {
  54  		t.Fatalf("decrypted = %q, want %q", got, plaintext)
  55  	}
  56  }
  57  
  58  func TestGnarlSealOpenEmptyPlaintext(t *testing.T) {
  59  	secret := gnarlTestSecret("empty-test")
  60  	identity := GMid([]byte("id"))
  61  	nonce := GnarlNonceFromCounter(0, 0)
  62  
  63  	pkt := GnarlSeal(secret, identity, nonce, []byte{})
  64  	if len(pkt.Ciphertext) != 0 {
  65  		t.Fatalf("ciphertext len = %d for empty plaintext", len(pkt.Ciphertext))
  66  	}
  67  
  68  	got, err := GnarlOpen(secret, pkt)
  69  	if err != nil {
  70  		t.Fatalf("GnarlOpen empty: %v", err)
  71  	}
  72  	if len(got) != 0 {
  73  		t.Fatalf("decrypted len = %d, want 0", len(got))
  74  	}
  75  }
  76  
  77  func TestGnarlSealOpenLargePayload(t *testing.T) {
  78  	secret := gnarlTestSecret("large")
  79  	identity := GMid([]byte("sender"))
  80  	nonce := GnarlNonceFromCounter(1, 42)
  81  
  82  	plaintext := make([]byte, 1400)
  83  	for i := range plaintext {
  84  		plaintext[i] = byte(i * 7)
  85  	}
  86  
  87  	pkt := GnarlSeal(secret, identity, nonce, plaintext)
  88  	got, err := GnarlOpen(secret, pkt)
  89  	if err != nil {
  90  		t.Fatal(err)
  91  	}
  92  	if !bytes.Equal(got, plaintext) {
  93  		t.Fatal("large payload round-trip failed")
  94  	}
  95  }
  96  
  97  func TestGnarlMarshalUnmarshalRoundTrip(t *testing.T) {
  98  	secret := gnarlTestSecret("marshal-test")
  99  	identity := GMid([]byte("me"))
 100  	nonce := GnarlNonceFromCounter(0, 99)
 101  	plaintext := []byte("wire format test")
 102  
 103  	pkt := GnarlSeal(secret, identity, nonce, plaintext)
 104  	wire := MarshalGnarlPacket(pkt)
 105  
 106  	if len(wire) != GnarlHeaderLen+len(plaintext) {
 107  		t.Fatalf("wire len = %d, want %d", len(wire), GnarlHeaderLen+len(plaintext))
 108  	}
 109  
 110  	pkt2, err := UnmarshalGnarlPacket(wire)
 111  	if err != nil {
 112  		t.Fatal(err)
 113  	}
 114  
 115  	if pkt2.Nonce != pkt.Nonce {
 116  		t.Fatal("nonce mismatch after unmarshal")
 117  	}
 118  	if pkt2.Identity != pkt.Identity {
 119  		t.Fatal("identity mismatch after unmarshal")
 120  	}
 121  	if pkt2.AuthTag != pkt.AuthTag {
 122  		t.Fatal("auth tag mismatch after unmarshal")
 123  	}
 124  	if !bytes.Equal(pkt2.Ciphertext, pkt.Ciphertext) {
 125  		t.Fatal("ciphertext mismatch after unmarshal")
 126  	}
 127  
 128  	got, err := GnarlOpen(secret, pkt2)
 129  	if err != nil {
 130  		t.Fatal(err)
 131  	}
 132  	if !bytes.Equal(got, plaintext) {
 133  		t.Fatal("decrypt after unmarshal failed")
 134  	}
 135  }
 136  
 137  func TestGnarlOpenWrongSecret(t *testing.T) {
 138  	secret1 := gnarlTestSecret("key-1")
 139  	secret2 := gnarlTestSecret("key-2")
 140  
 141  	identity := GMid([]byte("id"))
 142  	nonce := GnarlNonceFromCounter(0, 1)
 143  
 144  	pkt := GnarlSeal(secret1, identity, nonce, []byte("secret data"))
 145  
 146  	_, err := GnarlOpen(secret2, pkt)
 147  	if err == nil {
 148  		t.Fatal("wrong secret accepted")
 149  	}
 150  }
 151  
 152  func TestGnarlOpenTamperedCiphertext(t *testing.T) {
 153  	secret := gnarlTestSecret("tamper-test")
 154  	identity := GMid([]byte("id"))
 155  	nonce := GnarlNonceFromCounter(0, 1)
 156  
 157  	pkt := GnarlSeal(secret, identity, nonce, []byte("important data"))
 158  	pkt.Ciphertext[0] ^= 0x01
 159  
 160  	_, err := GnarlOpen(secret, pkt)
 161  	if err == nil {
 162  		t.Fatal("tampered ciphertext accepted")
 163  	}
 164  }
 165  
 166  func TestGnarlOpenTamperedIdentity(t *testing.T) {
 167  	secret := gnarlTestSecret("id-tamper")
 168  	identity := GMid([]byte("real-sender"))
 169  	nonce := GnarlNonceFromCounter(0, 1)
 170  
 171  	pkt := GnarlSeal(secret, identity, nonce, []byte("data"))
 172  	pkt.Identity[0] ^= 0xFF
 173  
 174  	_, err := GnarlOpen(secret, pkt)
 175  	if err == nil {
 176  		t.Fatal("tampered identity accepted")
 177  	}
 178  }
 179  
 180  func TestGnarlOpenTamperedNonce(t *testing.T) {
 181  	secret := gnarlTestSecret("nonce-tamper")
 182  	identity := GMid([]byte("sender"))
 183  	nonce := GnarlNonceFromCounter(0, 1)
 184  
 185  	pkt := GnarlSeal(secret, identity, nonce, []byte("data"))
 186  	pkt.Nonce[0] ^= 0x01
 187  
 188  	_, err := GnarlOpen(secret, pkt)
 189  	if err == nil {
 190  		t.Fatal("tampered nonce accepted")
 191  	}
 192  }
 193  
 194  func TestGnarlOpenNilPacket(t *testing.T) {
 195  	var secret Hamadryad
 196  	_, err := GnarlOpen(secret, nil)
 197  	if err == nil {
 198  		t.Fatal("nil packet accepted")
 199  	}
 200  }
 201  
 202  func TestUnmarshalGnarlPacketTooShort(t *testing.T) {
 203  	_, err := UnmarshalGnarlPacket(make([]byte, GnarlHeaderLen-1))
 204  	if err == nil {
 205  		t.Fatal("short packet accepted")
 206  	}
 207  }
 208  
 209  func TestUnmarshalGnarlPacketHeaderOnly(t *testing.T) {
 210  	pkt, err := UnmarshalGnarlPacket(make([]byte, GnarlHeaderLen))
 211  	if err != nil {
 212  		t.Fatalf("header-only unmarshal: %v", err)
 213  	}
 214  	if len(pkt.Ciphertext) != 0 {
 215  		t.Fatalf("ciphertext len = %d for header-only", len(pkt.Ciphertext))
 216  	}
 217  }
 218  
 219  func TestGnarlNonceFromCounter(t *testing.T) {
 220  	nonce := GnarlNonceFromCounter(0x1234, 0xDEADBEEF)
 221  	if nonce[0] != 0xEF || nonce[1] != 0xBE || nonce[2] != 0xAD || nonce[3] != 0xDE {
 222  		t.Fatalf("counter bytes wrong: %x", nonce[:8])
 223  	}
 224  	if nonce[8] != 0x34 || nonce[9] != 0x12 {
 225  		t.Fatalf("epoch bytes wrong: %x", nonce[8:10])
 226  	}
 227  }
 228  
 229  func TestGnarlSealDeterministic(t *testing.T) {
 230  	secret := gnarlTestSecret("det")
 231  	identity := GMid([]byte("id"))
 232  	nonce := GnarlNonceFromCounter(0, 0)
 233  
 234  	pkt1 := GnarlSeal(secret, identity, nonce, []byte("hello"))
 235  	pkt2 := GnarlSeal(secret, identity, nonce, []byte("hello"))
 236  	if !bytes.Equal(pkt1.Ciphertext, pkt2.Ciphertext) {
 237  		t.Fatal("Seal not deterministic")
 238  	}
 239  	if pkt1.AuthTag != pkt2.AuthTag {
 240  		t.Fatal("auth tag not deterministic")
 241  	}
 242  }
 243  
 244  func TestGnarlSealDifferentNonces(t *testing.T) {
 245  	secret := gnarlTestSecret("nonce-diff")
 246  	identity := GMid([]byte("id"))
 247  
 248  	n1 := GnarlNonceFromCounter(0, 1)
 249  	n2 := GnarlNonceFromCounter(0, 2)
 250  	plaintext := []byte("same message")
 251  
 252  	pkt1 := GnarlSeal(secret, identity, n1, plaintext)
 253  	pkt2 := GnarlSeal(secret, identity, n2, plaintext)
 254  	if bytes.Equal(pkt1.Ciphertext, pkt2.Ciphertext) {
 255  		t.Fatal("different nonces produced same ciphertext")
 256  	}
 257  	if pkt1.AuthTag == pkt2.AuthTag {
 258  		t.Fatal("different nonces produced same auth tag")
 259  	}
 260  }
 261