diff options
| author | Jakob Unterwurzacher | 2016-09-25 15:05:09 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-09-25 16:43:17 +0200 | 
| commit | 2050c7f3b3822a4a3329d4ba5b146d269ef05b4d (patch) | |
| tree | 69e5639a02498e37fa4f1aa3af6f605f756f42cc | |
| parent | f8da264222f7348c6b9e6dd9d372200f850d2878 (diff) | |
reverse: add gcmsiv flag and associated tests
| -rw-r--r-- | Documentation/MANPAGE.md | 9 | ||||
| -rw-r--r-- | cli_args.go | 3 | ||||
| -rw-r--r-- | init_dir.go | 16 | ||||
| -rw-r--r-- | internal/configfile/config_file.go | 8 | ||||
| -rw-r--r-- | internal/configfile/config_test.go | 4 | ||||
| -rw-r--r-- | main.go | 20 | ||||
| -rwxr-xr-x | test.bash | 4 | ||||
| -rw-r--r-- | tests/normal/cli_test.go | 59 | ||||
| -rw-r--r-- | tests/reverse/correctness_test.go | 17 | ||||
| -rw-r--r-- | tests/reverse/longname_perf_test.go | 45 | ||||
| -rw-r--r-- | tests/reverse/main_test.go | 32 | 
11 files changed, 153 insertions, 64 deletions
| diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index bf696f1..5c31a92 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -58,6 +58,9 @@ to mount the gocryptfs filesytem without user interaction.  **-fusedebug**  :	Enable fuse library debug output +**-gcmsiv** +:	Use the GCM-SIV encryption mode (implied by -reverse) +  **-init**  :	Initialize encrypted directory @@ -94,7 +97,7 @@ runs as root, you can enable device files by passing the opposite mount option,  interesting. For a complete liste see the section  `FILESYSTEM-INDEPENDENT MOUNT OPTIONS` in mount(8). -**-openssl bool** +**-openssl bool/"auto"**  :	Use OpenSSL instead of built-in Go crypto (default "auto"). Using  built-in crypto is 4x slower unless your CPU has AES instructions and  you are using Go 1.6+. In mode "auto", gocrypts chooses the faster @@ -109,6 +112,10 @@ option.  **-q, -quiet**  :	Quiet - silence informational messages +**-reverse** +:	Reverse mode shows a read-only encrypted view of a plaintext +directory +  **-ro**  :	Mount the filesystem read-only diff --git a/cli_args.go b/cli_args.go index 4c91f44..b50c350 100644 --- a/cli_args.go +++ b/cli_args.go @@ -14,7 +14,7 @@ import (  type argContainer struct {  	debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,  	plaintextnames, quiet, nosyslog, wpanic, -	longnames, allow_other, ro, reverse bool +	longnames, allow_other, ro, reverse, gcmsiv bool  	masterkey, mountpoint, cipherdir, cpuprofile, extpass,  	memprofile, o string  	// Configuration file name override @@ -51,6 +51,7 @@ func parseCliOpts() (args argContainer) {  		"Only works if user_allow_other is set in /etc/fuse.conf.")  	flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")  	flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode") +	flagSet.BoolVar(&args.gcmsiv, "gcmsiv", false, "GCM-SIV encryption")  	flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")  	flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")  	flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file") diff --git a/init_dir.go b/init_dir.go index fac4053..c9c7be6 100644 --- a/init_dir.go +++ b/init_dir.go @@ -39,7 +39,7 @@ func initDir(args *argContainer) {  	}  	password := readpassword.Twice(args.extpass)  	creator := tlog.ProgramName + " " + GitVersion -	err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.reverse) +	err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.gcmsiv)  	if err != nil {  		tlog.Fatal.Println(err)  		os.Exit(ERREXIT_INIT) @@ -53,8 +53,14 @@ func initDir(args *argContainer) {  			os.Exit(ERREXIT_INIT)  		}  	} - -	tlog.Info.Printf(tlog.ColorGreen + "The filesystem has been created successfully." + tlog.ColorReset) +	mountArgs := "" +	fsName := "gocryptfs" +	if args.reverse { +		mountArgs = " -reverse" +		fsName = "gocryptfs-reverse" +	} +	tlog.Info.Printf(tlog.ColorGreen+"The %s filesystem has been created successfully."+tlog.ColorReset, +		fsName)  	wd, _ := os.Getwd()  	friendlyPath, _ := filepath.Rel(wd, args.cipherdir)  	if strings.HasPrefix(friendlyPath, "../") { @@ -62,7 +68,7 @@ func initDir(args *argContainer) {  		// keep the absolute path.  		friendlyPath = args.cipherdir  	} -	tlog.Info.Printf(tlog.ColorGrey+"You can now mount it using: %s %s MOUNTPOINT"+tlog.ColorReset, -		tlog.ProgramName, friendlyPath) +	tlog.Info.Printf(tlog.ColorGrey+"You can now mount it using: %s%s %s MOUNTPOINT"+tlog.ColorReset, +		tlog.ProgramName, mountArgs, friendlyPath)  	os.Exit(0)  } diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index b1504b4..32e7e66 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -45,7 +45,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, creator string, reverse bool) error { +func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, gcmsiv bool) error {  	var cf ConfFile  	cf.filename = filename  	cf.Creator = creator @@ -59,7 +59,7 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN  	cf.EncryptKey(key, password, logN)  	// Set feature flags -	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128]) +	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128]) // 128-bit IVs  	if plaintextNames {  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])  	} else { @@ -67,8 +67,8 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])  	} -	if reverse { -		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMSIV]) +	if gcmsiv { +		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMSIV]) // GCM-SIV encryption mode  	}  	// Write file to disk diff --git a/internal/configfile/config_test.go b/internal/configfile/config_test.go index 72c25f6..ac85c8d 100644 --- a/internal/configfile/config_test.go +++ b/internal/configfile/config_test.go @@ -71,7 +71,7 @@ func TestCreateConfFile(t *testing.T) {  } -func TestCreateConfFileReverse(t *testing.T) { +func TestCreateConfFileGCMSIV(t *testing.T) {  	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true)  	if err != nil {  		t.Fatal(err) @@ -87,7 +87,7 @@ func TestCreateConfFileReverse(t *testing.T) {  func TestIsFeatureFlagKnown(t *testing.T) {  	// Test a few hardcoded values -	testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames"} +	testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "GCMSIV"}  	// And also everything in knownFlags (yes, it is likely that we end up with  	// some duplicates. Does not matter.)  	for _, f := range knownFlags { @@ -111,11 +111,9 @@ func printVersion() {  func main() {  	runtime.GOMAXPROCS(4)  	var err error -  	// Parse all command-line options (i.e. arguments starting with "-")  	// into "args". Path arguments are parsed below.  	args := parseCliOpts() -  	// Fork a child into the background if "-f" is not set AND we are mounting  	// a filesystem. The child will do all the work.  	if !args.foreground && flagSet.NArg() == 2 { @@ -152,6 +150,10 @@ func main() {  	if args.quiet {  		tlog.Info.Enabled = false  	} +	// "-reverse" implies "-gcmsiv" +	if args.reverse { +		args.gcmsiv = true +	}  	// "-config"  	if args.config != "" {  		args.config, err = filepath.Abs(args.config) @@ -201,7 +203,7 @@ func main() {  	} else {  		tlog.Debug.Printf("OpenSSL enabled")  	} -	// Operation flags: init, passwd or mount +	// Operation flags: -init or -passwd; otherwise: mount  	// "-init"  	if args.init {  		if flagSet.NArg() > 1 { @@ -288,8 +290,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  	if args.openssl {  		cryptoBackend = cryptocore.BackendOpenSSL  	} -	if args.reverse { -		// reverse implies GCMSIV +	if args.gcmsiv {  		cryptoBackend = cryptocore.BackendGCMSIV  	}  	frontendArgs := fusefrontend.Args{ @@ -306,7 +307,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  		if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) {  			frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV  		} else if args.reverse { -			tlog.Fatal.Printf("GCMSIV is required by reverse mode, but not enabled in the config file") +			tlog.Fatal.Printf("GCM-SIV is required by reverse mode, but not enabled in the config file")  			os.Exit(ERREXIT_USAGE)  		}  	} @@ -317,11 +318,14 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  	}  	jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")  	tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes)) - +	if frontendArgs.CryptoBackend == cryptocore.BackendGCMSIV { +		tlog.Info.Printf(tlog.ColorYellow + +			"Warning: The GCM-SIV format used by reverse mode is not yet finalized.\n" + +			"The on-disk format will change in the future." + tlog.ColorReset) +	}  	var finalFs pathfs.FileSystem  	if args.reverse {  		finalFs = fusefrontend_reverse.NewFS(frontendArgs) -		tlog.Info.Printf(tlog.ColorYellow + "REVERSE MODE IS EXPERIMENTAL!" + tlog.ColorReset)  	} else {  		finalFs = fusefrontend.NewFS(frontendArgs)  	} @@ -8,8 +8,8 @@ source build.bash  go test ./... $* -# Clean up after ourself, but don't descend into maybe still mounted -# example filesystems +# Clean up after ourself, but don't descend into possibly still mounted +# example filesystems.  # The tests cannot to this themselves as they are run in parallel  rm -Rf --one-file-system /tmp/gocryptfs-test-parent diff --git a/tests/normal/cli_test.go b/tests/normal/cli_test.go index ed3111b..62ad217 100644 --- a/tests/normal/cli_test.go +++ b/tests/normal/cli_test.go @@ -5,7 +5,6 @@ package normal  import (  	"os"  	"os/exec" -	"path/filepath"  	"testing"  	"github.com/rfjakob/gocryptfs/internal/configfile" @@ -24,18 +23,45 @@ func TestMain(m *testing.M) {  // Test -init flag  func TestInit(t *testing.T) {  	dir := test_helpers.InitFS(t) -	_, err := os.Stat(filepath.Join(dir, configfile.ConfDefaultName)) +	_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test")  	if err != nil {  		t.Fatal(err)  	} +	if c.IsFeatureFlagSet(configfile.FlagGCMSIV) { +		t.Error("GCMSIV flag should not be set") +	}  } -// Test -passwd flag -func TestPasswd(t *testing.T) { -	// Create FS -	dir := test_helpers.InitFS(t) +// Test -init with -gcmsiv +func TestInitGcmsiv(t *testing.T) { +	dir := test_helpers.InitFS(t, "-gcmsiv") +	_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test") +	if err != nil { +		t.Fatal(err) +	} +	if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) { +		t.Error("GCMSIV flag should be set but is not") +	} +} + +// Test -init with -reverse +func TestInitReverse(t *testing.T) { +	dir := test_helpers.InitFS(t, "-reverse") +	_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfReverseName, "test") +	if err != nil { +		t.Fatal(err) +	} +	if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) { +		t.Error("GCMSIV flag should be set but is not") +	} +} + +func testPasswd(t *testing.T, dir string, extraArgs ...string) {  	// Change password using "-extpass" -	cmd := exec.Command(test_helpers.GocryptfsBinary, "-q", "-passwd", "-extpass", "echo test", dir) +	args := []string{"-q", "-passwd", "-extpass", "echo test"} +	args = append(args, extraArgs...) +	args = append(args, dir) +	cmd := exec.Command(test_helpers.GocryptfsBinary, args...)  	cmd.Stdout = os.Stdout  	cmd.Stderr = os.Stderr  	err := cmd.Run() @@ -43,7 +69,10 @@ func TestPasswd(t *testing.T) {  		t.Error(err)  	}  	// Change password using stdin -	cmd = exec.Command(test_helpers.GocryptfsBinary, "-q", "-passwd", dir) +	args = []string{"-q", "-passwd", "-extpass", "echo test"} +	args = append(args, extraArgs...) +	args = append(args, dir) +	cmd = exec.Command(test_helpers.GocryptfsBinary, args...)  	cmd.Stdout = os.Stdout  	cmd.Stderr = os.Stderr  	p, err := cmd.StdinPipe() @@ -65,6 +94,20 @@ func TestPasswd(t *testing.T) {  	}  } +// Test -passwd flag +func TestPasswd(t *testing.T) { +	// Create FS +	dir := test_helpers.InitFS(t) +	testPasswd(t, dir) +} + +// Test -passwd with -reverse +func TestPasswdReverse(t *testing.T) { +	// Create FS +	dir := test_helpers.InitFS(t, "-reverse") +	testPasswd(t, dir, "-reverse") +} +  // Test -init & -config flag  func TestInitConfig(t *testing.T) {  	config := test_helpers.TmpDir + "/TestInitConfig.conf" diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go new file mode 100644 index 0000000..e268815 --- /dev/null +++ b/tests/reverse/correctness_test.go @@ -0,0 +1,17 @@ +package reverse_test + +import ( +	"os" +	"testing" +) + +func TestLongnameStat(t *testing.T) { +	_, err := os.Stat(dirA + "/" + "") +	if err != nil { +		t.Error(err) +	} +	_, err = os.Stat(dirA + "/" + "") +	if err != nil { +		t.Error(err) +	} +} diff --git a/tests/reverse/longname_perf_test.go b/tests/reverse/longname_perf_test.go index 43acd25..f170ad7 100644 --- a/tests/reverse/longname_perf_test.go +++ b/tests/reverse/longname_perf_test.go @@ -5,34 +5,17 @@ import (  	"fmt"  	"os"  	"testing" - -	"github.com/rfjakob/gocryptfs/tests/test_helpers"  ) -var dirA, dirB, x240 string - -func TestMain(m *testing.M) { -	x240 = string(bytes.Repeat([]byte("x"), 240)) -	dirA = test_helpers.TmpDir + "/a" -	dirB = test_helpers.TmpDir + "/b" -	os.Mkdir(dirA, 0700) -	os.Mkdir(dirB, 0700) -	generateFiles(dirA) -	test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse") -	r := m.Run() -	test_helpers.UnmountPanic(dirB) -	os.RemoveAll(test_helpers.TmpDir) -	os.Exit(r) -} - -func genName(i int) string { -	return fmt.Sprintf("%04d.%s", i, x240) +func genName(i int, postfix string) string { +	return fmt.Sprintf("%04d.%s", i, postfix)  }  // Create 10000 files with long names -func generateFiles(dir string) { +func generateLongnameFiles(dir string) { +	x240 := string(bytes.Repeat([]byte("x"), 240))  	for i := 0; i < 100000; i++ { -		n := genName(i) +		n := genName(i, x240)  		f, err := os.Create(dir + "/" + n)  		if err != nil {  			panic(err) @@ -41,18 +24,9 @@ func generateFiles(dir string) {  	}  } -func TestLongnameStat(t *testing.T) { -	_, err := os.Stat(dirA + "/" + genName(0)) -	if err != nil { -		t.Error(err) -	} -	_, err = os.Stat(dirA + "/" + genName(9999)) -	if err != nil { -		t.Error(err) -	} -} -  func BenchmarkLongnameStat(b *testing.B) { +	// Setup +	generateLongnameFiles(dirA)  	dirFd, err := os.Open(dirB)  	if err != nil {  		b.Fatal(err) @@ -63,6 +37,7 @@ func BenchmarkLongnameStat(b *testing.B) {  	}  	l := len(encryptedNames)  	dirFd.Close() +	// Benchmark  	b.ResetTimer()  	for i := 0; i < b.N; i++ {  		_, err := os.Stat(dirB + "/" + encryptedNames[i%l]) @@ -70,4 +45,8 @@ func BenchmarkLongnameStat(b *testing.B) {  			b.Fatal(err)  		}  	} +	// Cleanup +	b.StopTimer() +	os.RemoveAll(dirA) +	os.Mkdir(dirA, 0700)  } diff --git a/tests/reverse/main_test.go b/tests/reverse/main_test.go new file mode 100644 index 0000000..3d10750 --- /dev/null +++ b/tests/reverse/main_test.go @@ -0,0 +1,32 @@ +package reverse_test + +import ( +	"os" +	"testing" + +	"github.com/rfjakob/gocryptfs/tests/test_helpers" +) + +var dirA, dirB, dirC string + +func TestMain(m *testing.M) { +	dirA = test_helpers.TmpDir + "/a" +	dirB = test_helpers.TmpDir + "/b" +	dirC = test_helpers.TmpDir + "/c" +	if err := os.Mkdir(dirA, 0700); err != nil { +		panic(err) +	} +	if err := os.Mkdir(dirB, 0700); err != nil { +		panic(err) +	} +	if err := os.Mkdir(dirC, 0700); err != nil { +		panic(err) +	} +	test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse") +	test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-gcmsiv") +	r := m.Run() +	test_helpers.UnmountPanic(dirC) +	test_helpers.UnmountPanic(dirB) +	os.RemoveAll(test_helpers.TmpDir) +	os.Exit(r) +} | 
