diff options
| author | Jakob Unterwurzacher | 2018-02-18 11:33:47 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2018-02-18 11:39:10 +0100 | 
| commit | 18f6c6106c66ba1fe6e7b48aaa5dd444ba0f9b09 (patch) | |
| tree | 98d0fd648fee94bd6b4852144250f1a53e247da3 | |
| parent | 719693ec5dd1153318606f151915231d71ddfe0b (diff) | |
main: try to wipe cryptocore's secret keys on unmount
Raise the bar for recovering keys from memory.
https://github.com/rfjakob/gocryptfs/issues/211
| -rw-r--r-- | internal/cryptocore/cryptocore.go | 24 | ||||
| -rw-r--r-- | internal/stupidgcm/stupidgcm.go | 20 | ||||
| -rw-r--r-- | internal/stupidgcm/without_openssl.go | 18 | ||||
| -rw-r--r-- | mount.go | 8 | 
4 files changed, 50 insertions, 20 deletions
| diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 9e25bfa..a355342 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -8,11 +8,13 @@ import (  	"crypto/sha512"  	"fmt"  	"log" +	"runtime"  	"github.com/rfjakob/eme"  	"github.com/rfjakob/gocryptfs/internal/siv_aead"  	"github.com/rfjakob/gocryptfs/internal/stupidgcm" +	"github.com/rfjakob/gocryptfs/internal/tlog"  )  // AEADTypeEnum indicates the type of AEAD backend in use. @@ -129,3 +131,25 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec  		IVLen:       IVLen,  	}  } + +// Wipe tries to wipe secret keys from memory by overwriting them with zeros +// and/or setting references to nil. +// +// This is not bulletproof due to possible GC copies, but +// still raises to bar for extracting the key. +func (c *CryptoCore) Wipe() { +	if c.AEADBackend == BackendOpenSSL { +		tlog.Debug.Print("CryptoCore.Wipe: Wiping stupidgcm key") +		// We don't use "x, ok :=" because we *want* to crash loudly if the +		// type assertion fails (it should never fail). +		sgcm := c.AEADCipher.(*stupidgcm.StupidGCM) +		sgcm.Wipe() +	} else { +		tlog.Debug.Print("CryptoCore.Wipe: niling stdlib refs") +	} +	// We have no access to the keys (or key-equivalents) stored inside the +	// Go stdlib. Best we can is to nil the references and force a GC. +	c.AEADCipher = nil +	c.EMECipher = nil +	runtime.GC() +} diff --git a/internal/stupidgcm/stupidgcm.go b/internal/stupidgcm/stupidgcm.go index 9fa730f..77d6770 100644 --- a/internal/stupidgcm/stupidgcm.go +++ b/internal/stupidgcm/stupidgcm.go @@ -24,32 +24,32 @@ const (  )  // stupidGCM implements the cipher.AEAD interface -type stupidGCM struct { +type StupidGCM struct {  	key         []byte  	forceDecode bool  }  // Verify that we satisfy the cipher.AEAD interface -var _ cipher.AEAD = &stupidGCM{} +var _ cipher.AEAD = &StupidGCM{}  // New returns a new cipher.AEAD implementation..  func New(key []byte, forceDecode bool) cipher.AEAD {  	if len(key) != keyLen {  		log.Panicf("Only %d-byte keys are supported", keyLen)  	} -	return &stupidGCM{key: key, forceDecode: forceDecode} +	return &StupidGCM{key: key, forceDecode: forceDecode}  } -func (g *stupidGCM) NonceSize() int { +func (g *StupidGCM) NonceSize() int {  	return ivLen  } -func (g *stupidGCM) Overhead() int { +func (g *StupidGCM) Overhead() int {  	return tagLen  }  // Seal encrypts "in" using "iv" and "authData" and append the result to "dst" -func (g *stupidGCM) Seal(dst, iv, in, authData []byte) []byte { +func (g *StupidGCM) Seal(dst, iv, in, authData []byte) []byte {  	if len(iv) != ivLen {  		log.Panicf("Only %d-byte IVs are supported", ivLen)  	} @@ -136,7 +136,7 @@ func (g *stupidGCM) Seal(dst, iv, in, authData []byte) []byte {  }  // Open decrypts "in" using "iv" and "authData" and append the result to "dst" -func (g *stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { +func (g *StupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {  	if len(iv) != ivLen {  		log.Panicf("Only %d-byte IVs are supported", ivLen)  	} @@ -231,12 +231,12 @@ func (g *stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {  	return append(dst, buf...), nil  } -// Wipe wipes the AES key from memory by overwriting it with zeros and -// setting the reference to nil. +// Wipe tries to wipe the AES key from memory by overwriting it with zeros +// and setting the reference to nil.  //  // This is not bulletproof due to possible GC copies, but  // still raises to bar for extracting the key. -func (g *stupidGCM) Wipe() { +func (g *StupidGCM) Wipe() {  	for i := range g.key {  		g.key[i] = 0  	} diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go index 0c1c149..deac342 100644 --- a/internal/stupidgcm/without_openssl.go +++ b/internal/stupidgcm/without_openssl.go @@ -9,7 +9,7 @@ import (  	"github.com/rfjakob/gocryptfs/internal/exitcodes"  ) -type stupidGCM struct{} +type StupidGCM struct{}  const (  	// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time @@ -21,28 +21,32 @@ func errExit() {  	os.Exit(exitcodes.OpenSSL)  } -func New(_ []byte, _ bool) *stupidGCM { +func New(_ []byte, _ bool) *StupidGCM {  	errExit()  	// Never reached -	return &stupidGCM{} +	return &StupidGCM{}  } -func (g *stupidGCM) NonceSize() int { +func (g *StupidGCM) NonceSize() int {  	errExit()  	return -1  } -func (g *stupidGCM) Overhead() int { +func (g *StupidGCM) Overhead() int {  	errExit()  	return -1  } -func (g *stupidGCM) Seal(_, _, _, _ []byte) []byte { +func (g *StupidGCM) Seal(_, _, _, _ []byte) []byte {  	errExit()  	return nil  } -func (g *stupidGCM) Open(_, _, _, _ []byte) ([]byte, error) { +func (g *StupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {  	errExit()  	return nil, nil  } + +func (g *StupidGCM) Wipe() { +	errExit() +} @@ -123,7 +123,7 @@ func doMount(args *argContainer) int {  	// We cannot use JSON for pretty-printing as the fields are unexported  	tlog.Debug.Printf("cli args: %#v", args)  	// Initialize FUSE server -	srv := initFuseFrontend(masterkey, args, confFile) +	srv, wipeKeys := initFuseFrontend(masterkey, args, confFile)  	tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)  	// We have been forked into the background, as evidenced by the set  	// "notifypid". @@ -162,6 +162,8 @@ func doMount(args *argContainer) int {  	debug.FreeOSMemory()  	// Jump into server loop. Returns when it gets an umount request from the kernel.  	srv.Serve() +	// Try to wipe secrect keys from memory +	wipeKeys()  	return 0  } @@ -194,7 +196,7 @@ type ctlsockFs interface {  // initFuseFrontend - initialize gocryptfs/fusefrontend  // Calls os.Exit on errors -func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server { +func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile.ConfFile) (srv *fuse.Server, wipeKeys func()) {  	// Reconciliate CLI and config file arguments into a fusefrontend.Args struct  	// that is passed to the filesystem implementation  	cryptoBackend := cryptocore.BackendGoGCM @@ -361,7 +363,7 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile  	// directories with the requested permissions.  	syscall.Umask(0000) -	return srv +	return srv, func() { cCore.Wipe() }  }  func handleSigint(srv *fuse.Server, mountpoint string) { | 
