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
This commit is contained in:
Jakob Unterwurzacher 2017-08-11 18:42:30 +02:00
parent f59479736b
commit 0c520845f3
5 changed files with 20 additions and 11 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 {