summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/openssl-gcm.md13
-rw-r--r--internal/cryptocore/cryptocore.go7
-rw-r--r--internal/cryptocore/openssl_aead.go100
-rwxr-xr-xinternal/cryptocore/openssl_benchmark.bash3
-rw-r--r--internal/cryptocore/openssl_test.go75
-rw-r--r--openssl_benchmark/go-vs-openssl.md90
-rwxr-xr-xopenssl_benchmark/openssl_benchmark.bash5
-rw-r--r--openssl_benchmark/openssl_test.go163
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()
-}