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 +} |