Add support for unpadded base64 filenames, "-raw64"

Through base64.RawURLEncoding.

New command-line parameter "-raw64".
This commit is contained in:
Jakob Unterwurzacher 2016-11-01 18:43:22 +01:00
parent 964e0e6b36
commit 2b991c9743
10 changed files with 43 additions and 11 deletions

View File

@ -16,7 +16,7 @@ import (
type argContainer struct { type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version, debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
plaintextnames, quiet, nosyslog, wpanic, plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, ro, reverse, aessiv, nonempty bool longnames, allow_other, ro, reverse, aessiv, nonempty, raw64 bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass, masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, ko, passfile string memprofile, ko, passfile string
// Configuration file name override // Configuration file name override
@ -103,6 +103,7 @@ func parseCliOpts() (args argContainer) {
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode") flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption") flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories") flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
flagSet.BoolVar(&args.raw64, "raw64", false, "Use unpadded base64 for file names")
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.memprofile, "memprofile", "", "Write memory profile to specified file") flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")

View File

@ -37,7 +37,7 @@ func initDir(args *argContainer) {
} }
password := readpassword.Twice(args.extpass) password := readpassword.Twice(args.extpass)
creator := tlog.ProgramName + " " + GitVersion creator := tlog.ProgramName + " " + GitVersion
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv) err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv, args.raw64)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ErrExitInit) os.Exit(ErrExitInit)

View File

@ -49,7 +49,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, creator string, aessiv bool) error { func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool, raw64 bool) error {
var cf ConfFile var cf ConfFile
cf.filename = filename cf.filename = filename
cf.Creator = creator cf.Creator = creator
@ -70,6 +70,9 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
if raw64 {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagRaw64])
}
} }
if aessiv { if aessiv {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV]) cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])

View File

@ -60,7 +60,7 @@ func TestLoadV2StrangeFeature(t *testing.T) {
} }
func TestCreateConfFile(t *testing.T) { func TestCreateConfFile(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false) err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -68,11 +68,10 @@ func TestCreateConfFile(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestCreateConfFileAESSIV(t *testing.T) { func TestCreateConfFileAESSIV(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true) err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -85,6 +84,20 @@ func TestCreateConfFileAESSIV(t *testing.T) {
} }
} }
func TestCreateConfFileRaw64(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, true)
if err != nil {
t.Fatal(err)
}
_, c, err := LoadConfFile("config_test/tmp.conf", "test")
if err != nil {
t.Fatal(err)
}
if !c.IsFeatureFlagSet(FlagRaw64) {
t.Error("FlagRaw64 flag should be set but is not")
}
}
func TestIsFeatureFlagKnown(t *testing.T) { func TestIsFeatureFlagKnown(t *testing.T) {
// Test a few hardcoded values // Test a few hardcoded values
testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"} testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"}

View File

@ -17,6 +17,8 @@ const (
FlagLongNames FlagLongNames
// FlagAESSIV selects an AES-SIV based crypto backend. // FlagAESSIV selects an AES-SIV based crypto backend.
FlagAESSIV FlagAESSIV
// FlagRaw64 enables raw (unpadded) base64 encoding for file names
FlagRaw64
) )
// knownFlags stores the known feature flags and their string representation // knownFlags stores the known feature flags and their string representation
@ -27,6 +29,7 @@ var knownFlags = map[flagIota]string{
FlagGCMIV128: "GCMIV128", FlagGCMIV128: "GCMIV128",
FlagLongNames: "LongNames", FlagLongNames: "LongNames",
FlagAESSIV: "AESSIV", FlagAESSIV: "AESSIV",
FlagRaw64: "Raw64",
} }
// Filesystems that do not have these feature flags set are deprecated. // Filesystems that do not have these feature flags set are deprecated.

View File

@ -18,4 +18,7 @@ type Args struct {
// location. If it is false, reverse mode maps ".gocryptfs.reverse.conf" // location. If it is false, reverse mode maps ".gocryptfs.reverse.conf"
// to "gocryptfs.conf" in the plaintext dir. // to "gocryptfs.conf" in the plaintext dir.
ConfigCustom bool ConfigCustom bool
// Raw64 is true when RawURLEncoding (without padding) should be used for
// file names
Raw64 bool
} }

View File

@ -42,7 +42,7 @@ var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented.
func NewFS(args Args) *FS { func NewFS(args Args) *FS {
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
nameTransform := nametransform.New(cryptoCore, args.LongNames) nameTransform := nametransform.New(cryptoCore, args.LongNames, args.Raw64)
return &FS{ return &FS{
FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir), FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir),

View File

@ -56,7 +56,7 @@ func NewFS(args fusefrontend.Args) pathfs.FileSystem {
initLongnameCache() initLongnameCache()
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
nameTransform := nametransform.New(cryptoCore, args.LongNames) nameTransform := nametransform.New(cryptoCore, args.LongNames, args.Raw64)
return &reverseFS{ return &reverseFS{
// pathfs.defaultFileSystem returns ENOSYS for all operations // pathfs.defaultFileSystem returns ENOSYS for all operations

View File

@ -17,13 +17,20 @@ type NameTransform struct {
cryptoCore *cryptocore.CryptoCore cryptoCore *cryptocore.CryptoCore
longNames bool longNames bool
DirIVCache dirIVCache DirIVCache dirIVCache
// b64 = either base64.URLEncoding or base64.RawURLEncoding
b64 *base64.Encoding
} }
// New returns a new NameTransform instance. // New returns a new NameTransform instance.
func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform { func New(c *cryptocore.CryptoCore, longNames bool, raw64 bool) *NameTransform {
b64 := base64.URLEncoding
if raw64 {
b64 = base64.RawURLEncoding
}
return &NameTransform{ return &NameTransform{
cryptoCore: c, cryptoCore: c,
longNames: longNames, longNames: longNames,
b64: b64,
} }
} }
@ -32,7 +39,7 @@ func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform {
// This function is exported because it allows for a very efficient readdir // This function is exported because it allows for a very efficient readdir
// implementation (read IV once, decrypt all names using this function). // implementation (read IV once, decrypt all names using this function).
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) { func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
bin, err := base64.URLEncoding.DecodeString(cipherName) bin, err := n.b64.DecodeString(cipherName)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -63,6 +70,6 @@ func (n *NameTransform) EncryptName(plainName string, iv []byte) (cipherName64 s
bin := []byte(plainName) bin := []byte(plainName)
bin = pad16(bin) bin = pad16(bin)
bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionEncrypt) bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionEncrypt)
cipherName64 = base64.URLEncoding.EncodeToString(bin) cipherName64 = n.b64.EncodeToString(bin)
return cipherName64 return cipherName64
} }

View File

@ -143,11 +143,13 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
LongNames: args.longnames, LongNames: args.longnames,
CryptoBackend: cryptoBackend, CryptoBackend: cryptoBackend,
ConfigCustom: args._configCustom, ConfigCustom: args._configCustom,
Raw64: args.raw64,
} }
// 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(configfile.FlagPlaintextNames) frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
frontendArgs.Raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64)
if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) { if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
frontendArgs.CryptoBackend = cryptocore.BackendAESSIV frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
} else if args.reverse { } else if args.reverse {