nametransform: add longNameMax parameter
Determines when to start hashing long names instead of hardcoded 255. Will be used to alleviate "name too long" issues some users see on cloud storage. https://github.com/rfjakob/gocryptfs/issues/499
This commit is contained in:
parent
a652be805e
commit
dc32710045
@ -19,7 +19,7 @@ func newTestFS(args Args) *RootNode {
|
|||||||
key := make([]byte, cryptocore.KeyLen)
|
key := make([]byte, cryptocore.KeyLen)
|
||||||
cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
|
cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
|
||||||
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
|
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
|
||||||
n := nametransform.New(cCore.EMECipher, true, true, nil, false)
|
n := nametransform.New(cCore.EMECipher, true, 0, true, nil, false)
|
||||||
rn := NewRootNode(args, cEnc, n)
|
rn := NewRootNode(args, cEnc, n)
|
||||||
oneSec := time.Second
|
oneSec := time.Second
|
||||||
options := &fs.Options{
|
options := &fs.Options{
|
||||||
|
@ -48,7 +48,7 @@ func (be *NameTransform) EncryptAndHashBadName(name string, iv []byte, dirfd int
|
|||||||
//expand suffix on error
|
//expand suffix on error
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if be.longNames && len(cName) > NameMax {
|
if len(cName) > be.longNameMax {
|
||||||
cNamePart = be.HashLongName(cName)
|
cNamePart = be.HashLongName(cName)
|
||||||
}
|
}
|
||||||
cNameBadReverse := cNamePart + name[charpos:len(name)-len(BadnameSuffix)]
|
cNameBadReverse := cNamePart + name[charpos:len(name)-len(BadnameSuffix)]
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package nametransform
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/v2/internal/contentenc"
|
||||||
|
"github.com/rfjakob/gocryptfs/v2/internal/cryptocore"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsLongName(t *testing.T) {
|
func TestIsLongName(t *testing.T) {
|
||||||
@ -28,3 +32,40 @@ func TestRemoveLongNameSuffix(t *testing.T) {
|
|||||||
t.Error(".name suffix not removed")
|
t.Error(".name suffix not removed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newLognamesTestInstance(longNameMax uint8) *NameTransform {
|
||||||
|
key := make([]byte, cryptocore.KeyLen)
|
||||||
|
cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
|
||||||
|
return New(cCore.EMECipher, true, longNameMax, true, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLongNameMax(t *testing.T) {
|
||||||
|
iv := make([]byte, 16)
|
||||||
|
for max := 0; max <= NameMax; max++ {
|
||||||
|
n := newLognamesTestInstance(uint8(max))
|
||||||
|
if max == 0 {
|
||||||
|
// effective value is 255
|
||||||
|
max = NameMax
|
||||||
|
}
|
||||||
|
for l := 0; l <= NameMax+10; l++ {
|
||||||
|
name := strings.Repeat("x", l)
|
||||||
|
out, err := n.EncryptAndHashName(name, iv)
|
||||||
|
if l == 0 || l > NameMax {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("should have rejected a name of length %d, but did not", l)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cName, _ := n.EncryptName(name, iv)
|
||||||
|
rawLen := len(cName)
|
||||||
|
want := LongNameNone
|
||||||
|
if rawLen > max {
|
||||||
|
want = LongNameContent
|
||||||
|
}
|
||||||
|
have := NameType(out)
|
||||||
|
if have != want {
|
||||||
|
t.Errorf("l=%d max=%d: wanted %v, got %v\nname=%q\nout=%q", l, max, want, have, name, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ package nametransform
|
|||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"math"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@ -20,7 +21,9 @@ const (
|
|||||||
// NameTransform is used to transform filenames.
|
// NameTransform is used to transform filenames.
|
||||||
type NameTransform struct {
|
type NameTransform struct {
|
||||||
emeCipher *eme.EMECipher
|
emeCipher *eme.EMECipher
|
||||||
longNames bool
|
// Names longer than `longNameMax` are hashed. Set to MaxInt when
|
||||||
|
// longnames are disabled.
|
||||||
|
longNameMax int
|
||||||
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
|
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
|
||||||
// on the Raw64 feature flag
|
// on the Raw64 feature flag
|
||||||
B64 *base64.Encoding
|
B64 *base64.Encoding
|
||||||
@ -30,17 +33,28 @@ type NameTransform struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new NameTransform instance.
|
// New returns a new NameTransform instance.
|
||||||
func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
|
//
|
||||||
tlog.Debug.Printf("nametransform.New: longNames=%v, raw64=%v, badname=%q",
|
// If `longNames` is set, names longer than `longNameMax` are hashed to
|
||||||
longNames, raw64, badname)
|
// `gocryptfs.longname.[sha256]`.
|
||||||
|
// Pass `longNameMax = 0` to use the default value (255).
|
||||||
|
func New(e *eme.EMECipher, longNames bool, longNameMax uint8, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
|
||||||
|
tlog.Debug.Printf("nametransform.New: longNameMax=%v, raw64=%v, badname=%q",
|
||||||
|
longNameMax, raw64, badname)
|
||||||
b64 := base64.URLEncoding
|
b64 := base64.URLEncoding
|
||||||
if raw64 {
|
if raw64 {
|
||||||
b64 = base64.RawURLEncoding
|
b64 = base64.RawURLEncoding
|
||||||
}
|
}
|
||||||
|
var effectiveLongNameMax int = math.MaxInt
|
||||||
|
if longNames {
|
||||||
|
if longNameMax == 0 {
|
||||||
|
effectiveLongNameMax = NameMax
|
||||||
|
} else {
|
||||||
|
effectiveLongNameMax = int(longNameMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
return &NameTransform{
|
return &NameTransform{
|
||||||
emeCipher: e,
|
emeCipher: e,
|
||||||
longNames: longNames,
|
longNameMax: effectiveLongNameMax,
|
||||||
B64: b64,
|
B64: b64,
|
||||||
badnamePatterns: badname,
|
badnamePatterns: badname,
|
||||||
deterministicNames: deterministicNames,
|
deterministicNames: deterministicNames,
|
||||||
@ -115,7 +129,7 @@ func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if be.longNames && len(cName) > NameMax {
|
if len(cName) > be.longNameMax {
|
||||||
return be.HashLongName(cName), nil
|
return be.HashLongName(cName), nil
|
||||||
}
|
}
|
||||||
return cName, nil
|
return cName, nil
|
||||||
|
2
mount.go
2
mount.go
@ -324,7 +324,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
|
|||||||
// Init crypto backend
|
// Init crypto backend
|
||||||
cCore := cryptocore.New(masterkey, cryptoBackend, IVBits, args.hkdf)
|
cCore := cryptocore.New(masterkey, cryptoBackend, IVBits, args.hkdf)
|
||||||
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
|
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
|
||||||
nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames,
|
nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, 0,
|
||||||
args.raw64, []string(args.badname), frontendArgs.DeterministicNames)
|
args.raw64, []string(args.badname), frontendArgs.DeterministicNames)
|
||||||
// After the crypto backend is initialized,
|
// After the crypto backend is initialized,
|
||||||
// we can purge the master key from memory.
|
// we can purge the master key from memory.
|
||||||
|
Loading…
Reference in New Issue
Block a user