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:
Jakob Unterwurzacher 2021-10-21 09:37:04 +02:00
parent a652be805e
commit dc32710045
5 changed files with 65 additions and 10 deletions

View File

@ -19,7 +19,7 @@ func newTestFS(args Args) *RootNode {
key := make([]byte, cryptocore.KeyLen)
cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
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)
oneSec := time.Second
options := &fs.Options{

View File

@ -48,7 +48,7 @@ func (be *NameTransform) EncryptAndHashBadName(name string, iv []byte, dirfd int
//expand suffix on error
continue
}
if be.longNames && len(cName) > NameMax {
if len(cName) > be.longNameMax {
cNamePart = be.HashLongName(cName)
}
cNameBadReverse := cNamePart + name[charpos:len(name)-len(BadnameSuffix)]

View File

@ -1,7 +1,11 @@
package nametransform
import (
"strings"
"testing"
"github.com/rfjakob/gocryptfs/v2/internal/contentenc"
"github.com/rfjakob/gocryptfs/v2/internal/cryptocore"
)
func TestIsLongName(t *testing.T) {
@ -28,3 +32,40 @@ func TestRemoveLongNameSuffix(t *testing.T) {
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)
}
}
}
}

View File

@ -4,6 +4,7 @@ package nametransform
import (
"crypto/aes"
"encoding/base64"
"math"
"path/filepath"
"syscall"
@ -20,7 +21,9 @@ const (
// NameTransform is used to transform filenames.
type NameTransform struct {
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
// on the Raw64 feature flag
B64 *base64.Encoding
@ -30,17 +33,28 @@ type NameTransform struct {
}
// 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",
longNames, raw64, badname)
//
// If `longNames` is set, names longer than `longNameMax` are hashed to
// `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
if raw64 {
b64 = base64.RawURLEncoding
}
var effectiveLongNameMax int = math.MaxInt
if longNames {
if longNameMax == 0 {
effectiveLongNameMax = NameMax
} else {
effectiveLongNameMax = int(longNameMax)
}
}
return &NameTransform{
emeCipher: e,
longNames: longNames,
longNameMax: effectiveLongNameMax,
B64: b64,
badnamePatterns: badname,
deterministicNames: deterministicNames,
@ -115,7 +129,7 @@ func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, err
if err != nil {
return "", err
}
if be.longNames && len(cName) > NameMax {
if len(cName) > be.longNameMax {
return be.HashLongName(cName), nil
}
return cName, nil

View File

@ -324,7 +324,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
// Init crypto backend
cCore := cryptocore.New(masterkey, cryptoBackend, IVBits, args.hkdf)
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)
// After the crypto backend is initialized,
// we can purge the master key from memory.