diff options
author | Jakob Unterwurzacher | 2015-12-08 16:13:29 +0100 |
---|---|---|
committer | Jakob Unterwurzacher | 2015-12-08 16:17:04 +0100 |
commit | c6dacd6f913b4c6eb7a8917af49190dce32db108 (patch) | |
tree | c0fd9a08f42c37bd977b95d2bb0a7c96226045c1 /cryptfs | |
parent | ff8c81f95b311eb1cd9c822202519f1a90a8cdd4 (diff) |
Add EME filename encryption & enable it by default
Diffstat (limited to 'cryptfs')
-rw-r--r-- | cryptfs/config_file.go | 20 | ||||
-rw-r--r-- | cryptfs/config_test.go | 2 | ||||
-rw-r--r-- | cryptfs/cryptfs.go | 2 | ||||
-rw-r--r-- | cryptfs/filter.go | 15 | ||||
-rw-r--r-- | cryptfs/log.go | 13 | ||||
-rw-r--r-- | cryptfs/names_core.go (renamed from cryptfs/cryptfs_names.go) | 80 | ||||
-rw-r--r-- | cryptfs/names_diriv.go | 31 | ||||
-rw-r--r-- | cryptfs/names_noiv.go | 63 | ||||
-rw-r--r-- | cryptfs/names_test.go | 11 |
9 files changed, 136 insertions, 101 deletions
diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go index be37c60..bf1f2a0 100644 --- a/cryptfs/config_file.go +++ b/cryptfs/config_file.go @@ -12,10 +12,6 @@ const ( // The dot "." is not used in base64url (RFC4648), hence // we can never clash with an encrypted file. ConfDefaultName = "gocryptfs.conf" - // Understood Feature Flags - // Also teach isFeatureFlagKnown() about any additions - FlagPlaintextNames = "PlaintextNames" - FlagDirIV = "DirIV" ) type ConfFile struct { @@ -37,7 +33,7 @@ type ConfFile struct { // CreateConfFile - create a new config with a random key encrypted with // "password" and write it to "filename". // Uses scrypt with cost parameter logN. -func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error { +func CreateConfFile(filename string, password string, plaintextNames bool, logN int, EMENames bool) error { var cf ConfFile cf.filename = filename @@ -50,11 +46,13 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN // Set defaults cf.Version = HEADER_CURRENT_VERSION - cf.FeatureFlags = []string{FlagDirIV} // Set values chosen by the user if plaintextNames { cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames) + } else { + cf.FeatureFlags = append(cf.FeatureFlags, FlagDirIV) + cf.FeatureFlags = append(cf.FeatureFlags, FlagEMENames) } // Write file to disk @@ -157,10 +155,18 @@ func (cf *ConfFile) WriteFile() error { return nil } +const ( + // Understood Feature Flags. + // Also teach isFeatureFlagKnown() about any additions + FlagPlaintextNames = "PlaintextNames" + FlagDirIV = "DirIV" + FlagEMENames = "EMENames" +) + // Verify that we understand a feature flag func (cf *ConfFile) isFeatureFlagKnown(flag string) bool { switch flag { - case FlagPlaintextNames, FlagDirIV: + case FlagPlaintextNames, FlagDirIV, FlagEMENames: return true default: return false diff --git a/cryptfs/config_test.go b/cryptfs/config_test.go index 1c5a375..01c0d71 100644 --- a/cryptfs/config_test.go +++ b/cryptfs/config_test.go @@ -59,7 +59,7 @@ func TestLoadV2StrangeFeature(t *testing.T) { } func TestCreateConfFile(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", "test", false, 0) + err := CreateConfFile("config_test/tmp.conf", "test", false, 10, true) if err != nil { t.Fatal(err) } diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index df04973..5832e36 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -25,7 +25,6 @@ type CryptFS struct { cipherBS uint64 // Stores an all-zero block of size cipherBS allZeroBlock []byte - plaintextNames bool // DirIV cache for filename encryption DirIVCacheEnc DirIVCache } @@ -59,7 +58,6 @@ func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS { plainBS: DEFAULT_PLAINBS, cipherBS: uint64(cipherBS), allZeroBlock: make([]byte, cipherBS), - plaintextNames: plaintextNames, } } diff --git a/cryptfs/filter.go b/cryptfs/filter.go deleted file mode 100644 index f80889d..0000000 --- a/cryptfs/filter.go +++ /dev/null @@ -1,15 +0,0 @@ -package cryptfs - -// IsFiltered - check if "path" should be forbidden -// -// Used to prevent name clashes with gocryptfs.conf -// when file names are not encrypted -func (be *CryptFS) IsFiltered(path string) bool { - // gocryptfs.conf in the root directory is forbidden - if be.plaintextNames == true && path == ConfDefaultName { - Warn.Printf("The name /%s is reserved when -plaintextnames is used\n", - ConfDefaultName) - return true - } - return false -} diff --git a/cryptfs/log.go b/cryptfs/log.go index 64dc80e..a7fe579 100644 --- a/cryptfs/log.go +++ b/cryptfs/log.go @@ -3,6 +3,7 @@ package cryptfs import ( "fmt" "strings" + "encoding/json" ) type logChannel struct { @@ -26,6 +27,18 @@ func (l *logChannel) Dump(d []byte) { fmt.Println(strings.Replace(s, "\000", "\\0", -1)) } +func (l *logChannel) JSONDump(obj interface{}) { + if !l.enabled { + return + } + b, err := json.MarshalIndent(obj, "", "\t") + if err != nil { + fmt.Println(err) + } else { + fmt.Println(string(b)) + } +} + func (l *logChannel) Enable() { l.enabled = true } diff --git a/cryptfs/cryptfs_names.go b/cryptfs/names_core.go index 8f8486b..0f2e5b3 100644 --- a/cryptfs/cryptfs_names.go +++ b/cryptfs/names_core.go @@ -1,6 +1,6 @@ package cryptfs -// Filename encryption / decryption function +// Filename encryption / decryption functions import ( "crypto/aes" @@ -8,16 +8,22 @@ import ( "encoding/base64" "errors" "fmt" - "strings" -) -const ( - OpEncrypt = iota - OpDecrypt + "github.com/rfjakob/eme" ) // DecryptName - decrypt base64-encoded encrypted filename "cipherName" -func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) { +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +// +// This function is exported because it allows for a very efficient readdir +// implementation (read IV once, decrypt all names using this function). +func (be *CryptFS) DecryptName(cipherName string, iv []byte, EMENames bool) (string, error) { + return be.decryptName(cipherName, iv, EMENames) +} + +// decryptName - decrypt base64-encoded encrypted filename "cipherName". +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +func (be *CryptFS) decryptName(cipherName string, iv []byte, EMENames bool) (string, error) { // Make sure relative symlinks still work after encryption // by passing these through unchanged @@ -34,8 +40,12 @@ func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) { return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin)) } - cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) - cbc.CryptBlocks(bin, bin) + if EMENames { + bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionDecrypt) + } else { + cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) + cbc.CryptBlocks(bin, bin) + } bin, err = be.unPad16(bin) if err != nil { @@ -46,8 +56,9 @@ func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) { return plain, err } -// EncryptName - encrypt filename -func (be *CryptFS) encryptName(plainName string, iv []byte) string { +// encryptName - encrypt "plainName", return base64-encoded "cipherName64" +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +func (be *CryptFS) encryptName(plainName string, iv []byte, EMENames bool) (cipherName64 string) { // Make sure relative symlinks still work after encryption // by passing these trough unchanged @@ -58,48 +69,15 @@ func (be *CryptFS) encryptName(plainName string, iv []byte) string { bin := []byte(plainName) bin = be.pad16(bin) - cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) - cbc.CryptBlocks(bin, bin) - - cipherName64 := base64.URLEncoding.EncodeToString(bin) - return cipherName64 -} - -// TranslatePathZeroIV - encrypt or decrypt path using CBC with a constant all-zero IV. -// Just splits the string on "/" and hands the parts to encryptName() / decryptName() -func (be *CryptFS) TranslatePathZeroIV(path string, op int) (string, error) { - var err error - - // Empty string means root directory - if path == "" { - return path, err + if EMENames { + bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt) + } else { + cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) + cbc.CryptBlocks(bin, bin) } - zeroIV := make([]byte, DIRIV_LEN) - - // Run operation on each path component - var translatedParts []string - parts := strings.Split(path, "/") - for _, part := range parts { - if part == "" { - // This happens on "/foo/bar/" on the front and on the end. - // Don't panic. - translatedParts = append(translatedParts, "") - continue - } - var newPart string - if op == OpEncrypt { - newPart = be.encryptName(part, zeroIV) - } else { - newPart, err = be.DecryptName(part, zeroIV) - if err != nil { - return "", err - } - } - translatedParts = append(translatedParts, newPart) - } - - return strings.Join(translatedParts, "/"), err + cipherName64 = base64.URLEncoding.EncodeToString(bin) + return cipherName64 } // pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding diff --git a/cryptfs/names_diriv.go b/cryptfs/names_diriv.go index 035eac1..2e2429e 100644 --- a/cryptfs/names_diriv.go +++ b/cryptfs/names_diriv.go @@ -73,11 +73,8 @@ func WriteDirIV(dir string) error { return ioutil.WriteFile(file, iv, 0444) } -// EncryptPathDirIV - encrypt path using CBC with DirIV -func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, error) { - if be.plaintextNames { - return plainPath, nil - } +// EncryptPathDirIV - encrypt path using CBC or EME with DirIV +func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) { // Empty string means root directory if plainPath == "" { return plainPath, nil @@ -88,36 +85,32 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, e if found { //fmt.Print("h") baseName := filepath.Base(plainPath) - cBaseName := be.encryptName(baseName, iv) - cPath := cParentDir + "/" + cBaseName - return cPath, nil + cBaseName := be.encryptName(baseName, iv, eme) + cipherPath = cParentDir + "/" + cBaseName + return cipherPath, nil } // Walk the directory tree var wd = rootDir var encryptedNames []string - var err error plainNames := strings.Split(plainPath, "/") for _, plainName := range plainNames { iv, err = be.ReadDirIV(wd) if err != nil { return "", err } - encryptedName := be.encryptName(plainName, iv) + encryptedName := be.encryptName(plainName, iv, eme) encryptedNames = append(encryptedNames, encryptedName) wd = filepath.Join(wd, encryptedName) } // Cache the final DirIV - cPath := strings.Join(encryptedNames, "/") - cParentDir = filepath.Dir(cPath) + cipherPath = strings.Join(encryptedNames, "/") + cParentDir = filepath.Dir(cipherPath) be.DirIVCacheEnc.store(parentDir, iv, cParentDir) - return cPath, nil + return cipherPath, nil } -// DecryptPathDirIV - encrypt path using CBC with DirIV -func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) { - if be.plaintextNames { - return encryptedPath, nil - } +// DecryptPathDirIV - encrypt path using CBC or EME with DirIV +func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) { var wd = rootDir var plainNames []string encryptedNames := strings.Split(encryptedPath, "/") @@ -127,7 +120,7 @@ func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (strin if err != nil { return "", err } - plainName, err := be.DecryptName(encryptedName, iv) + plainName, err := be.decryptName(encryptedName, iv, eme) if err != nil { return "", err } diff --git a/cryptfs/names_noiv.go b/cryptfs/names_noiv.go new file mode 100644 index 0000000..7eed4b8 --- /dev/null +++ b/cryptfs/names_noiv.go @@ -0,0 +1,63 @@ +package cryptfs + +import ( + "strings" +) + +const ( + OpEncrypt = iota + OpDecrypt +) + +// DecryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { + plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt) + return plainPath, err +} + +// EncryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *CryptFS) EncryptPathNoIV(plainPath string) (cipherPath string) { + cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt) + return cipherPath +} + +// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV. +// Just splits the string on "/" and hands the parts to encryptName() / decryptName() +func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) { + var err error + + // Empty string means root directory + if path == "" { + return path, err + } + + zeroIV := make([]byte, DIRIV_LEN) + + // Run operation on each path component + var translatedParts []string + parts := strings.Split(path, "/") + for _, part := range parts { + if part == "" { + // This happens on "/foo/bar/" on the front and on the end. + // Don't panic. + translatedParts = append(translatedParts, "") + continue + } + var newPart string + if op == OpEncrypt { + newPart = be.encryptName(part, zeroIV, false) + } else { + newPart, err = be.decryptName(part, zeroIV, false) + if err != nil { + return "", err + } + } + translatedParts = append(translatedParts, newPart) + } + + return strings.Join(translatedParts, "/"), err +} diff --git a/cryptfs/names_test.go b/cryptfs/names_test.go index 1ad3391..6dffae3 100644 --- a/cryptfs/names_test.go +++ b/cryptfs/names_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestTranslatePath(t *testing.T) { +func TestEncryptPathNoIV(t *testing.T) { var s []string s = append(s, "foo") s = append(s, "foo12312312312312312313123123123") @@ -15,15 +15,14 @@ func TestTranslatePath(t *testing.T) { fs := NewCryptFS(key, true, false) for _, n := range s { - c, err := fs.TranslatePathZeroIV(n, OpEncrypt) - d, err := fs.TranslatePathZeroIV(c, OpDecrypt) + c := fs.EncryptPathNoIV(n) + d, err := fs.DecryptPathNoIV(c) if err != nil { - t.Errorf("Got error from DecryptName: %s", err) + t.Errorf("Got error from DecryptPathNoIV: %s", err) } if d != n { - t.Errorf("Content mismatch, n=\"%s\" d=\"%s\"", n, d) + t.Errorf("Content mismatch, n != d: n=%s c=%s d=%s", n, c, d) } - //fmt.Printf("n=%s c=%s d=%s\n", n, c, d) } } |