summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2016-09-20 21:58:04 +0200
committerJakob Unterwurzacher2016-09-25 16:43:17 +0200
commit7f87ed78f2f27831f2fa9409106846e3288c6f6e (patch)
treeb756236a7a9fcfce9f20eeee5ce4ba7aa51b00d9
parentd1762c5b95c3279b0a2dfa3df5c99fe59922b666 (diff)
cryptocore: add support for GCM-SIV
-rw-r--r--internal/configfile/config_file.go4
-rw-r--r--internal/configfile/feature_flags.go4
-rw-r--r--internal/contentenc/content.go8
-rw-r--r--internal/contentenc/content_test.go6
-rw-r--r--internal/cryptocore/cryptocore.go48
-rw-r--r--internal/cryptocore/cryptocore_go1.4_test.go5
-rw-r--r--internal/cryptocore/cryptocore_go1.5_test.go2
-rw-r--r--internal/cryptocore/cryptocore_test.go14
-rw-r--r--internal/fusefrontend/args.go6
-rw-r--r--internal/fusefrontend/fs.go2
-rw-r--r--internal/fusefrontend_reverse/rfs.go2
-rw-r--r--main.go20
12 files changed, 75 insertions, 46 deletions
diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go
index b36980f..178890b 100644
--- a/internal/configfile/config_file.go
+++ b/internal/configfile/config_file.go
@@ -139,7 +139,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
// Unlock master key using password-based key
// We use stock go GCM instead of OpenSSL here as we only use 96-bit IVs,
// speed is not important and we get better error messages
- cc := cryptocore.New(scryptHash, false, false)
+ cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, 96)
ce := contentenc.New(cc, 4096)
tlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password
@@ -163,7 +163,7 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
scryptHash := cf.ScryptObject.DeriveKey(password)
// Lock master key using password-based key
- cc := cryptocore.New(scryptHash, false, false)
+ cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, 96)
ce := contentenc.New(cc, 4096)
cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)
}
diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go
index bac8ce8..90b8c22 100644
--- a/internal/configfile/feature_flags.go
+++ b/internal/configfile/feature_flags.go
@@ -8,6 +8,7 @@ const (
FlagEMENames
FlagGCMIV128
FlagLongNames
+ FlagGCMSIV
)
// knownFlags stores the known feature flags and their string representation
@@ -17,6 +18,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{
FlagEMENames: "EMENames",
FlagGCMIV128: "GCMIV128",
FlagLongNames: "LongNames",
+ FlagGCMSIV: "GCMSIV",
}
// Filesystems that do not have these feature flags set are deprecated.
@@ -27,7 +29,7 @@ var requiredFlagsNormal []flagIota = []flagIota{
}
// Filesystems without filename encryption obviously don't have or need the
-// related feature flags.
+// filename related feature flags.
var requiredFlagsPlaintextNames []flagIota = []flagIota{
FlagGCMIV128,
}
diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go
index 375221a..e132536 100644
--- a/internal/contentenc/content.go
+++ b/internal/contentenc/content.go
@@ -14,6 +14,8 @@ import (
const (
// Default plaintext block size
DefaultBS = 4096
+ // We always use 128-bit IVs for file content encryption
+ IVBitLen = 128
)
type ContentEnc struct {
@@ -100,7 +102,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
aData := make([]byte, 8)
aData = append(aData, fileId...)
binary.BigEndian.PutUint64(aData, blockNo)
- plaintext, err := be.cryptoCore.Gcm.Open(plaintext, nonce, ciphertext, aData)
+ plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData)
if err != nil {
tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig))
@@ -133,7 +135,7 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by
}
// Get fresh nonce
- nonce := be.cryptoCore.GcmIVGen.Get()
+ nonce := be.cryptoCore.IVGenerator.Get()
// Authenticate block with block number and file ID
aData := make([]byte, 8)
@@ -141,7 +143,7 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by
aData = append(aData, fileID...)
// Encrypt plaintext and append to nonce
- ciphertext := be.cryptoCore.Gcm.Seal(nonce, nonce, plaintext, aData)
+ ciphertext := be.cryptoCore.AEADCipher.Seal(nonce, nonce, plaintext, aData)
return ciphertext
}
diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go
index 299c8c8..faa2780 100644
--- a/internal/contentenc/content_test.go
+++ b/internal/contentenc/content_test.go
@@ -23,7 +23,7 @@ func TestSplitRange(t *testing.T) {
testRange{6654, 8945})
key := make([]byte, cryptocore.KeyLen)
- cc := cryptocore.New(key, true, true)
+ cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)
f := New(cc, DefaultBS)
for _, r := range ranges {
@@ -51,7 +51,7 @@ func TestCiphertextRange(t *testing.T) {
testRange{6654, 8945})
key := make([]byte, cryptocore.KeyLen)
- cc := cryptocore.New(key, true, true)
+ cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)
f := New(cc, DefaultBS)
for _, r := range ranges {
@@ -74,7 +74,7 @@ func TestCiphertextRange(t *testing.T) {
func TestBlockNo(t *testing.T) {
key := make([]byte, cryptocore.KeyLen)
- cc := cryptocore.New(key, true, true)
+ cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)
f := New(cc, DefaultBS)
b := f.CipherOffToBlockNo(788)
diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go
index 1839aa2..a6708bd 100644
--- a/internal/cryptocore/cryptocore.go
+++ b/internal/cryptocore/cryptocore.go
@@ -8,17 +8,29 @@ import (
"fmt"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
+
+ "github.com/rfjakob/gcmsiv"
)
+type BackendTypeEnum int
+
const (
KeyLen = 32 // AES-256
AuthTagLen = 16
+
+ _ = iota // Skip zero
+ BackendOpenSSL BackendTypeEnum = iota
+ BackendGoGCM BackendTypeEnum = iota
+ BackendGCMSIV BackendTypeEnum = iota
)
type CryptoCore struct {
+ // AES-256 block cipher. This is used for EME filename encryption.
BlockCipher cipher.Block
- Gcm cipher.AEAD
- GcmIVGen *nonceGenerator
+ // GCM or GCM-SIV. This is used for content encryption.
+ AEADCipher cipher.AEAD
+ // GCM needs unique IVs (nonces)
+ IVGenerator *nonceGenerator
IVLen int
}
@@ -27,17 +39,12 @@ type CryptoCore struct {
// Even though the "GCMIV128" feature flag is now mandatory, we must still
// support 96-bit IVs here because they are used for encrypting the master
// key in gocryptfs.conf.
-func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore {
-
+func New(key []byte, backend BackendTypeEnum, IVBitLen int) *CryptoCore {
if len(key) != KeyLen {
panic(fmt.Sprintf("Unsupported key length %d", len(key)))
}
-
// We want the IV size in bytes
- IVLen := 96 / 8
- if GCMIV128 {
- IVLen = 128 / 8
- }
+ IVLen := IVBitLen / 8
// Name encryption always uses built-in Go AES through BlockCipher.
// Content encryption uses BlockCipher only if useOpenssl=false.
@@ -47,20 +54,27 @@ func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore {
}
var gcm cipher.AEAD
- if useOpenssl && GCMIV128 {
- // stupidgcm only supports 128-bit IVs
+ switch backend {
+ case BackendOpenSSL:
+ if IVLen != 16 {
+ panic("stupidgcm only supports 128-bit IVs")
+ }
gcm = stupidgcm.New(key)
- } else {
+ case BackendGoGCM:
gcm, err = goGCMWrapper(blockCipher, IVLen)
- if err != nil {
- panic(err)
- }
+ case BackendGCMSIV:
+ gcm, err = gcmsiv.NewGCMSIV(key)
+ default:
+ panic("unknown backend cipher")
+ }
+ if err != nil {
+ panic(err)
}
return &CryptoCore{
BlockCipher: blockCipher,
- Gcm: gcm,
- GcmIVGen: &nonceGenerator{nonceLen: IVLen},
+ AEADCipher: gcm,
+ IVGenerator: &nonceGenerator{nonceLen: IVLen},
IVLen: IVLen,
}
}
diff --git a/internal/cryptocore/cryptocore_go1.4_test.go b/internal/cryptocore/cryptocore_go1.4_test.go
index 3460d02..14e1e03 100644
--- a/internal/cryptocore/cryptocore_go1.4_test.go
+++ b/internal/cryptocore/cryptocore_go1.4_test.go
@@ -7,7 +7,8 @@ import (
"testing"
)
-// Native Go crypto with 128-bit IVs is only supported on Go 1.5 and up
+// Native Go crypto with 128-bit IVs is only supported on Go 1.5 and up,
+// this should panic.
func TestCryptoCoreNewGo14(t *testing.T) {
defer func() {
if r := recover(); r == nil {
@@ -15,5 +16,5 @@ func TestCryptoCoreNewGo14(t *testing.T) {
}
}()
key := make([]byte, 32)
- New(key, false, true)
+ New(key, BackendGoGCM, 128)
}
diff --git a/internal/cryptocore/cryptocore_go1.5_test.go b/internal/cryptocore/cryptocore_go1.5_test.go
index 1c93254..f9d38e9 100644
--- a/internal/cryptocore/cryptocore_go1.5_test.go
+++ b/internal/cryptocore/cryptocore_go1.5_test.go
@@ -9,7 +9,7 @@ import (
func TestCryptoCoreNewGo15(t *testing.T) {
key := make([]byte, 32)
- c := New(key, false, true)
+ c := New(key, BackendGoGCM, 128)
if c.IVLen != 16 {
t.Fail()
}
diff --git a/internal/cryptocore/cryptocore_test.go b/internal/cryptocore/cryptocore_test.go
index 1151591..9da6059 100644
--- a/internal/cryptocore/cryptocore_test.go
+++ b/internal/cryptocore/cryptocore_test.go
@@ -4,23 +4,19 @@ import (
"testing"
)
-// "New" should accept all param combinations
+// "New" should accept at least these param combinations
func TestCryptoCoreNew(t *testing.T) {
key := make([]byte, 32)
- c := New(key, true, true)
+ c := New(key, BackendOpenSSL, 128)
if c.IVLen != 16 {
t.Fail()
}
- c = New(key, true, false)
+ c = New(key, BackendGoGCM, 96)
if c.IVLen != 12 {
t.Fail()
}
- c = New(key, false, false)
- if c.IVLen != 12 {
- t.Fail()
- }
- // "New(key, false, true)" is tested for Go 1.4 and 1.5+ seperately
+ // "New(key, BackendGoGCM, 128)" is tested for Go 1.4 and 1.5+ seperately
}
// "New" should panic on any key not 32 bytes long
@@ -32,5 +28,5 @@ func TestNewPanic(t *testing.T) {
}()
key := make([]byte, 16)
- New(key, true, true)
+ New(key, BackendOpenSSL, 128)
}
diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go
index 78b9b5b..d0e1835 100644
--- a/internal/fusefrontend/args.go
+++ b/internal/fusefrontend/args.go
@@ -1,10 +1,14 @@
package fusefrontend
+import (
+ "github.com/rfjakob/gocryptfs/internal/cryptocore"
+)
+
// Container for arguments that are passed from main() to fusefrontend
type Args struct {
Masterkey []byte
Cipherdir string
- OpenSSL bool
+ CryptoBackend cryptocore.BackendTypeEnum
PlaintextNames bool
LongNames bool
// Should we chown a file after it has been created?
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go
index bc81c37..575865e 100644
--- a/internal/fusefrontend/fs.go
+++ b/internal/fusefrontend/fs.go
@@ -37,7 +37,7 @@ type FS struct {
// Encrypted FUSE overlay filesystem
func NewFS(args Args) *FS {
- cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, true)
+ cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.IVBitLen)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
nameTransform := nametransform.New(cryptoCore, args.LongNames)
diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go
index e20c851..4b04b86 100644
--- a/internal/fusefrontend_reverse/rfs.go
+++ b/internal/fusefrontend_reverse/rfs.go
@@ -36,7 +36,7 @@ type reverseFS struct {
// Encrypted FUSE overlay filesystem
func NewFS(args fusefrontend.Args) *reverseFS {
- cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, true)
+ cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.IVBitLen)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
nameTransform := nametransform.New(cryptoCore, args.LongNames)
diff --git a/main.go b/main.go
index 1f7230a..1877779 100644
--- a/main.go
+++ b/main.go
@@ -282,20 +282,30 @@ func main() {
// initFuseFrontend - initialize gocryptfs/fusefrontend
// Calls os.Exit on errors
func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFile) *fuse.Server {
-
- // Reconciliate CLI and config file arguments into a Args struct that is passed to the
- // filesystem implementation
+ // Reconciliate CLI and config file arguments into a fusefrontend.Args struct
+ // that is passed to the filesystem implementation
+ cryptoBackend := cryptocore.BackendGoGCM
+ if args.openssl {
+ cryptoBackend = cryptocore.BackendOpenSSL
+ }
+ if args.reverse {
+ // reverse implies GCMSIV
+ cryptoBackend = cryptocore.BackendGCMSIV
+ }
frontendArgs := fusefrontend.Args{
Cipherdir: args.cipherdir,
Masterkey: key,
- OpenSSL: args.openssl,
PlaintextNames: args.plaintextnames,
LongNames: args.longnames,
+ CryptoBackend: cryptoBackend,
}
// confFile is nil when "-zerokey" or "-masterkey" was used
if confFile != nil {
// Settings from the config file override command line args
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
+ if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) {
+ frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV
+ }
}
// If allow_other is set and we run as root, try to give newly created files to
// the right user.
@@ -308,7 +318,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
var finalFs pathfs.FileSystem
if args.reverse {
finalFs = fusefrontend_reverse.NewFS(frontendArgs)
- tlog.Info.Printf(tlog.ColorYellow + "REVERSE MODE IS EXPERIMENTAL" + tlog.ColorReset)
+ tlog.Info.Printf(tlog.ColorYellow + "REVERSE MODE IS EXPERIMENTAL!" + tlog.ColorReset)
} else {
finalFs = fusefrontend.NewFS(frontendArgs)
}