aboutsummaryrefslogtreecommitdiff
path: root/cryptfs
diff options
context:
space:
mode:
authorJakob Unterwurzacher2015-12-08 16:13:29 +0100
committerJakob Unterwurzacher2015-12-08 16:17:04 +0100
commitc6dacd6f913b4c6eb7a8917af49190dce32db108 (patch)
treec0fd9a08f42c37bd977b95d2bb0a7c96226045c1 /cryptfs
parentff8c81f95b311eb1cd9c822202519f1a90a8cdd4 (diff)
Add EME filename encryption & enable it by default
Diffstat (limited to 'cryptfs')
-rw-r--r--cryptfs/config_file.go20
-rw-r--r--cryptfs/config_test.go2
-rw-r--r--cryptfs/cryptfs.go2
-rw-r--r--cryptfs/filter.go15
-rw-r--r--cryptfs/log.go13
-rw-r--r--cryptfs/names_core.go (renamed from cryptfs/cryptfs_names.go)80
-rw-r--r--cryptfs/names_diriv.go31
-rw-r--r--cryptfs/names_noiv.go63
-rw-r--r--cryptfs/names_test.go11
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)
}
}