diff options
| author | Jakob Unterwurzacher | 2016-05-04 19:09:14 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-05-04 19:56:07 +0200 | 
| commit | 39f3a24484ffc343d2d5225d5c419082aabb9baa (patch) | |
| tree | 04b9d7aa98ef3e18f05b00efe39e5288baeed5c4 | |
| parent | c92190bf07d27a4c7fbecba5778d11c77c52574e (diff) | |
stupidgcm: completely replace spacemonkeygo/openssl
| -rw-r--r-- | Documentation/openssl-gcm.md | 13 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore.go | 7 | ||||
| -rw-r--r-- | internal/cryptocore/openssl_aead.go | 100 | ||||
| -rwxr-xr-x | internal/cryptocore/openssl_benchmark.bash | 3 | ||||
| -rw-r--r-- | internal/cryptocore/openssl_test.go | 75 | ||||
| -rw-r--r-- | openssl_benchmark/go-vs-openssl.md | 90 | ||||
| -rwxr-xr-x | openssl_benchmark/openssl_benchmark.bash | 5 | ||||
| -rw-r--r-- | openssl_benchmark/openssl_test.go | 163 | 
8 files changed, 18 insertions, 438 deletions
| diff --git a/Documentation/openssl-gcm.md b/Documentation/openssl-gcm.md new file mode 100644 index 0000000..d955b39 --- /dev/null +++ b/Documentation/openssl-gcm.md @@ -0,0 +1,13 @@ +Go builtin GCM vs OpenSSL +========================= + +OpenSSL is over four times faster than Go's built-in GCM implementation. + +``` +$ cd internal/stupidgcm +$ go test -bench . +PASS +Benchmark4kEncStupidGCM-2	   50000	     25860 ns/op	 158.39 MB/s +Benchmark4kEncGoGCM-2    	   10000	    116050 ns/op	  35.29 MB/s +ok  	github.com/rfjakob/gocryptfs/internal/stupidgcm	3.667s +``` diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index f286896..7e7f099 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -4,6 +4,8 @@ import (  	"crypto/aes"  	"crypto/cipher"  	"fmt" + +	"github.com/rfjakob/gocryptfs/internal/stupidgcm"  )  const ( @@ -39,8 +41,9 @@ func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore {  	}  	var gcm cipher.AEAD -	if useOpenssl { -		gcm = opensslGCM{key} +	if useOpenssl && GCMIV128 { +		// stupidgcm only supports 128-bit IVs +		gcm = stupidgcm.New(key)  	} else {  		gcm, err = goGCMWrapper(blockCipher, IVLen)  		if err != nil { diff --git a/internal/cryptocore/openssl_aead.go b/internal/cryptocore/openssl_aead.go deleted file mode 100644 index d4ed64b..0000000 --- a/internal/cryptocore/openssl_aead.go +++ /dev/null @@ -1,100 +0,0 @@ -package cryptocore - -// Implements cipher.AEAD with OpenSSL backend - -import ( -	"bytes" -	"github.com/spacemonkeygo/openssl" -) - -// Supports all nonce sizes -type opensslGCM struct { -	key []byte -} - -func (be opensslGCM) Overhead() int { -	return AuthTagLen -} - -func (be opensslGCM) NonceSize() int { -	// We support any nonce size -	return -1 -} - -// Seal encrypts and authenticates plaintext, authenticates the -// additional data and appends the result to dst, returning the updated -// slice. opensslGCM supports any nonce size. -func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte { - -	// Preallocate output buffer -	var cipherBuf bytes.Buffer -	cipherBuf.Grow(len(dst) + len(plaintext) + AuthTagLen) -	// Output will be appended to dst -	cipherBuf.Write(dst) - -	ectx, err := openssl.NewGCMEncryptionCipherCtx(KeyLen*8, nil, be.key, nonce) -	if err != nil { -		panic(err) -	} -	err = ectx.ExtraData(data) -	if err != nil { -		panic(err) -	} -	part, err := ectx.EncryptUpdate(plaintext) -	if err != nil { -		panic(err) -	} -	cipherBuf.Write(part) -	part, err = ectx.EncryptFinal() -	if err != nil { -		panic(err) -	} -	cipherBuf.Write(part) -	part, err = ectx.GetTag() -	if err != nil { -		panic(err) -	} -	cipherBuf.Write(part) - -	return cipherBuf.Bytes() -} - -// Open decrypts and authenticates ciphertext, authenticates the -// additional data and, if successful, appends the resulting plaintext -// to dst, returning the updated slice. The nonce must be NonceSize() -// bytes long and both it and the additional data must match the -// value passed to Seal. -// -// The ciphertext and dst may alias exactly or not at all. -func (be opensslGCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { - -	l := len(ciphertext) -	tag := ciphertext[l-AuthTagLen : l] -	ciphertext = ciphertext[0 : l-AuthTagLen] -	plainBuf := bytes.NewBuffer(dst) - -	dctx, err := openssl.NewGCMDecryptionCipherCtx(KeyLen*8, nil, be.key, nonce) -	if err != nil { -		return nil, err -	} -	err = dctx.ExtraData(data) -	if err != nil { -		return nil, err -	} -	part, err := dctx.DecryptUpdate(ciphertext) -	if err != nil { -		return nil, err -	} -	plainBuf.Write(part) -	err = dctx.SetTag(tag) -	if err != nil { -		return nil, err -	} -	part, err = dctx.DecryptFinal() -	if err != nil { -		return nil, err -	} -	plainBuf.Write(part) - -	return plainBuf.Bytes(), nil -} diff --git a/internal/cryptocore/openssl_benchmark.bash b/internal/cryptocore/openssl_benchmark.bash deleted file mode 100755 index df29628..0000000 --- a/internal/cryptocore/openssl_benchmark.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -go test -run NONE -bench BenchmarkEnc diff --git a/internal/cryptocore/openssl_test.go b/internal/cryptocore/openssl_test.go deleted file mode 100644 index 94b696a..0000000 --- a/internal/cryptocore/openssl_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package cryptocore - -// Benchmark go built-int GCM against spacemonkey openssl bindings -// -// Note: The benchmarks in this file supersede the ones in the openssl_benchmark -//       directory as they use the same code paths that gocryptfs actually uses. -// -// Run benchmark: -// go test -bench Enc - -import ( -	"crypto/aes" -	"testing" -) - -func benchmarkGoEnc(b *testing.B, plaintext []byte, key []byte, nonce []byte) (ciphertext []byte) { -	b.SetBytes(int64(len(plaintext))) -	aes, err := aes.NewCipher(key[:]) -	if err != nil { -		b.Fatal(err) -	} -	aesgcm, err := goGCMWrapper(aes, len(nonce)) -	if err != nil { -		b.Fatal(err) -	} -	// This would be fileID + blockNo -	aData := make([]byte, 24) -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		// Encrypt plaintext and append to nonce -		ciphertext = aesgcm.Seal(nonce, nonce, plaintext, aData) -	} -	return ciphertext -} - -func benchmarkOpensslEnc(b *testing.B, plaintext []byte, key []byte, nonce []byte) (ciphertext []byte) { -	b.SetBytes(int64(len(plaintext))) -	var aesgcm opensslGCM -	aesgcm.key = key -	// This would be fileID + blockNo -	aData := make([]byte, 24) -	for i := 0; i < b.N; i++ { -		// Encrypt plaintext and append to nonce -		ciphertext = aesgcm.Seal(nonce, nonce, plaintext, aData) -	} -	return ciphertext -} - -func BenchmarkEnc_Go_4k_AES256_nonce96(b *testing.B) { -	plaintext := make([]byte, 4048) -	key := make([]byte, 256/8) -	nonce := make([]byte, 96/8) -	benchmarkGoEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_Go_4k_AES256_nonce128(b *testing.B) { -	plaintext := make([]byte, 4048) -	key := make([]byte, 256/8) -	nonce := make([]byte, 128/8) -	benchmarkGoEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_OpenSSL_4k_AES256_nonce96(b *testing.B) { -	plaintext := make([]byte, 4048) -	key := make([]byte, 256/8) -	nonce := make([]byte, 96/8) -	benchmarkOpensslEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_OpenSSL_4k_AES256_nonce128(b *testing.B) { -	plaintext := make([]byte, 4048) -	key := make([]byte, 256/8) -	nonce := make([]byte, 96/8) -	benchmarkOpensslEnc(b, plaintext, key, nonce) -} diff --git a/openssl_benchmark/go-vs-openssl.md b/openssl_benchmark/go-vs-openssl.md deleted file mode 100644 index 222ae8e..0000000 --- a/openssl_benchmark/go-vs-openssl.md +++ /dev/null @@ -1,90 +0,0 @@ -Go 1.4.2 -======== - -39MB/s @1k - -	go1.4/src/crypto/cipher$ go test -bench=. - -	BenchmarkAESGCMSeal1K   	   50000	     25968 ns/op	  39.43 MB/s -	BenchmarkAESGCMOpen1K   	   50000	     25914 ns/op	  39.51 MB/s -	[...] - -Go 1.5 -====== - -41MB/s @1k - -	go1.5/src/crypto/cipher$ ~/go/src/go1.5/bin/go test -bench=. - -	BenchmarkAESGCMSeal1K-2   	   50000	     24429 ns/op	  41.92 MB/s -	BenchmarkAESGCMOpen1K-2   	   50000	     24578 ns/op	  41.66 MB/s -	BenchmarkAESGCMSeal8K-2   	   10000	    190340 ns/op	  43.04 MB/s -	BenchmarkAESGCMOpen8K-2   	   10000	    190308 ns/op	  43.05 MB/s -	[...] - -openssl 1.0.1k -============== - -302MB/s @1k - -	$ openssl speed -elapsed -evp aes-128-gcm - -	[...] -	The 'numbers' are in 1000s of bytes per second processed. -	type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes -	aes-128-gcm      71275.15k    80063.19k   275048.36k   302066.69k   308912.13k - - -gocryptfs with openssl bindings -=============================== - -148MB/s @4k - -	gocryptfs/openssl_benchmark$ ./openssl_benchmark.bash  - -	BenchmarkAESGCMSeal4K   	   20000	     98671 ns/op	  41.51 MB/s -	BenchmarkAESGCMOpen4K   	   20000	     98679 ns/op	  41.51 MB/s -	BenchmarkOpensslGCMenc4K	   50000	     27542 ns/op	 148.72 MB/s -	BenchmarkOpensslGCMdec4K	   50000	     27564 ns/op	 148.60 MB/s - - -CPU Info -======== - -This is tested on a dual-core Intel Sandy Bridge Pentium G630 which does NOT have -aes instructions ( https://en.wikipedia.org/wiki/AES_instruction_set ) - -	$ cat /proc/cpuinfo | fold -s -w 80 - -	processor	: 0 -	vendor_id	: GenuineIntel -	cpu family	: 6 -	model		: 42 -	model name	: Intel(R) Pentium(R) CPU G630 @ 2.70GHz -	stepping	: 7 -	microcode	: 0x29 -	cpu MHz		: 1617.574 -	cache size	: 3072 KB -	physical id	: 0 -	siblings	: 2 -	core id		: 0 -	cpu cores	: 2 -	apicid		: 0 -	initial apicid	: 0 -	fpu		: yes -	fpu_exception	: yes -	cpuid level	: 13 -	wp		: yes -	flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov  -	pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm  -	constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc  -	aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16  -	xtpr pdcm pcid sse4_1 sse4_2 popcnt tsc_deadline_timer xsave lahf_lm arat epb  -	pln pts dtherm tpr_shadow vnmi flexpriority ept vpid xsaveopt -	bugs		: -	bogomips	: 5387.68 -	clflush size	: 64 -	cache_alignment	: 64 -	address sizes	: 36 bits physical, 48 bits virtual -	power management: -	[...] diff --git a/openssl_benchmark/openssl_benchmark.bash b/openssl_benchmark/openssl_benchmark.bash deleted file mode 100755 index b328d24..0000000 --- a/openssl_benchmark/openssl_benchmark.bash +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -eux - -go test -bench=. diff --git a/openssl_benchmark/openssl_test.go b/openssl_benchmark/openssl_test.go deleted file mode 100644 index 1530f9b..0000000 --- a/openssl_benchmark/openssl_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package benchmark - -// Benchmark go built-int GCM against spacemonkey openssl bindings -// -// Note: This is deprecated in favor of the benchmarks integrated in cryptfs. -// -// Run benchmark: -// go test -bench=. - -import ( -	"bytes" -	"crypto/aes" -	"crypto/cipher" -	"fmt" -	"os" -	"testing" - -	"github.com/spacemonkeygo/openssl" - -	"github.com/rfjakob/gocryptfs/internal/cryptocore" -) - -func TestMain(m *testing.M) { - -	fmt.Printf("Benchmarking AES-GCM-%d with 4kB block size\n", cryptocore.KeyLen*8) - -	r := m.Run() -	os.Exit(r) -} - -func BenchmarkGoEnc4K(b *testing.B) { -	buf := make([]byte, 1024*4) -	b.SetBytes(int64(len(buf))) - -	var key [cryptocore.KeyLen]byte -	var nonce [12]byte -	aes, _ := aes.NewCipher(key[:]) -	aesgcm, _ := cipher.NewGCM(aes) -	var out []byte -	// This would be fileID + blockNo -	aData := make([]byte, 24) -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		out = aesgcm.Seal(out[:0], nonce[:], buf, aData) -	} -} - -func BenchmarkGoDec4K(b *testing.B) { -	buf := make([]byte, 1024*4) -	b.SetBytes(int64(len(buf))) - -	var key [cryptocore.KeyLen]byte -	var nonce [12]byte -	aes, _ := aes.NewCipher(key[:]) -	aesgcm, _ := cipher.NewGCM(aes) -	var out []byte -	out = aesgcm.Seal(out[:0], nonce[:], buf, nil) - -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		_, err := aesgcm.Open(buf[:0], nonce[:], out, nil) -		if err != nil { -			b.Errorf("Open: %v", err) -		} -	} -} - -func BenchmarkOpensslEnc4K(b *testing.B) { -	buf := make([]byte, 1024*4) -	b.SetBytes(int64(len(buf))) - -	var key [cryptocore.KeyLen]byte -	var nonce [12]byte - -	// This would be fileID + blockNo -	aData := make([]byte, 24) - -	var ciphertext bytes.Buffer -	var part []byte - -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		ciphertext.Reset() -		ectx, err := openssl.NewGCMEncryptionCipherCtx(cryptocore.KeyLen*8, nil, key[:], nonce[:]) -		if err != nil { -			b.FailNow() -		} -		err = ectx.ExtraData(aData) -		if err != nil { -			b.FailNow() -		} -		part, err = ectx.EncryptUpdate(buf) -		if err != nil { -			b.FailNow() -		} -		ciphertext.Write(part) -		part, err = ectx.EncryptFinal() -		if err != nil { -			b.FailNow() -		} -		ciphertext.Write(part) -		part, err = ectx.GetTag() -		if err != nil { -			b.FailNow() -		} -		ciphertext.Write(part) -	} -} - -func BenchmarkOpensslDec4K(b *testing.B) { -	buf := makeOpensslCiphertext() -	b.SetBytes(int64(1024 * 4)) - -	tag := buf[4096:] -	buf = buf[0:4096] - -	var key [cryptocore.KeyLen]byte -	var nonce [12]byte - -	var plaintext bytes.Buffer -	var part []byte - -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		plaintext.Reset() -		dctx, err := openssl.NewGCMDecryptionCipherCtx(cryptocore.KeyLen*8, nil, key[:], nonce[:]) -		if err != nil { -			b.FailNow() -		} -		part, err = dctx.DecryptUpdate(buf) -		if err != nil { -			b.FailNow() -		} -		plaintext.Write(part) -		err = dctx.SetTag(tag) -		if err != nil { -			b.FailNow() -		} -		part, err = dctx.DecryptFinal() -		if err != nil { -			b.FailNow() -		} -		plaintext.Write(part) -	} -} - -func makeOpensslCiphertext() []byte { -	buf := make([]byte, 1024*4) -	var key [cryptocore.KeyLen]byte -	var nonce [12]byte -	var ciphertext bytes.Buffer -	var part []byte - -	ectx, _ := openssl.NewGCMEncryptionCipherCtx(cryptocore.KeyLen*8, nil, key[:], nonce[:]) -	part, _ = ectx.EncryptUpdate(buf) -	ciphertext.Write(part) -	part, _ = ectx.EncryptFinal() -	ciphertext.Write(part) -	part, _ = ectx.GetTag() -	ciphertext.Write(part) - -	return ciphertext.Bytes() -} | 
