summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2018-02-18 11:33:47 +0100
committerJakob Unterwurzacher2018-02-18 11:39:10 +0100
commit18f6c6106c66ba1fe6e7b48aaa5dd444ba0f9b09 (patch)
tree98d0fd648fee94bd6b4852144250f1a53e247da3
parent719693ec5dd1153318606f151915231d71ddfe0b (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.go24
-rw-r--r--internal/stupidgcm/stupidgcm.go20
-rw-r--r--internal/stupidgcm/without_openssl.go18
-rw-r--r--mount.go8
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()
+}
diff --git a/mount.go b/mount.go
index 7b1fd4e..9b7410d 100644
--- a/mount.go
+++ b/mount.go
@@ -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) {