aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2015-09-13 21:47:18 +0200
committerJakob Unterwurzacher2015-09-13 22:09:38 +0200
commit6f9e90c414c165ff76cd7546b9898b51660a2440 (patch)
treed6dc91c505bc41e14d61dc592a5b07c9698dfe35
parent164739b65588bcad91425f38db1ae1aae5c15e56 (diff)
Encrypt key with scrypt-hashed password
-rw-r--r--cluefs_frontend/fe_fs.go2
-rw-r--r--cryptfs/config_file.go65
-rw-r--r--cryptfs/cryptfs.go13
-rw-r--r--cryptfs/kdf.go38
-rw-r--r--main.go15
-rw-r--r--pathfs_frontend/fs.go2
6 files changed, 94 insertions, 41 deletions
diff --git a/cluefs_frontend/fe_fs.go b/cluefs_frontend/fe_fs.go
index 2b12da6..9d677b2 100644
--- a/cluefs_frontend/fe_fs.go
+++ b/cluefs_frontend/fe_fs.go
@@ -27,7 +27,7 @@ type nullTracer struct {}
func (nullTracer) Trace(op cluefs.FsOperTracer) {}
-func NewFS(key [16]byte, backing string, useOpenssl bool) (*FS, error) {
+func NewFS(key []byte, backing string, useOpenssl bool) (*FS, error) {
var tracer nullTracer
clfs, err := cluefs.NewClueFS(backing, tracer)
if err != nil {
diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go
index 752ed09..5651716 100644
--- a/cryptfs/config_file.go
+++ b/cryptfs/config_file.go
@@ -1,7 +1,6 @@
package cryptfs
import (
- "errors"
"io/ioutil"
"encoding/json"
)
@@ -17,23 +16,30 @@ const (
)
type confFile struct {
- // File the config is saved in. Lowercase => not exported to JSON.
+ // File the config is saved to. Not exported to JSON.
filename string
- // Unencrypted AES key
- Key [16]byte
- // GCM ciphertext with auth tag to verify the key is correct
- TestBlock []byte
+ // Encrypted AES key, unlocked using a password hashed with scrypt
+ EncryptedKey []byte
+ // Stores parameters for scrypt hashing (key derivation)
+ ScryptObject scryptKdf
}
-// CreateConfFile - create a new config file with "key" and write to "filename"
-func CreateConfFile(filename string, key [16]byte) error {
+// CreateConfFile - create a new config with a random key encrypted with
+// "password" and write it to "filename"
+func CreateConfFile(filename string, password string) error {
var cf confFile
cf.filename = filename
- cf.Key = key
- // Generate test block
- cfs := NewCryptFS(cf.Key, false)
- cf.TestBlock = cfs.EncryptBlock([]byte(testBlockData))
+ // Generate new random master key
+ key := RandBytes(KEY_LEN)
+
+ // Generate derived key from password
+ cf.ScryptObject = NewScryptKdf()
+ scryptHash := cf.ScryptObject.DeriveKey(password)
+
+ // Lock master key using password-based key
+ cfs := NewCryptFS(scryptHash, false)
+ cf.EncryptedKey = cfs.EncryptBlock(key)
// Write file to disk
err := cf.WriteFile()
@@ -41,34 +47,39 @@ func CreateConfFile(filename string, key [16]byte) error {
return err
}
-// LoadConfFile - read config file from disk and verify the key using the
-// embedded TestBlock
-func LoadConfFile(filename string) (*confFile, error) {
+// LoadConfFile - read config file from disk and decrypt the
+// contained key using password
+func LoadConfFile(filename string, password string) ([]byte, error) {
+ var cf confFile
+ cf.filename = filename
+
// Read from disk
js, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
- var cf confFile
+
+ // Unmarshal
err = json.Unmarshal(js, &cf)
if err != nil {
+ Warn.Printf("Failed to unmarshal config file\n")
return nil, err
}
- cf.filename = filename
- // Try to decrypt the test block to see if the key is correct
- //
- // Speed does not matter here. Use built-in crypto.
- cfs := NewCryptFS(cf.Key, false)
- d, err := cfs.DecryptBlock(cf.TestBlock)
+ // Generate derived key from password
+ scryptHash := cf.ScryptObject.DeriveKey(password)
+
+ // Unlock master key using password-based key
+ // We use stock go GCM instead of OpenSSL here as speed is not important
+ // and we get better error messages
+ cfs := NewCryptFS(scryptHash, false)
+ key, err := cfs.DecryptBlock(cf.EncryptedKey)
if err != nil {
+ Warn.Printf("Failed to unlock master key: %s\n", err.Error())
return nil, err
}
- ds := string(d)
- if ds != testBlockData {
- return nil, errors.New("Invalid test block content: " + ds)
- }
- return &cf, nil
+
+ return key, nil
}
// WriteFile - write out config in JSON format to file "filename.tmp"
diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go
index 8927d74..6380a92 100644
--- a/cryptfs/cryptfs.go
+++ b/cryptfs/cryptfs.go
@@ -3,6 +3,7 @@ package cryptfs
// CryptFS is the crypto backend of GoCryptFS
import (
+ "fmt"
"crypto/cipher"
"crypto/aes"
)
@@ -21,16 +22,22 @@ type CryptFS struct {
cipherBS uint64
}
-func NewCryptFS(key [16]byte, useOpenssl bool) *CryptFS {
+func NewCryptFS(key []byte, useOpenssl bool) *CryptFS {
- b, err := aes.NewCipher(key[:])
+ if len(key) != KEY_LEN {
+ panic(fmt.Sprintf("Unsupported key length %d", len(key)))
+ }
+
+ b, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
var gcm cipher.AEAD
if useOpenssl {
- gcm = opensslGCM{key}
+ var k16 [16]byte
+ copy(k16[:], key)
+ gcm = opensslGCM{k16}
} else {
gcm, err = cipher.NewGCM(b)
if err != nil {
diff --git a/cryptfs/kdf.go b/cryptfs/kdf.go
new file mode 100644
index 0000000..275c72e
--- /dev/null
+++ b/cryptfs/kdf.go
@@ -0,0 +1,38 @@
+package cryptfs
+
+import (
+ "fmt"
+ "golang.org/x/crypto/scrypt"
+)
+
+const (
+ // 1 << 16 uses 64MB of memory,
+ // takes 4 seconds on my Atom Z3735F netbook
+ SCRYPT_DEFAULT_N = 1 << 16
+)
+
+type scryptKdf struct {
+ Salt []byte
+ N int
+ R int
+ P int
+ KeyLen int
+}
+
+func NewScryptKdf() scryptKdf {
+ var s scryptKdf
+ s.Salt = RandBytes(KEY_LEN)
+ s.N = SCRYPT_DEFAULT_N
+ s.R = 8 // Always 8
+ s.P = 1 // Always 1
+ s.KeyLen = KEY_LEN
+ return s
+}
+
+func (s *scryptKdf) DeriveKey(pw string) []byte {
+ k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen)
+ if err != nil {
+ panic(fmt.Sprintf("DeriveKey failed: %s", err.Error()))
+ }
+ return k
+}
diff --git a/main.go b/main.go
index 6315805..77b9c10 100644
--- a/main.go
+++ b/main.go
@@ -55,10 +55,7 @@ func main() {
}
dir, _ := filepath.Abs(flag.Arg(0))
filename := filepath.Join(dir, cryptfs.ConfDefaultName)
- r := cryptfs.RandBytes(cryptfs.KEY_LEN)
- var key [16]byte
- copy(key[:], r)
- err := cryptfs.CreateConfFile(filename, key)
+ err := cryptfs.CreateConfFile(filename, "test")
if err != nil {
fmt.Println(err)
os.Exit(ERREXIT_INIT)
@@ -86,20 +83,20 @@ func main() {
fmt.Printf("Please run \"%s --init %s\" first\n", PROGRAM_NAME, cipherdir)
os.Exit(ERREXIT_LOADCONF)
}
- cf, err := cryptfs.LoadConfFile(cfname)
+ key, err := cryptfs.LoadConfFile(cfname, "test")
if err != nil {
fmt.Println(err)
os.Exit(ERREXIT_LOADCONF)
}
if USE_CLUEFS {
- cluefsFrontend(cf.Key, cipherdir, mountpoint)
+ cluefsFrontend(key, cipherdir, mountpoint)
} else {
- pathfsFrontend(cf.Key, cipherdir, mountpoint, debug)
+ pathfsFrontend(key, cipherdir, mountpoint, debug)
}
}
-func cluefsFrontend(key [16]byte, cipherdir string, mountpoint string) {
+func cluefsFrontend(key []byte, cipherdir string, mountpoint string) {
cfs, err := cluefs_frontend.NewFS(key, cipherdir, USE_OPENSSL)
if err != nil {
fmt.Println(err)
@@ -138,7 +135,7 @@ func cluefsFrontend(key [16]byte, cipherdir string, mountpoint string) {
os.Exit(0)
}
-func pathfsFrontend(key [16]byte, cipherdir string, mountpoint string, debug bool){
+func pathfsFrontend(key []byte, cipherdir string, mountpoint string, debug bool){
finalFs := pathfs_frontend.NewFS(key, cipherdir, USE_OPENSSL)
diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go
index 1e4a1f3..5a52cfc 100644
--- a/pathfs_frontend/fs.go
+++ b/pathfs_frontend/fs.go
@@ -19,7 +19,7 @@ type FS struct {
}
// Encrypted FUSE overlay filesystem
-func NewFS(key [16]byte, backing string, useOpenssl bool) *FS {
+func NewFS(key []byte, backing string, useOpenssl bool) *FS {
return &FS{
CryptFS: cryptfs.NewCryptFS(key, useOpenssl),
FileSystem: pathfs.NewLoopbackFileSystem(backing),