From c92190bf07d27a4c7fbecba5778d11c77c52574e Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 1 May 2016 22:26:47 +0200 Subject: stupidgcm: add our own thin wrapper around openssl gcm ...complete with tests and benchmark. This will allow us to get rid of the dependency to spacemonkeygo/openssl that causes problems on Arch Linux ( https://github.com/rfjakob/gocryptfs/issues/21 ) --- internal/stupidgcm/stupidgcm_test.go | 154 +++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 internal/stupidgcm/stupidgcm_test.go (limited to 'internal/stupidgcm/stupidgcm_test.go') diff --git a/internal/stupidgcm/stupidgcm_test.go b/internal/stupidgcm/stupidgcm_test.go new file mode 100644 index 0000000..dfd495d --- /dev/null +++ b/internal/stupidgcm/stupidgcm_test.go @@ -0,0 +1,154 @@ +package stupidgcm + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/hex" + "testing" +) + +// Get "n" random bytes from /dev/urandom or panic +func randBytes(n int) []byte { + b := make([]byte, n) + _, err := rand.Read(b) + if err != nil { + panic("Failed to read random bytes: " + err.Error()) + } + return b +} + +// TestEncryptDecrypt encrypts and decrypts using both stupidgcm and Go's built-in +// GCM implemenatation and verifies that the results are identical. +func TestEncryptDecrypt(t *testing.T) { + key := randBytes(32) + sGCM := New(key) + authData := randBytes(24) + iv := randBytes(16) + dst := make([]byte, 71) // 71 = random length + + gAES, err := aes.NewCipher(key) + if err != nil { + t.Fatal(err) + } + gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) + 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.Compare(sOut, gOut) != 0 { + 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.Compare(sOut2, gOut2) != 0 { + t.Fatalf("Compare failed for decryption, size %d", i) + } + } +} + +// TestCorruption verifies that changes in the ciphertext result in a decryption +// error +func TestCorruption(t *testing.T) { + key := randBytes(32) + sGCM := New(key) + 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.Compare(in, sOut2) != 0 { + 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") + } +} + +// $ go test -bench . +// PASS +// Benchmark4kEncStupidGCM-2 50000 25622 ns/op 159.86 MB/s +// Benchmark4kEncGoGCM-2 10000 116544 ns/op 35.15 MB/s +// ok github.com/rfjakob/gocryptfs/internal/stupidgcm 3.775s +func Benchmark4kEncStupidGCM(b *testing.B) { + key := randBytes(32) + authData := randBytes(24) + iv := randBytes(16) + in := make([]byte, 4096) + b.SetBytes(int64(len(in))) + + sGCM := New(key) + + for i := 0; i < b.N; i++ { + // Encrypt and append to nonce + sGCM.Seal(iv, iv, in, authData) + } +} + +func Benchmark4kEncGoGCM(b *testing.B) { + key := randBytes(32) + authData := randBytes(24) + iv := randBytes(16) + in := make([]byte, 4096) + b.SetBytes(int64(len(in))) + + gAES, err := aes.NewCipher(key) + if err != nil { + b.Fatal(err) + } + gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) + if err != nil { + b.Fatal(err) + } + + for i := 0; i < b.N; i++ { + // Encrypt and append to nonce + gGCM.Seal(iv, iv, in, authData) + } +} -- cgit v1.2.3