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
This commit is contained in:
parent
719693ec5d
commit
18f6c6106c
@ -8,11 +8,13 @@ import (
|
|||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/rfjakob/eme"
|
"github.com/rfjakob/eme"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/siv_aead"
|
"github.com/rfjakob/gocryptfs/internal/siv_aead"
|
||||||
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AEADTypeEnum indicates the type of AEAD backend in use.
|
// 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,
|
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()
|
||||||
|
}
|
||||||
|
@ -24,32 +24,32 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// stupidGCM implements the cipher.AEAD interface
|
// stupidGCM implements the cipher.AEAD interface
|
||||||
type stupidGCM struct {
|
type StupidGCM struct {
|
||||||
key []byte
|
key []byte
|
||||||
forceDecode bool
|
forceDecode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that we satisfy the cipher.AEAD interface
|
// Verify that we satisfy the cipher.AEAD interface
|
||||||
var _ cipher.AEAD = &stupidGCM{}
|
var _ cipher.AEAD = &StupidGCM{}
|
||||||
|
|
||||||
// New returns a new cipher.AEAD implementation..
|
// New returns a new cipher.AEAD implementation..
|
||||||
func New(key []byte, forceDecode bool) cipher.AEAD {
|
func New(key []byte, forceDecode bool) cipher.AEAD {
|
||||||
if len(key) != keyLen {
|
if len(key) != keyLen {
|
||||||
log.Panicf("Only %d-byte keys are supported", 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
|
return ivLen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *stupidGCM) Overhead() int {
|
func (g *StupidGCM) Overhead() int {
|
||||||
return tagLen
|
return tagLen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal encrypts "in" using "iv" and "authData" and append the result to "dst"
|
// 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 {
|
if len(iv) != ivLen {
|
||||||
log.Panicf("Only %d-byte IVs are supported", 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"
|
// 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 {
|
if len(iv) != ivLen {
|
||||||
log.Panicf("Only %d-byte IVs are supported", 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
|
return append(dst, buf...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe wipes the AES key from memory by overwriting it with zeros and
|
// Wipe tries to wipe the AES key from memory by overwriting it with zeros
|
||||||
// setting the reference to nil.
|
// and setting the reference to nil.
|
||||||
//
|
//
|
||||||
// This is not bulletproof due to possible GC copies, but
|
// This is not bulletproof due to possible GC copies, but
|
||||||
// still raises to bar for extracting the key.
|
// still raises to bar for extracting the key.
|
||||||
func (g *stupidGCM) Wipe() {
|
func (g *StupidGCM) Wipe() {
|
||||||
for i := range g.key {
|
for i := range g.key {
|
||||||
g.key[i] = 0
|
g.key[i] = 0
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stupidGCM struct{}
|
type StupidGCM struct{}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time
|
// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time
|
||||||
@ -21,28 +21,32 @@ func errExit() {
|
|||||||
os.Exit(exitcodes.OpenSSL)
|
os.Exit(exitcodes.OpenSSL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(_ []byte, _ bool) *stupidGCM {
|
func New(_ []byte, _ bool) *StupidGCM {
|
||||||
errExit()
|
errExit()
|
||||||
// Never reached
|
// Never reached
|
||||||
return &stupidGCM{}
|
return &StupidGCM{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *stupidGCM) NonceSize() int {
|
func (g *StupidGCM) NonceSize() int {
|
||||||
errExit()
|
errExit()
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *stupidGCM) Overhead() int {
|
func (g *StupidGCM) Overhead() int {
|
||||||
errExit()
|
errExit()
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *stupidGCM) Seal(_, _, _, _ []byte) []byte {
|
func (g *StupidGCM) Seal(_, _, _, _ []byte) []byte {
|
||||||
errExit()
|
errExit()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *stupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
|
func (g *StupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
|
||||||
errExit()
|
errExit()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *StupidGCM) Wipe() {
|
||||||
|
errExit()
|
||||||
|
}
|
||||||
|
8
mount.go
8
mount.go
@ -123,7 +123,7 @@ func doMount(args *argContainer) int {
|
|||||||
// We cannot use JSON for pretty-printing as the fields are unexported
|
// We cannot use JSON for pretty-printing as the fields are unexported
|
||||||
tlog.Debug.Printf("cli args: %#v", args)
|
tlog.Debug.Printf("cli args: %#v", args)
|
||||||
// Initialize FUSE server
|
// 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)
|
tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
|
||||||
// We have been forked into the background, as evidenced by the set
|
// We have been forked into the background, as evidenced by the set
|
||||||
// "notifypid".
|
// "notifypid".
|
||||||
@ -162,6 +162,8 @@ func doMount(args *argContainer) int {
|
|||||||
debug.FreeOSMemory()
|
debug.FreeOSMemory()
|
||||||
// Jump into server loop. Returns when it gets an umount request from the kernel.
|
// Jump into server loop. Returns when it gets an umount request from the kernel.
|
||||||
srv.Serve()
|
srv.Serve()
|
||||||
|
// Try to wipe secrect keys from memory
|
||||||
|
wipeKeys()
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +196,7 @@ type ctlsockFs interface {
|
|||||||
|
|
||||||
// initFuseFrontend - initialize gocryptfs/fusefrontend
|
// initFuseFrontend - initialize gocryptfs/fusefrontend
|
||||||
// Calls os.Exit on errors
|
// 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
|
// Reconciliate CLI and config file arguments into a fusefrontend.Args struct
|
||||||
// that is passed to the filesystem implementation
|
// that is passed to the filesystem implementation
|
||||||
cryptoBackend := cryptocore.BackendGoGCM
|
cryptoBackend := cryptocore.BackendGoGCM
|
||||||
@ -361,7 +363,7 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile
|
|||||||
// directories with the requested permissions.
|
// directories with the requested permissions.
|
||||||
syscall.Umask(0000)
|
syscall.Umask(0000)
|
||||||
|
|
||||||
return srv
|
return srv, func() { cCore.Wipe() }
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSigint(srv *fuse.Server, mountpoint string) {
|
func handleSigint(srv *fuse.Server, mountpoint string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user