diff options
Diffstat (limited to 'cryptfs')
| -rw-r--r-- | cryptfs/config_file.go | 65 | ||||
| -rw-r--r-- | cryptfs/cryptfs.go | 13 | ||||
| -rw-r--r-- | cryptfs/kdf.go | 38 | 
3 files changed, 86 insertions, 30 deletions
| 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 +} | 
