Skip to content

Commit f7a3e6a

Browse files
committed
main: try to wipe cryptocore's secret keys on unmount
Raise the bar for recovering keys from memory. #211
1 parent 719693e commit f7a3e6a

File tree

4 files changed

+46
-20
lines changed

4 files changed

+46
-20
lines changed

internal/cryptocore/cryptocore.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import (
88
"crypto/sha512"
99
"fmt"
1010
"log"
11+
"runtime"
1112

1213
"github.com/rfjakob/eme"
1314

1415
"github.com/rfjakob/gocryptfs/internal/siv_aead"
1516
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
17+
"github.com/rfjakob/gocryptfs/internal/tlog"
1618
)
1719

1820
// AEADTypeEnum indicates the type of AEAD backend in use.
@@ -129,3 +131,25 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
129131
IVLen: IVLen,
130132
}
131133
}
134+
135+
// Wipe tries to wipe secret keys from memory by overwriting them with zeros
136+
// and/or setting references to nil.
137+
//
138+
// This is not bulletproof due to possible GC copies, but
139+
// still raises to bar for extracting the key.
140+
func (c *CryptoCore) Wipe() {
141+
if c.AEADBackend == BackendOpenSSL {
142+
tlog.Debug.Print("CryptoCore.Wipe: Wiping stupidgcm key")
143+
// We don't use "x, ok :=" because we *want* to crash loudly if the
144+
// type assertion fails (it should never fail).
145+
sgcm := c.AEADCipher.(*stupidgcm.StupidGCM)
146+
sgcm.Wipe()
147+
} else {
148+
tlog.Debug.Print("CryptoCore.Wipe: niling stdlib refs")
149+
}
150+
// We have no access to the keys (or key-equivalents) stored inside the
151+
// Go stdlib. Best we can is to nil the references and force a GC.
152+
c.AEADCipher = nil
153+
c.EMECipher = nil
154+
runtime.GC()
155+
}

internal/stupidgcm/stupidgcm.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,32 @@ const (
2424
)
2525

2626
// stupidGCM implements the cipher.AEAD interface
27-
type stupidGCM struct {
27+
type StupidGCM struct {
2828
key []byte
2929
forceDecode bool
3030
}
3131

3232
// Verify that we satisfy the cipher.AEAD interface
33-
var _ cipher.AEAD = &stupidGCM{}
33+
var _ cipher.AEAD = &StupidGCM{}
3434

3535
// New returns a new cipher.AEAD implementation..
3636
func New(key []byte, forceDecode bool) cipher.AEAD {
3737
if len(key) != keyLen {
3838
log.Panicf("Only %d-byte keys are supported", keyLen)
3939
}
40-
return &stupidGCM{key: key, forceDecode: forceDecode}
40+
return &StupidGCM{key: key, forceDecode: forceDecode}
4141
}
4242

43-
func (g *stupidGCM) NonceSize() int {
43+
func (g *StupidGCM) NonceSize() int {
4444
return ivLen
4545
}
4646

47-
func (g *stupidGCM) Overhead() int {
47+
func (g *StupidGCM) Overhead() int {
4848
return tagLen
4949
}
5050

5151
// Seal encrypts "in" using "iv" and "authData" and append the result to "dst"
52-
func (g *stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
52+
func (g *StupidGCM) Seal(dst, iv, in, authData []byte) []byte {
5353
if len(iv) != ivLen {
5454
log.Panicf("Only %d-byte IVs are supported", ivLen)
5555
}
@@ -136,7 +136,7 @@ func (g *stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
136136
}
137137

138138
// Open decrypts "in" using "iv" and "authData" and append the result to "dst"
139-
func (g *stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
139+
func (g *StupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
140140
if len(iv) != ivLen {
141141
log.Panicf("Only %d-byte IVs are supported", ivLen)
142142
}
@@ -231,12 +231,12 @@ func (g *stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
231231
return append(dst, buf...), nil
232232
}
233233

234-
// Wipe wipes the AES key from memory by overwriting it with zeros and
235-
// setting the reference to nil.
234+
// Wipe tries to wipe the AES key from memory by overwriting it with zeros
235+
// and setting the reference to nil.
236236
//
237237
// This is not bulletproof due to possible GC copies, but
238238
// still raises to bar for extracting the key.
239-
func (g *stupidGCM) Wipe() {
239+
func (g *StupidGCM) Wipe() {
240240
for i := range g.key {
241241
g.key[i] = 0
242242
}

internal/stupidgcm/without_openssl.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/rfjakob/gocryptfs/internal/exitcodes"
1010
)
1111

12-
type stupidGCM struct{}
12+
type StupidGCM struct{}
1313

1414
const (
1515
// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time
@@ -21,28 +21,28 @@ func errExit() {
2121
os.Exit(exitcodes.OpenSSL)
2222
}
2323

24-
func New(_ []byte, _ bool) *stupidGCM {
24+
func New(_ []byte, _ bool) *StupidGCM {
2525
errExit()
2626
// Never reached
27-
return &stupidGCM{}
27+
return &StupidGCM{}
2828
}
2929

30-
func (g *stupidGCM) NonceSize() int {
30+
func (g *StupidGCM) NonceSize() int {
3131
errExit()
3232
return -1
3333
}
3434

35-
func (g *stupidGCM) Overhead() int {
35+
func (g *StupidGCM) Overhead() int {
3636
errExit()
3737
return -1
3838
}
3939

40-
func (g *stupidGCM) Seal(_, _, _, _ []byte) []byte {
40+
func (g *StupidGCM) Seal(_, _, _, _ []byte) []byte {
4141
errExit()
4242
return nil
4343
}
4444

45-
func (g *stupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
45+
func (g *StupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
4646
errExit()
4747
return nil, nil
4848
}

mount.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func doMount(args *argContainer) int {
123123
// We cannot use JSON for pretty-printing as the fields are unexported
124124
tlog.Debug.Printf("cli args: %#v", args)
125125
// Initialize FUSE server
126-
srv := initFuseFrontend(masterkey, args, confFile)
126+
srv, wipeKeys := initFuseFrontend(masterkey, args, confFile)
127127
tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
128128
// We have been forked into the background, as evidenced by the set
129129
// "notifypid".
@@ -162,6 +162,8 @@ func doMount(args *argContainer) int {
162162
debug.FreeOSMemory()
163163
// Jump into server loop. Returns when it gets an umount request from the kernel.
164164
srv.Serve()
165+
// Try to wipe secrect keys from memory
166+
wipeKeys()
165167
return 0
166168
}
167169

@@ -194,7 +196,7 @@ type ctlsockFs interface {
194196

195197
// initFuseFrontend - initialize gocryptfs/fusefrontend
196198
// Calls os.Exit on errors
197-
func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server {
199+
func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile.ConfFile) (srv *fuse.Server, wipeKeys func()) {
198200
// Reconciliate CLI and config file arguments into a fusefrontend.Args struct
199201
// that is passed to the filesystem implementation
200202
cryptoBackend := cryptocore.BackendGoGCM
@@ -361,7 +363,7 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile
361363
// directories with the requested permissions.
362364
syscall.Umask(0000)
363365

364-
return srv
366+
return srv, func() { cCore.Wipe() }
365367
}
366368

367369
func handleSigint(srv *fuse.Server, mountpoint string) {

0 commit comments

Comments
 (0)