reverse: add gcmsiv flag and associated tests

This commit is contained in:
Jakob Unterwurzacher 2016-09-25 15:05:09 +02:00
parent f8da264222
commit 2050c7f3b3
11 changed files with 153 additions and 64 deletions

View File

@ -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

View File

@ -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")

View File

@ -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)
}

View File

@ -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

View File

@ -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 {

20
main.go
View File

@ -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)
}

View File

@ -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

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}