From 0c520845f3623eff28f0277a52e3ccffd928f5c2 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 11 Aug 2017 18:42:30 +0200 Subject: [PATCH] main: purge masterkey from memory as soon as possible Remove the "Masterkey" field from fusefrontend.Args because it should not be stored longer than neccessary. Instead pass the masterkey as a separate argument to the filesystem initializers. Then overwrite it with zeros immediately so we don't have to wait for garbage collection. Note that the crypto implementation still stores at least a masterkey-derived value, so this change makes it harder, but not impossible, to extract the encryption keys from memory. Suggested at https://github.com/rfjakob/gocryptfs/issues/137 --- internal/cryptocore/cryptocore.go | 10 ++++++++-- internal/fusefrontend/args.go | 1 - internal/fusefrontend/fs.go | 4 ++-- internal/fusefrontend_reverse/rfs.go | 4 ++-- mount.go | 12 ++++++++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 1ad928d..aafe12b 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -72,7 +72,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec emeCipher = eme.New(emeBlockCipher) } - // Initilize an AEAD cipher for file content encryption. + // Initialize an AEAD cipher for file content encryption. var aeadCipher cipher.AEAD if aeadType == BackendOpenSSL || aeadType == BackendGoGCM { gcmKey := key @@ -84,7 +84,13 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec if IVLen != 16 { log.Panic("stupidgcm only supports 128-bit IVs") } - aeadCipher = stupidgcm.New(gcmKey, forceDecode) + // stupidgcm does not create a private copy of the key, so things + // break when initFuseFrontend() overwrites it with zeros. Create + // a copy here. This is unneccessary when useHKDF == true, but + // does no harm. + var stupidgcmKey []byte + stupidgcmKey = append(stupidgcmKey, gcmKey...) + aeadCipher = stupidgcm.New(stupidgcmKey, forceDecode) case BackendGoGCM: goGcmBlockCipher, err := aes.NewCipher(gcmKey) if err != nil { diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index 37f4463..fc9de73 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -7,7 +7,6 @@ import ( // Args is a container for arguments that are passed from main() to fusefrontend type Args struct { - Masterkey []byte // Cipherdir is the backing storage directory (absolute path). // For reverse mode, Cipherdir actually contains *plaintext* files. Cipherdir string diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 7a23710..3c442a5 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -42,8 +42,8 @@ type FS struct { var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented. // NewFS returns a new encrypted FUSE overlay filesystem. -func NewFS(args Args) *FS { - cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, args.ForceDecode) +func NewFS(masterkey []byte, args Args) *FS { + cryptoCore := cryptocore.New(masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, args.ForceDecode) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS, args.ForceDecode) nameTransform := nametransform.New(cryptoCore.EMECipher, args.LongNames, args.Raw64) diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 76b1361..53e6d22 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -40,12 +40,12 @@ var _ pathfs.FileSystem = &ReverseFS{} // NewFS returns an encrypted FUSE overlay filesystem. // In this case (reverse mode) the backing directory is plain-text and // ReverseFS provides an encrypted view. -func NewFS(args fusefrontend.Args) *ReverseFS { +func NewFS(masterkey []byte, args fusefrontend.Args) *ReverseFS { if args.CryptoBackend != cryptocore.BackendAESSIV { log.Panic("reverse mode must use AES-SIV, everything else is insecure") } initLongnameCache() - cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, false) + cryptoCore := cryptocore.New(masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, false) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS, false) nameTransform := nametransform.New(cryptoCore.EMECipher, args.LongNames, args.Raw64) diff --git a/mount.go b/mount.go index 7405ff3..89ac6d8 100644 --- a/mount.go +++ b/mount.go @@ -170,7 +170,7 @@ func setOpenFileLimit() { // initFuseFrontend - initialize gocryptfs/fusefrontend // Calls os.Exit on errors -func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server { +func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server { // Reconciliate CLI and config file arguments into a fusefrontend.Args struct // that is passed to the filesystem implementation cryptoBackend := cryptocore.BackendGoGCM @@ -187,7 +187,6 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF } frontendArgs := fusefrontend.Args{ Cipherdir: args.cipherdir, - Masterkey: key, PlaintextNames: args.plaintextnames, LongNames: args.longnames, CryptoBackend: cryptoBackend, @@ -222,14 +221,19 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF var finalFs pathfs.FileSystem var ctlSockBackend ctlsock.Interface if args.reverse { - fs := fusefrontend_reverse.NewFS(frontendArgs) + fs := fusefrontend_reverse.NewFS(masterkey, frontendArgs) finalFs = fs ctlSockBackend = fs } else { - fs := fusefrontend.NewFS(frontendArgs) + fs := fusefrontend.NewFS(masterkey, frontendArgs) finalFs = fs ctlSockBackend = fs } + // fusefrontend / fusefrontend_reverse have initialized their crypto with + // derived keys (HKDF), we can purge the master key from memory. + for i := range masterkey { + masterkey[i] = 0 + } // We have opened the socket early so that we cannot fail here after // asking the user for the password if args._ctlsockFd != nil {