diff options
| -rw-r--r-- | cli_args.go | 3 | ||||
| -rw-r--r-- | init_dir.go | 21 | ||||
| -rw-r--r-- | internal/configfile/config_file.go | 6 | ||||
| -rw-r--r-- | internal/configfile/config_test.go | 8 | ||||
| -rw-r--r-- | internal/configfile/feature_flags.go | 4 | ||||
| -rw-r--r-- | internal/exitcodes/exitcodes.go | 3 | ||||
| -rw-r--r-- | internal/readpassword/trezor.go | 26 | ||||
| -rw-r--r-- | main.go | 36 | ||||
| -rw-r--r-- | masterkey.go | 4 | ||||
| -rw-r--r-- | tests/fsck/fsck_test.go | 1 | 
10 files changed, 86 insertions, 26 deletions
| diff --git a/cli_args.go b/cli_args.go index 08b3186..7e8deeb 100644 --- a/cli_args.go +++ b/cli_args.go @@ -22,7 +22,7 @@ type argContainer struct {  	plaintextnames, quiet, nosyslog, wpanic,  	longnames, allow_other, reverse, aessiv, nonempty, raw64,  	noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info, -	sharedstorage, devrandom, fsck bool +	sharedstorage, devrandom, fsck, trezor bool  	// Mount options with opposites  	dev, nodev, suid, nosuid, exec, noexec, rw, ro bool  	masterkey, mountpoint, cipherdir, cpuprofile, extpass, @@ -138,6 +138,7 @@ func parseCliOpts() (args argContainer) {  	flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")  	flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")  	flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR") +	flagSet.BoolVar(&args.trezor, "trezor", false, "Protect the masterkey using a SatoshiLabs Trezor instead of a password")  	// Mount options with opposites  	flagSet.BoolVar(&args.dev, "dev", false, "Allow device files") diff --git a/init_dir.go b/init_dir.go index 0e1ad95..ea6fff4 100644 --- a/init_dir.go +++ b/init_dir.go @@ -43,11 +43,12 @@ func isDir(dir string) error {  	return nil  } -// initDir prepares a directory for use as a gocryptfs storage directory. +// initDir handles "gocryptfs -init". It prepares a directory for use as a +// gocryptfs storage directory.  // In forward mode, this means creating the gocryptfs.conf and gocryptfs.diriv  // files in an empty directory.  // In reverse mode, we create .gocryptfs.reverse.conf and the directory does -// not to be empty. +// not need to be empty.  func initDir(args *argContainer) {  	var err error  	if args.reverse { @@ -68,10 +69,18 @@ func initDir(args *argContainer) {  		tlog.Info.Printf("Choose a password for protecting your files.")  	}  	{ +		var password []byte +		if args.trezor { +			// Get binary data from from Trezor +			password = readpassword.Trezor() +		} else { +			// Normal password entry +			password = readpassword.Twice(args.extpass) +			readpassword.CheckTrailingGarbage() +		}  		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) +		err = configfile.CreateConfFile(args.config, password, args.plaintextnames, +			args.scryptn, creator, args.aessiv, args.devrandom, args.trezor)  		if err != nil {  			tlog.Fatal.Println(err)  			os.Exit(exitcodes.WriteConf) @@ -81,7 +90,7 @@ func initDir(args *argContainer) {  		}  		// password runs out of scope here  	} -	// Forward mode with filename encryption enabled needs a gocryptfs.diriv +	// Forward mode with filename encryption enabled needs a gocryptfs.diriv file  	// in the root dir  	if !args.plaintextnames && !args.reverse {  		err = nametransform.WriteDirIV(nil, args.cipherdir) diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index 0d5e545..da6c4da 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -67,7 +67,8 @@ 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, devrandom bool, trezor bool) error {  	var cf ConfFile  	cf.filename = filename  	cf.Creator = creator @@ -87,6 +88,9 @@ func CreateConfFile(filename string, password []byte, plaintextNames bool, logN  	if aessiv {  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])  	} +	if trezor { +		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagTrezor]) +	}  	{  		// Generate new random master key  		var key []byte diff --git a/internal/configfile/config_test.go b/internal/configfile/config_test.go index 15728c6..3152ab5 100644 --- a/internal/configfile/config_test.go +++ b/internal/configfile/config_test.go @@ -62,7 +62,7 @@ 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", testPw, false, 10, "test", false, false, false)  	if err != nil {  		t.Fatal(err)  	} @@ -83,14 +83,14 @@ 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", testPw, false, 10, "test", false, true, false)  	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", testPw, true, 10, "test", false, false, false)  	if err != nil {  		t.Fatal(err)  	} @@ -111,7 +111,7 @@ 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", testPw, false, 10, "test", true, false, false)  	if err != nil {  		t.Fatal(err)  	} diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go index 2d609f2..141b007 100644 --- a/internal/configfile/feature_flags.go +++ b/internal/configfile/feature_flags.go @@ -25,6 +25,9 @@ const (  	// Note that this flag does not change the password hashing algorithm  	// which always is scrypt.  	FlagHKDF +	// FlagTrezor means that "-trezor" was used when creating the filesystem. +	// The masterkey is protected using a Trezor device instead of a password. +	FlagTrezor  )  // knownFlags stores the known feature flags and their string representation @@ -37,6 +40,7 @@ var knownFlags = map[flagIota]string{  	FlagAESSIV:         "AESSIV",  	FlagRaw64:          "Raw64",  	FlagHKDF:           "HKDF", +	FlagTrezor:         "Trezor",  }  // Filesystems that do not have these feature flags set are deprecated. diff --git a/internal/exitcodes/exitcodes.go b/internal/exitcodes/exitcodes.go index fc65166..3fbfdc6 100644 --- a/internal/exitcodes/exitcodes.go +++ b/internal/exitcodes/exitcodes.go @@ -65,6 +65,9 @@ const (  	FsckErrors = 26  	// DeprecatedFS - this filesystem is deprecated  	DeprecatedFS = 27 +	// TrezorError - an error was encountered while interacting with a Trezor +	// device +	TrezorError = 28  )  // Err wraps an error with an associated numeric exit code diff --git a/internal/readpassword/trezor.go b/internal/readpassword/trezor.go new file mode 100644 index 0000000..37dde79 --- /dev/null +++ b/internal/readpassword/trezor.go @@ -0,0 +1,26 @@ +package readpassword + +import ( +	"os" + +	"github.com/rfjakob/gocryptfs/internal/exitcodes" +	"github.com/rfjakob/gocryptfs/internal/tlog" +) + +// Trezor reads 16 deterministically derived bytes from a +// SatoshiLabs Trezor USB security module. +// The bytes are pseudorandom binary data and may contain null bytes. +// This function either succeeds and returns 16 bytes or calls os.Exit to end +// the application. +func Trezor() []byte { +	var err error +	// TODO try to read bytes here.... +	// Handle errors +	if err != nil { +		tlog.Fatal.Printf("xxx some error was encountered...") +		os.Exit(exitcodes.TrezorError) +	} + +	tlog.Warn.Println("XXX readpassword.Trezor(): not implemented yet - returning hardcoded dummy bytes XXX") +	return []byte("1234567890123456") +} @@ -33,26 +33,33 @@ var raceDetector bool  // loadConfig loads the config file "args.config", prompting the user for the password  func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile, err error) { -	// Check if the file can be opened at all before prompting for a password -	fd, err := os.Open(args.config) +	// First check if the file can be read at all, and find out if a Trezor should +	// be used instead of a password. +	_, cf1, err := configfile.LoadConfFile(args.config, nil)  	if err != nil {  		tlog.Fatal.Printf("Cannot open config file: %v", err) -		return nil, nil, exitcodes.NewErr(err.Error(), exitcodes.OpenConf) +		return nil, nil, err  	} -	fd.Close() -	// The user has passed the master key (probably because he forgot the -	// password). +	// The user has passed the master key on the command line (probably because +	// he forgot the password).  	if args.masterkey != "" {  		masterkey = parseMasterKey(args.masterkey, false) -		_, confFile, err = configfile.LoadConfFile(args.config, nil) +		return masterkey, cf1, nil +	} +	var pw []byte +	if cf1.IsFeatureFlagSet(configfile.FlagTrezor) { +		// Get binary data from from Trezor +		pw = readpassword.Trezor()  	} 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 -		} +		// Normal password entry +		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 +	} +  	if err != nil {  		tlog.Fatal.Println(err)  		return nil, nil, err @@ -71,6 +78,9 @@ func changePassword(args *argContainer) {  		if err != nil {  			exitcodes.Exit(err)  		} +		if len(masterkey) == 0 { +			panic("empty masterkey") +		}  		tlog.Info.Println("Please enter your new password.")  		newPw := readpassword.Twice(args.extpass)  		readpassword.CheckTrailingGarbage() diff --git a/masterkey.go b/masterkey.go index c67f115..1b4d9c3 100644 --- a/masterkey.go +++ b/masterkey.go @@ -103,7 +103,9 @@ func getMasterKey(args *argContainer) (masterkey []byte, confFile *configfile.Co  		}  		exitcodes.Exit(err)  	} -	readpassword.CheckTrailingGarbage() +	if !args.trezor { +		readpassword.CheckTrailingGarbage() +	}  	if !args.fsck {  		// We only want to print the masterkey message on a normal mount.  		printMasterKey(masterkey) diff --git a/tests/fsck/fsck_test.go b/tests/fsck/fsck_test.go index 2faed97..8e73cf7 100644 --- a/tests/fsck/fsck_test.go +++ b/tests/fsck/fsck_test.go @@ -69,6 +69,7 @@ func TestExampleFses(t *testing.T) {  		fsNames = append(fsNames, e.Name())  	}  	for _, n := range fsNames { +		t.Logf("Checking %q", n)  		path := "../example_filesystems/" + n  		cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", path)  		outBin, err := cmd.CombinedOutput() | 
