Implement "gocryptfs -speed"

A crypto benchmark mode like "openssl speed".

Example run:

  $ ./gocryptfs -speed
  AES-GCM-256-OpenSSL 	 180.89 MB/s	(selected in auto mode)
  AES-GCM-256-Go      	  48.19 MB/s
  AES-SIV-512-Go      	  37.40 MB/s
This commit is contained in:
Jakob Unterwurzacher 2017-02-22 23:55:43 +01:00
parent b056776a01
commit 1e03e059fa
7 changed files with 158 additions and 59 deletions

View File

@ -20,6 +20,7 @@ script:
- go build - go build
- ./build-without-openssl.bash - ./build-without-openssl.bash
- ./build.bash - ./build.bash
- ./gocryptfs -speed
- ./test.bash - ./test.bash
# fuse on travis # fuse on travis

View File

@ -18,7 +18,7 @@ type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, fg, version, debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
plaintextnames, quiet, nosyslog, wpanic, plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64, longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
noprealloc bool noprealloc, speed bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass, masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, ko, passfile, ctlsock, fsname string memprofile, ko, passfile, ctlsock, fsname string
// Configuration file name override // Configuration file name override
@ -110,6 +110,7 @@ func parseCliOpts() (args argContainer) {
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories") flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
flagSet.BoolVar(&args.raw64, "raw64", false, "Use unpadded base64 for file names") flagSet.BoolVar(&args.raw64, "raw64", false, "Use unpadded base64 for file names")
flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing") flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing")
flagSet.BoolVar(&args.speed, "speed", false, "Run crypto speed test")
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key") flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file") flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file") flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")

BIN
gocryptfs.gz Executable file

Binary file not shown.

120
internal/speed/speed.go Normal file
View File

@ -0,0 +1,120 @@
// Package speed implements the "-speed" command-line option,
// similar to "openssl speed".
// It benchmarks the crypto algorithms and libraries used by
// gocryptfs.
package speed
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"log"
"testing"
"github.com/rfjakob/gocryptfs/internal/prefer_openssl"
"github.com/rfjakob/gocryptfs/internal/siv_aead"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
)
func Run() {
bTable := []struct {
name string
f func(*testing.B)
preferred bool
}{
{name: "AES-GCM-256-OpenSSL", f: bStupidGCM, preferred: prefer_openssl.PreferOpenSSL()},
{name: "AES-GCM-256-Go", f: bGoGCM, preferred: !prefer_openssl.PreferOpenSSL()},
{name: "AES-SIV-512-Go", f: bAESSIV, preferred: false},
}
for _, b := range bTable {
fmt.Printf("%-20s\t", b.name)
mbs := mbPerSec(testing.Benchmark(b.f))
if mbs > 0 {
fmt.Printf("%7.2f MB/s", mbs)
} else {
fmt.Printf(" N/A")
}
if b.preferred {
fmt.Printf("\t(selected in auto mode)\n")
} else {
fmt.Printf("\t\n")
}
}
}
func mbPerSec(r testing.BenchmarkResult) float64 {
if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
return 0
}
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
}
// 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 {
log.Panic("Failed to read random bytes: " + err.Error())
}
return b
}
const blockSize = 4096
func bStupidGCM(b *testing.B) {
if stupidgcm.BuiltWithoutOpenssl {
b.Skip("openssl has been disabled at compile-time")
}
key := randBytes(32)
authData := randBytes(24)
iv := randBytes(16)
in := make([]byte, blockSize)
b.SetBytes(int64(len(in)))
sGCM := stupidgcm.New(key)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Encrypt and append to nonce
sGCM.Seal(iv, iv, in, authData)
}
}
func bGoGCM(b *testing.B) {
key := randBytes(32)
authData := randBytes(24)
iv := randBytes(16)
in := make([]byte, blockSize)
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)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Encrypt and append to nonce
gGCM.Seal(iv, iv, in, authData)
}
}
func bAESSIV(b *testing.B) {
key := randBytes(64)
authData := randBytes(24)
iv := randBytes(16)
in := make([]byte, blockSize)
b.SetBytes(int64(len(in)))
gGCM := siv_aead.New(key)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Encrypt and append to nonce
gGCM.Seal(iv, iv, in, authData)
}
}

View File

@ -0,0 +1,29 @@
package speed
/*
Make the "-speed" benchmarks also accessible to the standard test system.
Example run:
$ go test -bench .
BenchmarkStupidGCM-2 100000 22552 ns/op 181.62 MB/s
BenchmarkGoGCM-2 20000 81871 ns/op 50.03 MB/s
BenchmarkAESSIV-2 10000 104623 ns/op 39.15 MB/s
PASS
ok github.com/rfjakob/gocryptfs/internal/speed 6.022s
*/
import (
"testing"
)
func BenchmarkStupidGCM(b *testing.B) {
bStupidGCM(b)
}
func BenchmarkGoGCM(b *testing.B) {
bGoGCM(b)
}
func BenchmarkAESSIV(b *testing.B) {
bAESSIV(b)
}

View File

@ -15,9 +15,6 @@ import (
"encoding/hex" "encoding/hex"
"log" "log"
"testing" "testing"
// For benchmark comparison
"github.com/rfjakob/gocryptfs/internal/siv_aead"
) )
// Get "n" random bytes from /dev/urandom or panic // Get "n" random bytes from /dev/urandom or panic
@ -121,58 +118,3 @@ func TestCorruption(t *testing.T) {
t.Fatalf("Should have gotten error") 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)
}
}
func Benchmark4kEncAESSIV(b *testing.B) {
key := randBytes(32)
authData := randBytes(24)
iv := randBytes(16)
in := make([]byte, 4096)
b.SetBytes(int64(len(in)))
gGCM := siv_aead.New(key)
for i := 0; i < b.N; i++ {
// Encrypt and append to nonce
gGCM.Seal(iv, iv, in, authData)
}
}

View File

@ -13,6 +13,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/configfile" "github.com/rfjakob/gocryptfs/internal/configfile"
"github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/contentenc"
"github.com/rfjakob/gocryptfs/internal/readpassword" "github.com/rfjakob/gocryptfs/internal/readpassword"
"github.com/rfjakob/gocryptfs/internal/speed"
"github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
@ -149,6 +150,11 @@ func main() {
printVersion() printVersion()
os.Exit(0) os.Exit(0)
} }
// "-speed"
if args.speed {
speed.Run()
os.Exit(0)
}
if args.wpanic { if args.wpanic {
tlog.Warn.Wpanic = true tlog.Warn.Wpanic = true
tlog.Debug.Printf("Panicing on warnings") tlog.Debug.Printf("Panicing on warnings")