Add EME filename encryption & enable it by default
This commit is contained in:
parent
ff8c81f95b
commit
c6dacd6f91
@ -42,6 +42,9 @@ Options:
|
|||||||
**-diriv**
|
**-diriv**
|
||||||
: Use per-directory file name IV (default true)
|
: Use per-directory file name IV (default true)
|
||||||
|
|
||||||
|
**-emenames**
|
||||||
|
: Use EME filename encryption (default true). This option implies diriv.
|
||||||
|
|
||||||
**-extpass string**
|
**-extpass string**
|
||||||
: Use an external program (like ssh-askpass) for the password prompt.
|
: Use an external program (like ssh-askpass) for the password prompt.
|
||||||
The program should return the password on stdout, a trailing newline is
|
The program should return the password on stdout, a trailing newline is
|
||||||
|
@ -12,10 +12,6 @@ const (
|
|||||||
// The dot "." is not used in base64url (RFC4648), hence
|
// The dot "." is not used in base64url (RFC4648), hence
|
||||||
// we can never clash with an encrypted file.
|
// we can never clash with an encrypted file.
|
||||||
ConfDefaultName = "gocryptfs.conf"
|
ConfDefaultName = "gocryptfs.conf"
|
||||||
// Understood Feature Flags
|
|
||||||
// Also teach isFeatureFlagKnown() about any additions
|
|
||||||
FlagPlaintextNames = "PlaintextNames"
|
|
||||||
FlagDirIV = "DirIV"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfFile struct {
|
type ConfFile struct {
|
||||||
@ -37,7 +33,7 @@ type ConfFile struct {
|
|||||||
// CreateConfFile - create a new config with a random key encrypted with
|
// CreateConfFile - create a new config with a random key encrypted with
|
||||||
// "password" and write it to "filename".
|
// "password" and write it to "filename".
|
||||||
// Uses scrypt with cost parameter logN.
|
// 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
|
var cf ConfFile
|
||||||
cf.filename = filename
|
cf.filename = filename
|
||||||
|
|
||||||
@ -50,11 +46,13 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
|
|||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
cf.Version = HEADER_CURRENT_VERSION
|
cf.Version = HEADER_CURRENT_VERSION
|
||||||
cf.FeatureFlags = []string{FlagDirIV}
|
|
||||||
|
|
||||||
// Set values chosen by the user
|
// Set values chosen by the user
|
||||||
if plaintextNames {
|
if plaintextNames {
|
||||||
cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames)
|
cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames)
|
||||||
|
} else {
|
||||||
|
cf.FeatureFlags = append(cf.FeatureFlags, FlagDirIV)
|
||||||
|
cf.FeatureFlags = append(cf.FeatureFlags, FlagEMENames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write file to disk
|
// Write file to disk
|
||||||
@ -157,10 +155,18 @@ func (cf *ConfFile) WriteFile() error {
|
|||||||
return nil
|
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
|
// Verify that we understand a feature flag
|
||||||
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
||||||
switch flag {
|
switch flag {
|
||||||
case FlagPlaintextNames, FlagDirIV:
|
case FlagPlaintextNames, FlagDirIV, FlagEMENames:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -59,7 +59,7 @@ func TestLoadV2StrangeFeature(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateConfFile(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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ type CryptFS struct {
|
|||||||
cipherBS uint64
|
cipherBS uint64
|
||||||
// Stores an all-zero block of size cipherBS
|
// Stores an all-zero block of size cipherBS
|
||||||
allZeroBlock []byte
|
allZeroBlock []byte
|
||||||
plaintextNames bool
|
|
||||||
// DirIV cache for filename encryption
|
// DirIV cache for filename encryption
|
||||||
DirIVCacheEnc DirIVCache
|
DirIVCacheEnc DirIVCache
|
||||||
}
|
}
|
||||||
@ -59,7 +58,6 @@ func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS {
|
|||||||
plainBS: DEFAULT_PLAINBS,
|
plainBS: DEFAULT_PLAINBS,
|
||||||
cipherBS: uint64(cipherBS),
|
cipherBS: uint64(cipherBS),
|
||||||
allZeroBlock: make([]byte, cipherBS),
|
allZeroBlock: make([]byte, cipherBS),
|
||||||
plaintextNames: plaintextNames,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package cryptfs
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type logChannel struct {
|
type logChannel struct {
|
||||||
@ -26,6 +27,18 @@ func (l *logChannel) Dump(d []byte) {
|
|||||||
fmt.Println(strings.Replace(s, "\000", "\\0", -1))
|
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() {
|
func (l *logChannel) Enable() {
|
||||||
l.enabled = true
|
l.enabled = true
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package cryptfs
|
package cryptfs
|
||||||
|
|
||||||
// Filename encryption / decryption function
|
// Filename encryption / decryption functions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
@ -8,16 +8,22 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
"github.com/rfjakob/eme"
|
||||||
OpEncrypt = iota
|
|
||||||
OpDecrypt
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecryptName - decrypt base64-encoded encrypted filename "cipherName"
|
// 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
|
// Make sure relative symlinks still work after encryption
|
||||||
// by passing these through unchanged
|
// 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))
|
return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin))
|
||||||
}
|
}
|
||||||
|
|
||||||
cbc := cipher.NewCBCDecrypter(be.blockCipher, iv)
|
if EMENames {
|
||||||
cbc.CryptBlocks(bin, bin)
|
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)
|
bin, err = be.unPad16(bin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -46,8 +56,9 @@ func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) {
|
|||||||
return plain, err
|
return plain, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptName - encrypt filename
|
// encryptName - encrypt "plainName", return base64-encoded "cipherName64"
|
||||||
func (be *CryptFS) encryptName(plainName string, iv []byte) string {
|
// 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
|
// Make sure relative symlinks still work after encryption
|
||||||
// by passing these trough unchanged
|
// by passing these trough unchanged
|
||||||
@ -58,50 +69,17 @@ func (be *CryptFS) encryptName(plainName string, iv []byte) string {
|
|||||||
bin := []byte(plainName)
|
bin := []byte(plainName)
|
||||||
bin = be.pad16(bin)
|
bin = be.pad16(bin)
|
||||||
|
|
||||||
cbc := cipher.NewCBCEncrypter(be.blockCipher, iv)
|
if EMENames {
|
||||||
cbc.CryptBlocks(bin, bin)
|
bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt)
|
||||||
|
} else {
|
||||||
|
cbc := cipher.NewCBCEncrypter(be.blockCipher, iv)
|
||||||
|
cbc.CryptBlocks(bin, bin)
|
||||||
|
}
|
||||||
|
|
||||||
cipherName64 := base64.URLEncoding.EncodeToString(bin)
|
cipherName64 = base64.URLEncoding.EncodeToString(bin)
|
||||||
return cipherName64
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding
|
// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding
|
||||||
// https://tools.ietf.org/html/rfc5652#section-6.3
|
// https://tools.ietf.org/html/rfc5652#section-6.3
|
||||||
func (be *CryptFS) pad16(orig []byte) (padded []byte) {
|
func (be *CryptFS) pad16(orig []byte) (padded []byte) {
|
@ -73,11 +73,8 @@ func WriteDirIV(dir string) error {
|
|||||||
return ioutil.WriteFile(file, iv, 0444)
|
return ioutil.WriteFile(file, iv, 0444)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptPathDirIV - encrypt path using CBC with DirIV
|
// EncryptPathDirIV - encrypt path using CBC or EME with DirIV
|
||||||
func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, error) {
|
func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) {
|
||||||
if be.plaintextNames {
|
|
||||||
return plainPath, nil
|
|
||||||
}
|
|
||||||
// Empty string means root directory
|
// Empty string means root directory
|
||||||
if plainPath == "" {
|
if plainPath == "" {
|
||||||
return plainPath, nil
|
return plainPath, nil
|
||||||
@ -88,36 +85,32 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, e
|
|||||||
if found {
|
if found {
|
||||||
//fmt.Print("h")
|
//fmt.Print("h")
|
||||||
baseName := filepath.Base(plainPath)
|
baseName := filepath.Base(plainPath)
|
||||||
cBaseName := be.encryptName(baseName, iv)
|
cBaseName := be.encryptName(baseName, iv, eme)
|
||||||
cPath := cParentDir + "/" + cBaseName
|
cipherPath = cParentDir + "/" + cBaseName
|
||||||
return cPath, nil
|
return cipherPath, nil
|
||||||
}
|
}
|
||||||
// Walk the directory tree
|
// Walk the directory tree
|
||||||
var wd = rootDir
|
var wd = rootDir
|
||||||
var encryptedNames []string
|
var encryptedNames []string
|
||||||
var err error
|
|
||||||
plainNames := strings.Split(plainPath, "/")
|
plainNames := strings.Split(plainPath, "/")
|
||||||
for _, plainName := range plainNames {
|
for _, plainName := range plainNames {
|
||||||
iv, err = be.ReadDirIV(wd)
|
iv, err = be.ReadDirIV(wd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
encryptedName := be.encryptName(plainName, iv)
|
encryptedName := be.encryptName(plainName, iv, eme)
|
||||||
encryptedNames = append(encryptedNames, encryptedName)
|
encryptedNames = append(encryptedNames, encryptedName)
|
||||||
wd = filepath.Join(wd, encryptedName)
|
wd = filepath.Join(wd, encryptedName)
|
||||||
}
|
}
|
||||||
// Cache the final DirIV
|
// Cache the final DirIV
|
||||||
cPath := strings.Join(encryptedNames, "/")
|
cipherPath = strings.Join(encryptedNames, "/")
|
||||||
cParentDir = filepath.Dir(cPath)
|
cParentDir = filepath.Dir(cipherPath)
|
||||||
be.DirIVCacheEnc.store(parentDir, iv, cParentDir)
|
be.DirIVCacheEnc.store(parentDir, iv, cParentDir)
|
||||||
return cPath, nil
|
return cipherPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptPathDirIV - encrypt path using CBC with DirIV
|
// DecryptPathDirIV - encrypt path using CBC or EME with DirIV
|
||||||
func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) {
|
func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) {
|
||||||
if be.plaintextNames {
|
|
||||||
return encryptedPath, nil
|
|
||||||
}
|
|
||||||
var wd = rootDir
|
var wd = rootDir
|
||||||
var plainNames []string
|
var plainNames []string
|
||||||
encryptedNames := strings.Split(encryptedPath, "/")
|
encryptedNames := strings.Split(encryptedPath, "/")
|
||||||
@ -127,7 +120,7 @@ func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
plainName, err := be.DecryptName(encryptedName, iv)
|
plainName, err := be.decryptName(encryptedName, iv, eme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
63
cryptfs/names_noiv.go
Normal file
63
cryptfs/names_noiv.go
Normal file
@ -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
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTranslatePath(t *testing.T) {
|
func TestEncryptPathNoIV(t *testing.T) {
|
||||||
var s []string
|
var s []string
|
||||||
s = append(s, "foo")
|
s = append(s, "foo")
|
||||||
s = append(s, "foo12312312312312312313123123123")
|
s = append(s, "foo12312312312312312313123123123")
|
||||||
@ -15,15 +15,14 @@ func TestTranslatePath(t *testing.T) {
|
|||||||
fs := NewCryptFS(key, true, false)
|
fs := NewCryptFS(key, true, false)
|
||||||
|
|
||||||
for _, n := range s {
|
for _, n := range s {
|
||||||
c, err := fs.TranslatePathZeroIV(n, OpEncrypt)
|
c := fs.EncryptPathNoIV(n)
|
||||||
d, err := fs.TranslatePathZeroIV(c, OpDecrypt)
|
d, err := fs.DecryptPathNoIV(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Got error from DecryptName: %s", err)
|
t.Errorf("Got error from DecryptPathNoIV: %s", err)
|
||||||
}
|
}
|
||||||
if d != n {
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func TestExampleFsV04(t *testing.T) {
|
|||||||
checkExampleContent(t, pDir)
|
checkExampleContent(t, pDir)
|
||||||
unmount(pDir)
|
unmount(pDir)
|
||||||
mount(cDir, pDir, "-masterkey", "74676e34-0b47c145-00dac61a-17a92316-"+
|
mount(cDir, pDir, "-masterkey", "74676e34-0b47c145-00dac61a-17a92316-"+
|
||||||
"bb57044c-e205b71f-65f4fdca-7cabd4b3", "-diriv=false")
|
"bb57044c-e205b71f-65f4fdca-7cabd4b3", "-diriv=false", "-emenames=false")
|
||||||
checkExampleContent(t, pDir)
|
checkExampleContent(t, pDir)
|
||||||
unmount(pDir)
|
unmount(pDir)
|
||||||
err = os.Remove(pDir)
|
err = os.Remove(pDir)
|
||||||
@ -79,7 +79,7 @@ func TestExampleFsV05(t *testing.T) {
|
|||||||
checkExampleContent(t, pDir)
|
checkExampleContent(t, pDir)
|
||||||
unmount(pDir)
|
unmount(pDir)
|
||||||
mount(cDir, pDir, "-masterkey", "199eae55-36bff4af-83b9a3a2-4fa16f65-"+
|
mount(cDir, pDir, "-masterkey", "199eae55-36bff4af-83b9a3a2-4fa16f65-"+
|
||||||
"1549ccdb-2d08d1f0-b1b26965-1b61f896")
|
"1549ccdb-2d08d1f0-b1b26965-1b61f896", "-emenames=false")
|
||||||
checkExampleContent(t, pDir)
|
checkExampleContent(t, pDir)
|
||||||
unmount(pDir)
|
unmount(pDir)
|
||||||
err = os.Remove(pDir)
|
err = os.Remove(pDir)
|
||||||
|
20
main.go
20
main.go
@ -35,7 +35,7 @@ const (
|
|||||||
|
|
||||||
type argContainer struct {
|
type argContainer struct {
|
||||||
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
|
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
|
||||||
plaintextnames, quiet, diriv bool
|
plaintextnames, quiet, diriv, emenames bool
|
||||||
masterkey, mountpoint, cipherdir, cpuprofile, config, extpass string
|
masterkey, mountpoint, cipherdir, cpuprofile, config, extpass string
|
||||||
notifypid, scryptn int
|
notifypid, scryptn int
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func initDir(args *argContainer) {
|
|||||||
// Create gocryptfs.conf
|
// Create gocryptfs.conf
|
||||||
cryptfs.Info.Printf("Choose a password for protecting your files.\n")
|
cryptfs.Info.Printf("Choose a password for protecting your files.\n")
|
||||||
password := readPasswordTwice(args.extpass)
|
password := readPasswordTwice(args.extpass)
|
||||||
err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn)
|
err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, args.emenames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ERREXIT_INIT)
|
||||||
@ -146,6 +146,7 @@ func main() {
|
|||||||
"file names")
|
"file names")
|
||||||
flagSet.BoolVar(&args.quiet, "q", false, "Quiet - silence informational messages")
|
flagSet.BoolVar(&args.quiet, "q", false, "Quiet - silence informational messages")
|
||||||
flagSet.BoolVar(&args.diriv, "diriv", true, "Use per-directory file name IV")
|
flagSet.BoolVar(&args.diriv, "diriv", true, "Use per-directory file name IV")
|
||||||
|
flagSet.BoolVar(&args.emenames, "emenames", true, "Use EME filename encryption. This option implies diriv.")
|
||||||
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
||||||
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
||||||
flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf")
|
flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf")
|
||||||
@ -262,7 +263,7 @@ func main() {
|
|||||||
printMasterKey(masterkey)
|
printMasterKey(masterkey)
|
||||||
}
|
}
|
||||||
// Initialize FUSE server
|
// Initialize FUSE server
|
||||||
cryptfs.Debug.Printf("args: %v\n", args)
|
cryptfs.Debug.Printf("cli args: %v\n", args)
|
||||||
srv := pathfsFrontend(masterkey, args, confFile)
|
srv := pathfsFrontend(masterkey, args, confFile)
|
||||||
cryptfs.Info.Println("Filesystem ready.")
|
cryptfs.Info.Println("Filesystem ready.")
|
||||||
// We are ready - send USR1 signal to our parent
|
// We are ready - send USR1 signal to our parent
|
||||||
@ -289,13 +290,26 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *
|
|||||||
OpenSSL: args.openssl,
|
OpenSSL: args.openssl,
|
||||||
PlaintextNames: args.plaintextnames,
|
PlaintextNames: args.plaintextnames,
|
||||||
DirIV: args.diriv,
|
DirIV: args.diriv,
|
||||||
|
EMENames: args.emenames,
|
||||||
}
|
}
|
||||||
// confFile is nil when "-zerokey" or "-masterkey" was used
|
// confFile is nil when "-zerokey" or "-masterkey" was used
|
||||||
if confFile != nil {
|
if confFile != nil {
|
||||||
// Settings from the config file override command line args
|
// Settings from the config file override command line args
|
||||||
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames)
|
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames)
|
||||||
frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV)
|
frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV)
|
||||||
|
frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames)
|
||||||
}
|
}
|
||||||
|
// EMENames implies DirIV, both on the command line and in the config file.
|
||||||
|
if frontendArgs.EMENames {
|
||||||
|
frontendArgs.DirIV = true
|
||||||
|
}
|
||||||
|
// PlainTexnames disables both EMENames and DirIV
|
||||||
|
if frontendArgs.PlaintextNames {
|
||||||
|
frontendArgs.DirIV = false
|
||||||
|
frontendArgs.EMENames = false
|
||||||
|
}
|
||||||
|
cryptfs.Debug.Printf("frontendArgs: ")
|
||||||
|
cryptfs.Debug.JSONDump(frontendArgs)
|
||||||
|
|
||||||
finalFs := pathfs_frontend.NewFS(frontendArgs)
|
finalFs := pathfs_frontend.NewFS(frontendArgs)
|
||||||
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
|
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
|
||||||
|
@ -7,4 +7,5 @@ type Args struct {
|
|||||||
OpenSSL bool
|
OpenSSL bool
|
||||||
PlaintextNames bool
|
PlaintextNames bool
|
||||||
DirIV bool
|
DirIV bool
|
||||||
|
EMENames bool
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func (fs *FS) getBackingPath(relPath string) (string, error) {
|
|||||||
|
|
||||||
func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||||
cryptfs.Debug.Printf("FS.GetAttr('%s')\n", name)
|
cryptfs.Debug.Printf("FS.GetAttr('%s')\n", name)
|
||||||
if fs.CryptFS.IsFiltered(name) {
|
if fs.isFiltered(name) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
cName, err := fs.encryptPath(name)
|
cName, err := fs.encryptPath(name)
|
||||||
@ -103,7 +103,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var name string
|
var name string
|
||||||
name, err = fs.CryptFS.DecryptName(cName, cachedIV)
|
name, err = fs.CryptFS.DecryptName(cName, cachedIV, fs.args.EMENames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s\n", cName, dirName, err)
|
cryptfs.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s\n", cName, dirName, err)
|
||||||
continue
|
continue
|
||||||
@ -128,7 +128,7 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
||||||
@ -147,7 +147,7 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
|
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
||||||
@ -163,7 +163,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.encryptPath(path)
|
cPath, err := fs.encryptPath(path)
|
||||||
@ -174,7 +174,7 @@ func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.encryptPath(path)
|
cPath, err := fs.encryptPath(path)
|
||||||
@ -185,7 +185,7 @@ func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.encryptPath(path)
|
cPath, err := fs.encryptPath(path)
|
||||||
@ -201,7 +201,7 @@ func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Utimens(path string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Utimens(path string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.encryptPath(path)
|
cPath, err := fs.encryptPath(path)
|
||||||
@ -244,7 +244,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(relPath) {
|
if fs.isFiltered(relPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
encPath, err := fs.getBackingPath(relPath)
|
encPath, err := fs.getBackingPath(relPath)
|
||||||
@ -275,7 +275,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.getBackingPath(path)
|
cPath, err := fs.getBackingPath(path)
|
||||||
@ -349,7 +349,7 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|||||||
|
|
||||||
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||||
cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")\n", target, linkName)
|
cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")\n", target, linkName)
|
||||||
if fs.CryptFS.IsFiltered(linkName) {
|
if fs.isFiltered(linkName) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.getBackingPath(linkName)
|
cPath, err := fs.getBackingPath(linkName)
|
||||||
@ -376,7 +376,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(newPath) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cOldPath, err := fs.getBackingPath(oldPath)
|
cOldPath, err := fs.getBackingPath(oldPath)
|
||||||
@ -396,7 +396,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(newPath) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cOldPath, err := fs.getBackingPath(oldPath)
|
cOldPath, err := fs.getBackingPath(oldPath)
|
||||||
@ -411,7 +411,7 @@ func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.CryptFS.IsFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
cPath, err := fs.getBackingPath(path)
|
cPath, err := fs.getBackingPath(path)
|
||||||
|
@ -6,20 +6,47 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
"github.com/rfjakob/gocryptfs/cryptfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (fs *FS) encryptPath(plainPath string) (string, error) {
|
// isFiltered - check if plaintext "path" should be forbidden
|
||||||
if !fs.args.DirIV {
|
//
|
||||||
return fs.CryptFS.TranslatePathZeroIV(plainPath, cryptfs.OpEncrypt)
|
// Prevents name clashes with internal files when file names are not encrypted
|
||||||
|
func (fs *FS) isFiltered(path string) bool {
|
||||||
|
if !fs.args.PlaintextNames {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
fs.dirIVLock.RLock()
|
// gocryptfs.conf in the root directory is forbidden
|
||||||
defer fs.dirIVLock.RUnlock()
|
if path == cryptfs.ConfDefaultName {
|
||||||
return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir)
|
cryptfs.Warn.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
||||||
|
cryptfs.ConfDefaultName)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
|
||||||
|
// are exclusive
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) decryptPath(cipherPath string) (string, error) {
|
|
||||||
|
// encryptPath - encrypt relative plaintext path
|
||||||
|
func (fs *FS) encryptPath(plainPath string) (string, error) {
|
||||||
|
if fs.args.PlaintextNames {
|
||||||
|
return plainPath, nil
|
||||||
|
}
|
||||||
if !fs.args.DirIV {
|
if !fs.args.DirIV {
|
||||||
return fs.CryptFS.TranslatePathZeroIV(cipherPath, cryptfs.OpDecrypt)
|
return fs.CryptFS.EncryptPathNoIV(plainPath), nil
|
||||||
}
|
}
|
||||||
fs.dirIVLock.RLock()
|
fs.dirIVLock.RLock()
|
||||||
defer fs.dirIVLock.RUnlock()
|
defer fs.dirIVLock.RUnlock()
|
||||||
return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir)
|
return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir, fs.args.EMENames)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decryptPath - decrypt relative ciphertext path
|
||||||
|
func (fs *FS) decryptPath(cipherPath string) (string, error) {
|
||||||
|
if fs.args.PlaintextNames {
|
||||||
|
return cipherPath, nil
|
||||||
|
}
|
||||||
|
if !fs.args.DirIV {
|
||||||
|
return fs.CryptFS.DecryptPathNoIV(cipherPath)
|
||||||
|
}
|
||||||
|
fs.dirIVLock.RLock()
|
||||||
|
defer fs.dirIVLock.RUnlock()
|
||||||
|
return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user