reverse: switch from GCM-SIV to AES-SIV

GCM-SIV is not yet finalized, and the reference implemenation is
painfully slow at about 2 MB/s. Switch to AES-SIV.
This commit is contained in:
Jakob Unterwurzacher 2016-09-26 23:25:13 +02:00
parent d9fc652df0
commit e9bb8b800c
13 changed files with 50 additions and 57 deletions

View File

@ -30,6 +30,9 @@ DESCRIPTION
Options: Options:
**-aessiv**
: Use the AES-SIV encryption mode (implied by -reverse)
**-allow_other** **-allow_other**
: By default, the Linux kernel prevents any other user (even root) to : By default, the Linux kernel prevents any other user (even root) to
access a mounted FUSE filesystem. Settings this option allows access for access a mounted FUSE filesystem. Settings this option allows access for
@ -58,9 +61,6 @@ to mount the gocryptfs filesytem without user interaction.
**-fusedebug** **-fusedebug**
: Enable fuse library debug output : Enable fuse library debug output
**-gcmsiv**
: Use the GCM-SIV encryption mode (implied by -reverse)
**-init** **-init**
: Initialize encrypted directory : Initialize encrypted directory

View File

@ -14,7 +14,7 @@ import (
type argContainer struct { type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version, debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
plaintextnames, quiet, nosyslog, wpanic, plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, ro, reverse, gcmsiv bool longnames, allow_other, ro, reverse, aessiv bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass, masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, o string memprofile, o string
// Configuration file name override // Configuration file name override
@ -51,7 +51,7 @@ func parseCliOpts() (args argContainer) {
"Only works if user_allow_other is set in /etc/fuse.conf.") "Only works if user_allow_other is set in /etc/fuse.conf.")
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only") flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode") flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
flagSet.BoolVar(&args.gcmsiv, "gcmsiv", false, "GCM-SIV encryption") flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
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")

View File

@ -39,7 +39,7 @@ func initDir(args *argContainer) {
} }
password := readpassword.Twice(args.extpass) password := readpassword.Twice(args.extpass)
creator := tlog.ProgramName + " " + GitVersion creator := tlog.ProgramName + " " + GitVersion
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.gcmsiv) err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ERREXIT_INIT)

View File

@ -45,7 +45,7 @@ type ConfFile struct {
// CreateConfFile - create a new config with a random key encrypted with // CreateConfFile - create a new config with a random key encrypted with
// "password" and write it to "filename". // "password" and write it to "filename".
// Uses scrypt with cost parameter logN. // Uses scrypt with cost parameter logN.
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, gcmsiv bool) error { func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool) error {
var cf ConfFile var cf ConfFile
cf.filename = filename cf.filename = filename
cf.Creator = creator cf.Creator = creator
@ -59,7 +59,7 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
cf.EncryptKey(key, password, logN) cf.EncryptKey(key, password, logN)
// Set feature flags // Set feature flags
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128]) // 128-bit IVs cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
if plaintextNames { if plaintextNames {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
} else { } else {
@ -67,8 +67,8 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
} }
if gcmsiv { if aessiv {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMSIV]) // GCM-SIV encryption mode cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
} }
// Write file to disk // Write file to disk

View File

@ -71,7 +71,7 @@ func TestCreateConfFile(t *testing.T) {
} }
func TestCreateConfFileGCMSIV(t *testing.T) { func TestCreateConfFileAESSIV(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true) err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -80,14 +80,14 @@ func TestCreateConfFileGCMSIV(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !c.IsFeatureFlagSet(FlagGCMSIV) { if !c.IsFeatureFlagSet(FlagAESSIV) {
t.Error("GCMSIV flag should be set but is not") t.Error("AESSIV flag should be set but is not")
} }
} }
func TestIsFeatureFlagKnown(t *testing.T) { func TestIsFeatureFlagKnown(t *testing.T) {
// Test a few hardcoded values // Test a few hardcoded values
testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "GCMSIV"} testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"}
// And also everything in knownFlags (yes, it is likely that we end up with // And also everything in knownFlags (yes, it is likely that we end up with
// some duplicates. Does not matter.) // some duplicates. Does not matter.)
for _, f := range knownFlags { for _, f := range knownFlags {

View File

@ -8,7 +8,7 @@ const (
FlagEMENames FlagEMENames
FlagGCMIV128 FlagGCMIV128
FlagLongNames FlagLongNames
FlagGCMSIV FlagAESSIV
) )
// knownFlags stores the known feature flags and their string representation // knownFlags stores the known feature flags and their string representation
@ -18,7 +18,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{
FlagEMENames: "EMENames", FlagEMENames: "EMENames",
FlagGCMIV128: "GCMIV128", FlagGCMIV128: "GCMIV128",
FlagLongNames: "LongNames", FlagLongNames: "LongNames",
FlagGCMSIV: "GCMSIV", FlagAESSIV: "AESSIV",
} }
// Filesystems that do not have these feature flags set are deprecated. // Filesystems that do not have these feature flags set are deprecated.

View File

@ -105,8 +105,8 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
// Extract nonce // Extract nonce
nonce := ciphertext[:be.cryptoCore.IVLen] nonce := ciphertext[:be.cryptoCore.IVLen]
if bytes.Equal(nonce, be.allZeroNonce) && be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { if bytes.Equal(nonce, be.allZeroNonce) {
panic("Hit an all-zero nonce with GCMSIV off. This MUST NOT happen!") panic("Hit an all-zero nonce. This MUST NOT happen!")
} }
ciphertextOrig := ciphertext ciphertextOrig := ciphertext
ciphertext = ciphertext[be.cryptoCore.IVLen:] ciphertext = ciphertext[be.cryptoCore.IVLen:]
@ -150,13 +150,13 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by
var nonce []byte var nonce []byte
switch nMode { switch nMode {
case ExternalNonce: case ExternalNonce:
if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
panic("MUST NOT use deterministic nonces unless in GCMSIV mode!") panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
} }
nonce = externalNonce nonce = externalNonce
case ReverseDeterministicNonce: case ReverseDeterministicNonce:
if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
panic("MUST NOT use deterministic nonces unless in GCMSIV mode!") panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
} }
l := be.cryptoCore.IVLen l := be.cryptoCore.IVLen
nonce = make([]byte, l) nonce = make([]byte, l)

View File

@ -7,9 +7,8 @@ import (
"crypto/cipher" "crypto/cipher"
"fmt" "fmt"
"github.com/rfjakob/gocryptfs/internal/siv_aead"
"github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gcmsiv"
) )
type BackendTypeEnum int type BackendTypeEnum int
@ -21,13 +20,13 @@ const (
_ = iota // Skip zero _ = iota // Skip zero
BackendOpenSSL BackendTypeEnum = iota BackendOpenSSL BackendTypeEnum = iota
BackendGoGCM BackendTypeEnum = iota BackendGoGCM BackendTypeEnum = iota
BackendGCMSIV BackendTypeEnum = iota BackendAESSIV BackendTypeEnum = iota
) )
type CryptoCore struct { type CryptoCore struct {
// AES-256 block cipher. This is used for EME filename encryption. // AES-256 block cipher. This is used for EME filename encryption.
BlockCipher cipher.Block BlockCipher cipher.Block
// GCM or GCM-SIV. This is used for content encryption. // GCM or AES-SIV. This is used for content encryption.
AEADCipher cipher.AEAD AEADCipher cipher.AEAD
// Which backend is behind AEADCipher? // Which backend is behind AEADCipher?
AEADBackend BackendTypeEnum AEADBackend BackendTypeEnum
@ -64,8 +63,8 @@ func New(key []byte, backend BackendTypeEnum, IVBitLen int) *CryptoCore {
gcm = stupidgcm.New(key) gcm = stupidgcm.New(key)
case BackendGoGCM: case BackendGoGCM:
gcm, err = goGCMWrapper(blockCipher, IVLen) gcm, err = goGCMWrapper(blockCipher, IVLen)
case BackendGCMSIV: case BackendAESSIV:
gcm, err = gcmsiv.NewGCMSIV(key) gcm = siv_aead.New(key)
default: default:
panic("unknown backend cipher") panic("unknown backend cipher")
} }

View File

@ -15,7 +15,8 @@ import (
"encoding/hex" "encoding/hex"
"testing" "testing"
"github.com/rfjakob/gcmsiv" // 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
@ -162,16 +163,13 @@ func Benchmark4kEncGoGCM(b *testing.B) {
} }
} }
func Benchmark4kEncGCMSIV(b *testing.B) { func Benchmark4kEncAESSIV(b *testing.B) {
key := randBytes(32) key := randBytes(32)
authData := randBytes(24) authData := randBytes(24)
iv := randBytes(16) iv := randBytes(16)
in := make([]byte, 4096) in := make([]byte, 4096)
b.SetBytes(int64(len(in))) b.SetBytes(int64(len(in)))
gGCM, err := gcmsiv.NewGCMSIV(key) gGCM := siv_aead.New(key)
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
// Encrypt and append to nonce // Encrypt and append to nonce
gGCM.Seal(iv, iv, in, authData) gGCM.Seal(iv, iv, in, authData)

19
main.go
View File

@ -150,9 +150,9 @@ func main() {
if args.quiet { if args.quiet {
tlog.Info.Enabled = false tlog.Info.Enabled = false
} }
// "-reverse" implies "-gcmsiv" // "-reverse" implies "-aessiv"
if args.reverse { if args.reverse {
args.gcmsiv = true args.aessiv = true
} }
// "-config" // "-config"
if args.config != "" { if args.config != "" {
@ -290,8 +290,8 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
if args.openssl { if args.openssl {
cryptoBackend = cryptocore.BackendOpenSSL cryptoBackend = cryptocore.BackendOpenSSL
} }
if args.gcmsiv { if args.aessiv {
cryptoBackend = cryptocore.BackendGCMSIV cryptoBackend = cryptocore.BackendAESSIV
} }
frontendArgs := fusefrontend.Args{ frontendArgs := fusefrontend.Args{
Cipherdir: args.cipherdir, Cipherdir: args.cipherdir,
@ -304,10 +304,10 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
if confFile != nil { if confFile != nil {
// Settings from the config file override command line args // Settings from the config file override command line args
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames) frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) { if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
} else if args.reverse { } else if args.reverse {
tlog.Fatal.Printf("GCM-SIV is required by reverse mode, but not enabled in the config file") tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
os.Exit(ERREXIT_USAGE) os.Exit(ERREXIT_USAGE)
} }
} }
@ -318,11 +318,6 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
} }
jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t") jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes)) tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
if frontendArgs.CryptoBackend == cryptocore.BackendGCMSIV {
tlog.Info.Printf(tlog.ColorYellow +
"Warning: The GCM-SIV format used by reverse mode is not yet finalized.\n" +
"The on-disk format will change in the future." + tlog.ColorReset)
}
var finalFs pathfs.FileSystem var finalFs pathfs.FileSystem
if args.reverse { if args.reverse {
finalFs = fusefrontend_reverse.NewFS(frontendArgs) finalFs = fusefrontend_reverse.NewFS(frontendArgs)

View File

@ -27,20 +27,20 @@ func TestInit(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if c.IsFeatureFlagSet(configfile.FlagGCMSIV) { if c.IsFeatureFlagSet(configfile.FlagAESSIV) {
t.Error("GCMSIV flag should not be set") t.Error("AESSIV flag should not be set")
} }
} }
// Test -init with -gcmsiv // Test -init with -aessiv
func TestInitGcmsiv(t *testing.T) { func TestInitAessiv(t *testing.T) {
dir := test_helpers.InitFS(t, "-gcmsiv") dir := test_helpers.InitFS(t, "-aessiv")
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test") _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) { if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
t.Error("GCMSIV flag should be set but is not") t.Error("AESSIV flag should be set but is not")
} }
} }
@ -51,8 +51,8 @@ func TestInitReverse(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) { if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
t.Error("GCMSIV flag should be set but is not") t.Error("AESSIV flag should be set but is not")
} }
} }

View File

@ -28,4 +28,5 @@ gocryptfs -q -extpass="echo test" b c
# Check md5 sums # Check md5 sums
cd c cd c
echo "Checking md5 sums" echo "Checking md5 sums"
md5sum --status -c $MD5 set -o pipefail
md5sum -c $MD5 | pv -l -s 36782 -N "files checked" | (grep -v ": OK" || true)

View File

@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
panic(err) panic(err)
} }
test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse") test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse")
test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-gcmsiv") test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-aessiv")
r := m.Run() r := m.Run()
test_helpers.UnmountPanic(dirC) test_helpers.UnmountPanic(dirC)
test_helpers.UnmountPanic(dirB) test_helpers.UnmountPanic(dirB)