diff options
| author | Jakob Unterwurzacher | 2021-09-02 09:57:06 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2021-09-02 09:57:20 +0200 | 
| commit | 676a4ceb87e8c8e0811b4312ce4b3b74f53b4368 (patch) | |
| tree | 997f610c7225792c18893bcbea24edbd32c165cd | |
| parent | c9b090770ac457090224c407aefa47c55394fb70 (diff) | |
stupidgcm: deduplicate tests 1/2
Pull the code shared between chacha and gcm into
generic functions.
| -rw-r--r-- | internal/stupidgcm/common_test.go | 152 | ||||
| -rw-r--r-- | internal/stupidgcm/stupidchacha_test.go | 131 | ||||
| -rw-r--r-- | internal/stupidgcm/stupidgcm_test.go | 114 | 
3 files changed, 167 insertions, 230 deletions
diff --git a/internal/stupidgcm/common_test.go b/internal/stupidgcm/common_test.go new file mode 100644 index 0000000..28f3308 --- /dev/null +++ b/internal/stupidgcm/common_test.go @@ -0,0 +1,152 @@ +package stupidgcm + +import ( +	"bytes" +	"crypto/cipher" +	"encoding/hex" +	"testing" +) + +// testEncryptDecrypt encrypts and decrypts using both stupidgcm and Go's built-in +// GCM implementation and verifies that the results are identical. +func testEncryptDecrypt(t *testing.T, c1 cipher.AEAD, c2 cipher.AEAD) { +	if c1.NonceSize() != c2.NonceSize() { +		t.Fatal("different NonceSize") +	} +	if c1.Overhead() != c2.Overhead() { +		t.Fatal("different Overhead") +	} + +	authData := randBytes(24) +	iv := randBytes(c1.NonceSize()) + +	dst := make([]byte, 71) // 71 = arbitrary length + +	// Check all block sizes from 1 to 5000 +	for i := 1; i < 5000; i++ { +		in := make([]byte, i) + +		c1out := c1.Seal(dst, iv, in, authData) +		c2out := c2.Seal(dst, iv, in, authData) + +		// Ciphertext must be identical to Go GCM +		if !bytes.Equal(c1out, c2out) { +			t.Fatalf("Compare failed for encryption, size %d", i) +			t.Log("c1out:") +			t.Log("\n" + hex.Dump(c1out)) +			t.Log("c2out:") +			t.Log("\n" + hex.Dump(c2out)) +		} + +		c1out2, sErr := c1.Open(dst, iv, c1out[len(dst):], authData) +		if sErr != nil { +			t.Fatal(sErr) +		} +		c2out2, gErr := c2.Open(dst, iv, c2out[len(dst):], authData) +		if gErr != nil { +			t.Fatal(gErr) +		} + +		// Plaintext must be identical to Go GCM +		if !bytes.Equal(c1out2, c2out2) { +			t.Fatalf("Compare failed for decryption, size %d", i) +		} +	} +} + +// Seal re-uses the "dst" buffer it is large enough. +// Check that this works correctly by testing different "dst" capacities from +// 5000 to 16 and "in" lengths from 1 to 5000. +func testInplaceSeal(t *testing.T, c1 cipher.AEAD, c2 cipher.AEAD) { +	authData := randBytes(24) +	iv := randBytes(c1.NonceSize()) + +	max := 5016 +	// Check all block sizes from 1 to 5000 +	for i := 1; i < max-16; i++ { +		in := make([]byte, i) +		dst := make([]byte, max-i) +		dst = dst[:16] + +		c1out := c1.Seal(dst, iv, in, authData) +		dst2 := make([]byte, 16) +		c2out := c2.Seal(dst2, iv, in, authData) + +		// Ciphertext must be identical to Go GCM +		if !bytes.Equal(c1out, c2out) { +			t.Fatalf("Compare failed for encryption, size %d", i) +			t.Log("sOut:") +			t.Log("\n" + hex.Dump(c1out)) +			t.Log("gOut:") +			t.Log("\n" + hex.Dump(c2out)) +		} +	} +} + +// testInplaceOpen - Open re-uses the "dst" buffer it is large enough. +// Check that this works correctly by testing different "dst" capacities from +// 5000 to 16 and "in" lengths from 1 to 5000. +func testInplaceOpen(t *testing.T, c1 cipher.AEAD, c2 cipher.AEAD) { +	authData := randBytes(24) +	iv := randBytes(c1.NonceSize()) + +	max := 5016 +	// Check all block sizes from 1 to 5000 +	for i := 1; i < max-c1.NonceSize(); i++ { +		in := make([]byte, i) + +		c2ciphertext := c2.Seal(iv, iv, in, authData) + +		dst := make([]byte, max-i) +		// sPlaintext ... stupidgcm plaintext +		c1plaintext, err := c1.Open(dst[:0], iv, c2ciphertext[c1.NonceSize():], authData) +		if err != nil { +			t.Fatal(err) +		} + +		// Plaintext must be identical to Go GCM +		if !bytes.Equal(in, c1plaintext) { +			t.Fatalf("Compare failed, i=%d", i) +		} +	} +} + +// testCorruption verifies that changes in the ciphertext result in a decryption +// error +func testCorruption(t *testing.T, c cipher.AEAD) { +	authData := randBytes(24) +	iv := randBytes(c.NonceSize()) + +	in := make([]byte, 354) +	out := c.Seal(nil, iv, in, authData) +	out2, sErr := c.Open(nil, iv, out, authData) +	if sErr != nil { +		t.Fatal(sErr) +	} +	if !bytes.Equal(in, out2) { +		t.Fatalf("Compare failed") +	} + +	// Corrupt first byte +	out[0]++ +	out2, sErr = c.Open(nil, iv, out, authData) +	if sErr == nil || out2 != nil { +		t.Fatalf("Should have gotten error") +	} +	out[0]-- + +	// Corrupt last byte +	out[len(out)-1]++ +	out2, sErr = c.Open(nil, iv, out, authData) +	if sErr == nil || out2 != nil { +		t.Fatalf("Should have gotten error") +	} +	out[len(out)-1]-- + +	// Append one byte +	out = append(out, 0) +	out2, sErr = c.Open(nil, iv, out, authData) +	if sErr == nil || out2 != nil { +		t.Fatalf("Should have gotten error") +	} +} diff --git a/internal/stupidgcm/stupidchacha_test.go b/internal/stupidgcm/stupidchacha_test.go index 010055f..c1086d1 100644 --- a/internal/stupidgcm/stupidchacha_test.go +++ b/internal/stupidgcm/stupidchacha_test.go @@ -6,8 +6,6 @@  package stupidgcm  import ( -	"bytes" -	"encoding/hex"  	"testing"  	"golang.org/x/crypto/chacha20poly1305" @@ -17,46 +15,13 @@ import (  // GCM implementation and verifies that the results are identical.  func TestEncryptDecryptChacha(t *testing.T) {  	key := randBytes(32) -	sGCM := newChacha20poly1305(key) -	authData := randBytes(24) -	iv := randBytes(sGCM.NonceSize()) -	dst := make([]byte, 71) // 71 = random length - -	gGCM, err := chacha20poly1305.New(key) +	c := newChacha20poly1305(key) +	ref, err := chacha20poly1305.New(key)  	if err != nil {  		t.Fatal(err)  	} -	// Check all block sizes from 1 to 5000 -	for i := 1; i < 5000; i++ { -		in := make([]byte, i) - -		sOut := sGCM.Seal(dst, iv, in, authData) -		gOut := gGCM.Seal(dst, iv, in, authData) - -		// Ciphertext must be identical to Go GCM -		if !bytes.Equal(sOut, gOut) { -			t.Fatalf("Compare failed for encryption, size %d", i) -			t.Log("sOut:") -			t.Log("\n" + hex.Dump(sOut)) -			t.Log("gOut:") -			t.Log("\n" + hex.Dump(gOut)) -		} - -		sOut2, sErr := sGCM.Open(dst, iv, sOut[len(dst):], authData) -		if sErr != nil { -			t.Fatal(sErr) -		} -		gOut2, gErr := gGCM.Open(dst, iv, gOut[len(dst):], authData) -		if gErr != nil { -			t.Fatal(gErr) -		} - -		// Plaintext must be identical to Go GCM -		if !bytes.Equal(sOut2, gOut2) { -			t.Fatalf("Compare failed for decryption, size %d", i) -		} -	} +	testEncryptDecrypt(t, c, ref)  }  // Seal re-uses the "dst" buffer it is large enough. @@ -64,34 +29,13 @@ func TestEncryptDecryptChacha(t *testing.T) {  // 5000 to 16 and "in" lengths from 1 to 5000.  func TestInplaceSealChacha(t *testing.T) {  	key := randBytes(32) -	sGCM := newChacha20poly1305(key) -	authData := randBytes(24) -	iv := randBytes(sGCM.NonceSize()) - -	gGCM, err := chacha20poly1305.New(key) +	c := newChacha20poly1305(key) +	ref, err := chacha20poly1305.New(key)  	if err != nil {  		t.Fatal(err)  	} -	max := 5016 -	// Check all block sizes from 1 to 5000 -	for i := 1; i < max-len(iv); i++ { -		in := make([]byte, i) -		dst := make([]byte, max-i) -		dst = dst[:len(iv)] - -		sOut := sGCM.Seal(dst, iv, in, authData) -		dst2 := make([]byte, len(iv)) -		gOut := gGCM.Seal(dst2, iv, in, authData) -		// Ciphertext must be identical to Go GCM -		if !bytes.Equal(sOut, gOut) { -			t.Fatalf("Compare failed for encryption, size %d", i) -			t.Log("sOut:") -			t.Log("\n" + hex.Dump(sOut)) -			t.Log("gOut:") -			t.Log("\n" + hex.Dump(gOut)) -		} -	} +	testInplaceSeal(t, c, ref)  }  // Open re-uses the "dst" buffer it is large enough. @@ -99,73 +43,20 @@ func TestInplaceSealChacha(t *testing.T) {  // 5000 to 16 and "in" lengths from 1 to 5000.  func TestInplaceOpenChacha(t *testing.T) {  	key := randBytes(32) -	sGCM := newChacha20poly1305(key) -	authData := randBytes(24) -	iv := randBytes(sGCM.NonceSize()) - -	gGCM, err := chacha20poly1305.New(key) +	c := newChacha20poly1305(key) +	ref, err := chacha20poly1305.New(key)  	if err != nil {  		t.Fatal(err)  	} -	max := 5016 -	// Check all block sizes from 1 to 5000 -	for i := 1; i < max-len(iv); i++ { -		in := make([]byte, i) - -		gCiphertext := gGCM.Seal(iv, iv, in, authData) -		dst := make([]byte, max-i) -		// sPlaintext ... stupidgcm plaintext -		sPlaintext, err := sGCM.Open(dst[:0], iv, gCiphertext[len(iv):], authData) -		if err != nil { -			t.Fatal(err) -		} - -		// Plaintext must be identical to Go GCM -		if !bytes.Equal(in, sPlaintext) { -			t.Fatalf("Compare failed, i=%d", i) -		} -	} +	testInplaceOpen(t, c, ref)  }  // TestCorruption verifies that changes in the ciphertext result in a decryption  // error  func TestCorruptionChacha(t *testing.T) {  	key := randBytes(32) -	sGCM := newChacha20poly1305(key) -	authData := randBytes(24) -	iv := randBytes(sGCM.NonceSize()) - -	in := make([]byte, 354) -	sOut := sGCM.Seal(nil, iv, in, authData) -	sOut2, sErr := sGCM.Open(nil, iv, sOut, authData) -	if sErr != nil { -		t.Fatal(sErr) -	} -	if !bytes.Equal(in, sOut2) { -		t.Fatalf("Compare failed") -	} +	c := newChacha20poly1305(key) -	// Corrupt first byte -	sOut[0]++ -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} -	sOut[0]-- - -	// Corrupt last byte -	sOut[len(sOut)-1]++ -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} -	sOut[len(sOut)-1]-- - -	// Append one byte -	sOut = append(sOut, 0) -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} +	testCorruption(t, c)  } diff --git a/internal/stupidgcm/stupidgcm_test.go b/internal/stupidgcm/stupidgcm_test.go index 18732df..968034e 100644 --- a/internal/stupidgcm/stupidgcm_test.go +++ b/internal/stupidgcm/stupidgcm_test.go @@ -6,11 +6,9 @@  package stupidgcm  import ( -	"bytes"  	"crypto/aes"  	"crypto/cipher"  	"crypto/rand" -	"encoding/hex"  	"log"  	"testing"  ) @@ -30,9 +28,6 @@ func randBytes(n int) []byte {  func TestEncryptDecrypt(t *testing.T) {  	key := randBytes(32)  	sGCM := New(key, false) -	authData := randBytes(24) -	iv := randBytes(16) -	dst := make([]byte, 71) // 71 = random length  	gAES, err := aes.NewCipher(key)  	if err != nil { @@ -43,36 +38,7 @@ func TestEncryptDecrypt(t *testing.T) {  		t.Fatal(err)  	} -	// Check all block sizes from 1 to 5000 -	for i := 1; i < 5000; i++ { -		in := make([]byte, i) - -		sOut := sGCM.Seal(dst, iv, in, authData) -		gOut := gGCM.Seal(dst, iv, in, authData) - -		// Ciphertext must be identical to Go GCM -		if !bytes.Equal(sOut, gOut) { -			t.Fatalf("Compare failed for encryption, size %d", i) -			t.Log("sOut:") -			t.Log("\n" + hex.Dump(sOut)) -			t.Log("gOut:") -			t.Log("\n" + hex.Dump(gOut)) -		} - -		sOut2, sErr := sGCM.Open(dst, iv, sOut[len(dst):], authData) -		if sErr != nil { -			t.Fatal(sErr) -		} -		gOut2, gErr := gGCM.Open(dst, iv, gOut[len(dst):], authData) -		if gErr != nil { -			t.Fatal(gErr) -		} - -		// Plaintext must be identical to Go GCM -		if !bytes.Equal(sOut2, gOut2) { -			t.Fatalf("Compare failed for decryption, size %d", i) -		} -	} +	testEncryptDecrypt(t, sGCM, gGCM)  }  // Seal re-uses the "dst" buffer it is large enough. @@ -81,8 +47,6 @@ func TestEncryptDecrypt(t *testing.T) {  func TestInplaceSeal(t *testing.T) {  	key := randBytes(32)  	sGCM := New(key, false) -	authData := randBytes(24) -	iv := randBytes(16)  	gAES, err := aes.NewCipher(key)  	if err != nil { @@ -92,26 +56,8 @@ func TestInplaceSeal(t *testing.T) {  	if err != nil {  		t.Fatal(err)  	} -	max := 5016 -	// Check all block sizes from 1 to 5000 -	for i := 1; i < max-16; i++ { -		in := make([]byte, i) -		dst := make([]byte, max-i) -		dst = dst[:16] -		sOut := sGCM.Seal(dst, iv, in, authData) -		dst2 := make([]byte, 16) -		gOut := gGCM.Seal(dst2, iv, in, authData) - -		// Ciphertext must be identical to Go GCM -		if !bytes.Equal(sOut, gOut) { -			t.Fatalf("Compare failed for encryption, size %d", i) -			t.Log("sOut:") -			t.Log("\n" + hex.Dump(sOut)) -			t.Log("gOut:") -			t.Log("\n" + hex.Dump(gOut)) -		} -	} +	testInplaceSeal(t, sGCM, gGCM)  }  // Open re-uses the "dst" buffer it is large enough. @@ -120,8 +66,6 @@ func TestInplaceSeal(t *testing.T) {  func TestInplaceOpen(t *testing.T) {  	key := randBytes(32)  	sGCM := New(key, false) -	authData := randBytes(24) -	iv := randBytes(16)  	gAES, err := aes.NewCipher(key)  	if err != nil { @@ -131,25 +75,8 @@ func TestInplaceOpen(t *testing.T) {  	if err != nil {  		t.Fatal(err)  	} -	max := 5016 -	// Check all block sizes from 1 to 5000 -	for i := 1; i < max-16; i++ { -		in := make([]byte, i) - -		gCiphertext := gGCM.Seal(iv, iv, in, authData) - -		dst := make([]byte, max-i) -		// sPlaintext ... stupidgcm plaintext -		sPlaintext, err := sGCM.Open(dst[:0], iv, gCiphertext[16:], authData) -		if err != nil { -			t.Fatal(err) -		} -		// Plaintext must be identical to Go GCM -		if !bytes.Equal(in, sPlaintext) { -			t.Fatalf("Compare failed, i=%d", i) -		} -	} +	testInplaceOpen(t, sGCM, gGCM)  }  // TestCorruption verifies that changes in the ciphertext result in a decryption @@ -157,39 +84,6 @@ func TestInplaceOpen(t *testing.T) {  func TestCorruption(t *testing.T) {  	key := randBytes(32)  	sGCM := New(key, false) -	authData := randBytes(24) -	iv := randBytes(16) -	in := make([]byte, 354) -	sOut := sGCM.Seal(nil, iv, in, authData) -	sOut2, sErr := sGCM.Open(nil, iv, sOut, authData) -	if sErr != nil { -		t.Fatal(sErr) -	} -	if !bytes.Equal(in, sOut2) { -		t.Fatalf("Compare failed") -	} - -	// Corrupt first byte -	sOut[0]++ -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} -	sOut[0]-- - -	// Corrupt last byte -	sOut[len(sOut)-1]++ -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} -	sOut[len(sOut)-1]-- - -	// Append one byte -	sOut = append(sOut, 0) -	sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) -	if sErr == nil || sOut2 != nil { -		t.Fatalf("Should have gotten error") -	} +	testCorruption(t, sGCM)  }  | 
