diff --git a/README.md b/README.md index 91a9da03..7ba77ab7 100644 --- a/README.md +++ b/README.md @@ -150,9 +150,31 @@ LS: 1.75 RM: 4.42 ``` +Secure hardware encryption +-------------------------- + +gocryptfs supports openhardware cryptographic device +[Trezor One](https://github.com/trezor/trezor-mcu) + + $ mkdir cipher plain + $ ./gocryptfs -init -cryptowallet_encrypt_masterkey cipher + $ ./gocryptfs cipher plain + +Notes: +* Flag `-cryptowallet_encrypt_masterkey` encrypts/decrypts masterkey using trezor + so there's a decrypted masterkey in RAM while you're working with + the decrypted directory. +* ATM, the only supported Trezor device is "Trezor One" +* There was no security audit of the code quality (related to the Trezor + devices support) + Changelog --------- +vNEXT, in progress +* Add a support of encrypting the master key using (open)hardware device "Trezor One" + (`-cryptowallet_encrypt_masterkey`) + v1.5, 2018-06-12 * **Support extended attributes (xattr)** in forward mode ([#217](https://github.com/rfjakob/gocryptfs/issues/217)). Older gocryptfs versions diff --git a/cli_args.go b/cli_args.go index 08b31869..cec0efaf 100644 --- a/cli_args.go +++ b/cli_args.go @@ -20,12 +20,12 @@ import ( type argContainer struct { debug, init, zerokey, fusedebug, openssl, passwd, fg, version, plaintextnames, quiet, nosyslog, wpanic, - longnames, allow_other, reverse, aessiv, nonempty, raw64, + longnames, allow_other, reverse, aessiv, cryptowalletencryptmasterkey, nonempty, raw64, noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info, sharedstorage, devrandom, fsck bool // Mount options with opposites dev, nodev, suid, nosuid, exec, noexec, rw, ro bool - masterkey, mountpoint, cipherdir, cpuprofile, extpass, + masterkey, mountpoint, cipherdir, cpuprofile, extpass, cryptowalletkeyname, memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string // Configuration file name override config string @@ -125,6 +125,7 @@ func parseCliOpts() (args argContainer) { "Only works if user_allow_other is set in /etc/fuse.conf.") flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode") flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption") + flagSet.BoolVar(&args.cryptowalletencryptmasterkey, "cryptowallet_encrypt_masterkey", false, `Encrypt master key through a hardware cryptowallet device.`) flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories") flagSet.BoolVar(&args.raw64, "raw64", true, "Use unpadded base64 for file names") flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing") @@ -154,6 +155,7 @@ func parseCliOpts() (args argContainer) { flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file") flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf") flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt") + flagSet.StringVar(&args.cryptowalletkeyname, "cryptowallet_keyname", "gocryptfs", "A name of the key for a cryptowallet device") flagSet.StringVar(&args.passfile, "passfile", "", "Read password from file") flagSet.StringVar(&args.ko, "ko", "", "Pass additional options directly to the kernel, comma-separated list") flagSet.StringVar(&args.ctlsock, "ctlsock", "", "Create control socket at specified path") diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go index 1531eb4f..deecf80f 100644 --- a/gocryptfs-xray/xray_main.go +++ b/gocryptfs-xray/xray_main.go @@ -11,7 +11,6 @@ import ( "github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/readpassword" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -60,16 +59,12 @@ func main() { func dumpMasterKey(fn string) { tlog.Info.Enabled = false - pw := readpassword.Once("", "") - masterkey, _, err := configfile.LoadConfFile(fn, pw) + masterkey, _, err := configfile.LoadConfFile(fn, true, "") if err != nil { fmt.Fprintln(os.Stderr, err) exitcodes.Exit(err) } fmt.Println(hex.EncodeToString(masterkey)) - for i := range pw { - pw[i] = 0 - } } func inspectCiphertext(fd *os.File) { diff --git a/help.go b/help.go index 714fcbf6..f3f82024 100644 --- a/help.go +++ b/help.go @@ -21,6 +21,8 @@ Common Options (use -hh to show all): -allow_other Allow other users to access the mount -config Custom path to config file -ctlsock Create control socket at location + -cryptowallet_encrypt_masterkey Encrypt master key through a hardware cryptowallet device (with -init) + -cryptowallet_keyname Set the key name for a cryptowallet device (default: "gocryptfs") -extpass Call external program to prompt for the password -fg Stay in the foreground -fusedebug Debug FUSE calls diff --git a/init_dir.go b/init_dir.go index 0e1ad956..eac98b5c 100644 --- a/init_dir.go +++ b/init_dir.go @@ -63,15 +63,19 @@ func initDir(args *argContainer) { os.Exit(exitcodes.Init) } } - // Choose password for config file - if args.extpass == "" { - tlog.Info.Printf("Choose a password for protecting your files.") - } { creator := tlog.ProgramName + " " + GitVersion - password := readpassword.Twice(args.extpass) - readpassword.CheckTrailingGarbage() - err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv, args.devrandom) + var password []byte + if !args.cryptowalletencryptmasterkey { + // Choose password for config file + if args.extpass == "" { + tlog.Info.Printf("Choose a password for protecting your files.") + } + password = readpassword.Twice(args.extpass) + readpassword.CheckTrailingGarbage() + } + err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, + args.aessiv, args.cryptowalletencryptmasterkey, args.cryptowalletkeyname, args.devrandom) if err != nil { tlog.Fatal.Println(err) os.Exit(exitcodes.WriteConf) diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index 753dd726..3280effa 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -12,7 +12,9 @@ import ( "github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/exitcodes" + "github.com/rfjakob/gocryptfs/internal/readpassword" "github.com/rfjakob/gocryptfs/internal/tlog" + "github.com/xaionaro-go/cryptoWallet" ) import "os" @@ -40,6 +42,8 @@ type ConfFile struct { ScryptObject ScryptKDF // Version is the On-Disk-Format version this filesystem uses Version uint16 + // CryptowalletKeyname is a string that is passed to a cryptowallet device as a key name + CryptowalletKeyname string // FeatureFlags is a list of feature flags this filesystem has enabled. // If gocryptfs encounters a feature flag it does not support, it will refuse // mounting. This mechanism is analogous to the ext4 feature flags that are @@ -67,7 +71,7 @@ func randBytesDevRandom(n int) []byte { // 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 []byte, plaintextNames bool, logN int, creator string, aessiv bool, devrandom bool) error { +func CreateConfFile(filename string, password []byte, plaintextNames bool, logN int, creator string, aessiv bool, cryptowalletEncryptMasterkey bool, cryptowalletKeyname string, devrandom bool) error { var cf ConfFile cf.filename = filename cf.Creator = creator @@ -87,6 +91,11 @@ func CreateConfFile(filename string, password []byte, plaintextNames bool, logN if aessiv { cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV]) } + if cryptowalletEncryptMasterkey { + cf.CryptowalletKeyname = cryptowalletKeyname + cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagCryptowalletEncryptMasterkey]) + } + { // Generate new random master key var key []byte @@ -95,10 +104,15 @@ func CreateConfFile(filename string, password []byte, plaintextNames bool, logN } else { key = cryptocore.RandBytes(cryptocore.KeyLen) } - // Encrypt it using the password - // This sets ScryptObject and EncryptedKey - // Note: this looks at the FeatureFlags, so call it AFTER setting them. - cf.EncryptKey(key, password, logN) + if cryptowalletEncryptMasterkey { + // Encrypt it using a cryptowallet device + cf.EncryptKeyByCryptowallet(key) + } else { + // Encrypt it using the password + // This sets ScryptObject and EncryptedKey + // Note: this looks at the FeatureFlags, so call it AFTER setting them. + cf.EncryptKeyByPassword(key, password, logN) + } for i := range key { key[i] = 0 } @@ -108,13 +122,17 @@ func CreateConfFile(filename string, password []byte, plaintextNames bool, logN return cf.WriteFile() } +func getPin(title, description, ok, cancel string) ([]byte, error) { + return readpassword.Once("", title), nil +} + // LoadConfFile - read config file from disk and decrypt the // contained key using "password". // Returns the decrypted key and the ConfFile object // // If "password" is empty, the config file is read // but the key is not decrypted (returns nil in its place). -func LoadConfFile(filename string, password []byte) ([]byte, *ConfFile, error) { +func LoadConfFile(filename string, retrieveMasterKey bool, extpass string) ([]byte, *ConfFile, error) { var cf ConfFile cf.filename = filename @@ -171,12 +189,28 @@ func LoadConfFile(filename string, password []byte) ([]byte, *ConfFile, error) { return nil, nil, exitcodes.NewErr("Deprecated filesystem", exitcodes.DeprecatedFS) } - if len(password) == 0 { - // We have validated the config file, but without a password we cannot - // decrypt the master key. Return only the parsed config. + + if !retrieveMasterKey { return nil, &cf, nil } + if cf.IsFeatureFlagSet(FlagCryptowalletEncryptMasterkey) { + // if `-cryptowallet_encrypt_masterkey` is enabled then the password is passed to a cryptowallet device + // directly (via pinentry) and we should ask for it here + wallet := cryptoWallet.FindAny() + wallet.SetGetPinFunc(getPin) + var key []byte + key, err = wallet.DecryptKey(cryptocore.CryptowalletBIPPath, cf.EncryptedKey, []byte{}, cf.CryptowalletKeyname) + return key, &cf, err + } + + password := readpassword.Once(extpass, "") + defer func() { + for i := range password { + password[i] = 0 + } + }() + // Generate derived key from password scryptHash := cf.ScryptObject.DeriveKey(password) @@ -195,11 +229,21 @@ func LoadConfFile(filename string, password []byte) ([]byte, *ConfFile, error) { return key, &cf, err } -// EncryptKey - encrypt "key" using an scrypt hash generated from "password" + +func (cf *ConfFile) EncryptKeyByCryptowallet(key []byte) { + var err error + wallet := cryptoWallet.FindAny() + wallet.SetGetPinFunc(getPin) + cf.EncryptedKey, err = wallet.EncryptKey(cryptocore.CryptowalletBIPPath, key, []byte{}, cf.CryptowalletKeyname) + if err != nil { + log.Fatal(err) + } +} +// EncryptKeyByPassword - encrypt "key" using an scrypt hash generated from "password" // and store it in cf.EncryptedKey. // Uses scrypt with cost parameter logN and stores the scrypt parameters in // cf.ScryptObject. -func (cf *ConfFile) EncryptKey(key []byte, password []byte, logN int) { +func (cf *ConfFile) EncryptKeyByPassword(key []byte, password []byte, logN int) { // Generate scrypt-derived key from password cf.ScryptObject = NewScryptKDF(logN) scryptHash := cf.ScryptObject.DeriveKey(password) diff --git a/internal/configfile/config_test.go b/internal/configfile/config_test.go index 15728c6b..fb2cc9b1 100644 --- a/internal/configfile/config_test.go +++ b/internal/configfile/config_test.go @@ -8,10 +8,10 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) -var testPw = []byte("test") +var testPw = "test" func TestLoadV1(t *testing.T) { - _, _, err := LoadConfFile("config_test/v1.conf", testPw) + _, _, err := LoadConfFile("config_test/v1.conf", true, "/bin/echo "+testPw) if err == nil { t.Errorf("Outdated v1 config file must fail to load but it didn't") } else if testing.Verbose() { @@ -24,7 +24,7 @@ func TestLoadV1(t *testing.T) { func TestLoadV2(t *testing.T) { t1 := time.Now() - _, _, err := LoadConfFile("config_test/v2.conf", testPw) + _, _, err := LoadConfFile("config_test/v2.conf", true, "/bin/echo "+testPw) if err != nil { t.Errorf("Could not load v2 config file: %v", err) } @@ -39,21 +39,21 @@ func TestLoadV2PwdError(t *testing.T) { if !testing.Verbose() { tlog.Warn.Enabled = false } - _, _, err := LoadConfFile("config_test/v2.conf", []byte("wrongpassword")) + _, _, err := LoadConfFile("config_test/v2.conf", true, "/bin/echo wrongpassword") if err == nil { t.Errorf("Loading with wrong password must fail but it didn't") } } func TestLoadV2Feature(t *testing.T) { - _, _, err := LoadConfFile("config_test/PlaintextNames.conf", testPw) + _, _, err := LoadConfFile("config_test/PlaintextNames.conf", true, "/bin/echo "+testPw) if err != nil { t.Errorf("Could not load v2 PlaintextNames config file: %v", err) } } func TestLoadV2StrangeFeature(t *testing.T) { - _, _, err := LoadConfFile("config_test/StrangeFeature.conf", testPw) + _, _, err := LoadConfFile("config_test/StrangeFeature.conf", true, "/bin/echo "+testPw) if err == nil { t.Errorf("Loading unknown feature must fail but it didn't") } else if testing.Verbose() { @@ -62,11 +62,11 @@ func TestLoadV2StrangeFeature(t *testing.T) { } func TestCreateConfDefault(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", testPw, false, 10, "test", false, false) + err := CreateConfFile("config_test/tmp.conf", []byte(testPw), false, 10, "test", false, false, "", false) if err != nil { t.Fatal(err) } - _, c, err := LoadConfFile("config_test/tmp.conf", testPw) + _, c, err := LoadConfFile("config_test/tmp.conf", true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } @@ -83,18 +83,18 @@ func TestCreateConfDefault(t *testing.T) { } func TestCreateConfDevRandom(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", testPw, false, 10, "test", false, true) + err := CreateConfFile("config_test/tmp.conf", []byte(testPw), false, 10, "test", false, false, "", true) if err != nil { t.Fatal(err) } } func TestCreateConfPlaintextnames(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", testPw, true, 10, "test", false, false) + err := CreateConfFile("config_test/tmp.conf", []byte(testPw), true, 10, "test", false, false, "", false) if err != nil { t.Fatal(err) } - _, c, err := LoadConfFile("config_test/tmp.conf", testPw) + _, c, err := LoadConfFile("config_test/tmp.conf", true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } @@ -111,11 +111,11 @@ func TestCreateConfPlaintextnames(t *testing.T) { // Reverse mode uses AESSIV func TestCreateConfFileAESSIV(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", testPw, false, 10, "test", true, false) + err := CreateConfFile("config_test/tmp.conf", []byte(testPw), false, 10, "test", true, false, "", false) if err != nil { t.Fatal(err) } - _, c, err := LoadConfFile("config_test/tmp.conf", testPw) + _, c, err := LoadConfFile("config_test/tmp.conf", true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go index 2d609f24..6c241265 100644 --- a/internal/configfile/feature_flags.go +++ b/internal/configfile/feature_flags.go @@ -17,6 +17,8 @@ const ( FlagLongNames // FlagAESSIV selects an AES-SIV based crypto backend. FlagAESSIV + // FlagCryptowalletEncrypEncrypttMasterkey additionally encrypt master key using "Cryptowallet" + FlagCryptowalletEncryptMasterkey // FlagRaw64 enables raw (unpadded) base64 encoding for file names FlagRaw64 // FlagHKDF enables HKDF-derived keys for use with GCM, EME and SIV @@ -29,14 +31,15 @@ const ( // knownFlags stores the known feature flags and their string representation var knownFlags = map[flagIota]string{ - FlagPlaintextNames: "PlaintextNames", - FlagDirIV: "DirIV", - FlagEMENames: "EMENames", - FlagGCMIV128: "GCMIV128", - FlagLongNames: "LongNames", - FlagAESSIV: "AESSIV", - FlagRaw64: "Raw64", - FlagHKDF: "HKDF", + FlagPlaintextNames: "PlaintextNames", + FlagDirIV: "DirIV", + FlagEMENames: "EMENames", + FlagGCMIV128: "GCMIV128", + FlagLongNames: "LongNames", + FlagAESSIV: "AESSIV", + FlagCryptowalletEncryptMasterkey: "CryptowalletEncryptMasterkey", + FlagRaw64: "Raw64", + FlagHKDF: "HKDF", } // Filesystems that do not have these feature flags set are deprecated. diff --git a/internal/configfile/scrypt_test.go b/internal/configfile/scrypt_test.go index 8f7a5c8f..830e63d9 100644 --- a/internal/configfile/scrypt_test.go +++ b/internal/configfile/scrypt_test.go @@ -23,7 +23,7 @@ ok github.com/rfjakob/gocryptfs/cryptfs 18.772s func benchmarkScryptN(n int, b *testing.B) { kdf := NewScryptKDF(n) for i := 0; i < b.N; i++ { - kdf.DeriveKey(testPw) + kdf.DeriveKey([]byte(testPw)) } } diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index d66f3900..f072e7fd 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -22,6 +22,8 @@ const ( KeyLen = 32 // AuthTagLen is the length of a GCM auth tag in bytes. AuthTagLen = 16 + // BIP32 path for a cryptowallet device + CryptowalletBIPPath = `m/71'/a6'/3'/45'/96'` ) // AEADTypeEnum indicates the type of AEAD backend in use. diff --git a/main.go b/main.go index 226a20a2..eaeabc28 100644 --- a/main.go +++ b/main.go @@ -44,14 +44,9 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf // password). if args.masterkey != "" { masterkey = parseMasterKey(args.masterkey, false) - _, confFile, err = configfile.LoadConfFile(args.config, nil) + _, confFile, err = configfile.LoadConfFile(args.config, false, "") } else { - pw := readpassword.Once(args.extpass, "") - tlog.Info.Println("Decrypting master key") - masterkey, confFile, err = configfile.LoadConfFile(args.config, pw) - for i := range pw { - pw[i] = 0 - } + masterkey, confFile, err = configfile.LoadConfFile(args.config, true, args.extpass) } if err != nil { tlog.Fatal.Println(err) @@ -71,10 +66,14 @@ func changePassword(args *argContainer) { if err != nil { exitcodes.Exit(err) } + if confFile.IsFeatureFlagSet(configfile.FlagCryptowalletEncryptMasterkey) { + tlog.Fatal.Printf("The master key is encrypted by a cryptowallet device but not by a password.") + os.Exit(exitcodes.PasswordIncorrect) + } tlog.Info.Println("Please enter your new password.") newPw := readpassword.Twice(args.extpass) readpassword.CheckTrailingGarbage() - confFile.EncryptKey(masterkey, newPw, confFile.ScryptObject.LogN()) + confFile.EncryptKeyByPassword(masterkey, newPw, confFile.ScryptObject.LogN()) for i := range newPw { newPw[i] = 0 } diff --git a/mount.go b/mount.go index cbe77b41..12f933ec 100644 --- a/mount.go +++ b/mount.go @@ -203,12 +203,16 @@ func initFuseFrontend(args *argContainer) (pfs pathfs.FileSystem, wipeKeys func( frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames) args.raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64) args.hkdf = confFile.IsFeatureFlagSet(configfile.FlagHKDF) + args.cryptowalletkeyname = confFile.CryptowalletKeyname if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) { cryptoBackend = cryptocore.BackendAESSIV } else if args.reverse { tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file") os.Exit(exitcodes.Usage) } + if confFile.IsFeatureFlagSet(configfile.FlagCryptowalletEncryptMasterkey) { + args.cryptowalletencryptmasterkey = true + } } // If allow_other is set and we run as root, try to give newly created files to // the right user. diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index 55083604..13e56bc9 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -17,7 +17,7 @@ import ( "github.com/rfjakob/gocryptfs/tests/test_helpers" ) -var testPw = []byte("test") +var testPw = "test" func TestMain(m *testing.M) { test_helpers.ResetTmpDir(false) @@ -34,7 +34,7 @@ func TestMain(m *testing.M) { // Test -init flag func TestInit(t *testing.T) { dir := test_helpers.InitFS(t) - _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, testPw) + _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } @@ -51,7 +51,7 @@ func TestInitDevRandom(t *testing.T) { // Test -init with -aessiv func TestInitAessiv(t *testing.T) { dir := test_helpers.InitFS(t, "-aessiv") - _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, testPw) + _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } @@ -63,7 +63,7 @@ func TestInitAessiv(t *testing.T) { // Test -init with -reverse func TestInitReverse(t *testing.T) { dir := test_helpers.InitFS(t, "-reverse") - _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfReverseName, testPw) + _, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfReverseName, true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) } diff --git a/tests/plaintextnames/plaintextnames_test.go b/tests/plaintextnames/plaintextnames_test.go index 6b513aaa..6360920c 100644 --- a/tests/plaintextnames/plaintextnames_test.go +++ b/tests/plaintextnames/plaintextnames_test.go @@ -15,7 +15,7 @@ import ( var cDir string var pDir string -var testPw = []byte("test") +var testPw = "test" // Create and mount "-plaintextnames" fs func TestMain(m *testing.M) { @@ -29,7 +29,7 @@ func TestMain(m *testing.M) { // Only the PlaintextNames feature flag should be set func TestFlags(t *testing.T) { - _, cf, err := configfile.LoadConfFile(cDir+"/gocryptfs.conf", testPw) + _, cf, err := configfile.LoadConfFile(cDir+"/gocryptfs.conf", true, "/bin/echo "+testPw) if err != nil { t.Fatal(err) }