Skip to content

Commit

Permalink
nametransform: add longNameMax parameter
Browse files Browse the repository at this point in the history
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.

#499
  • Loading branch information
rfjakob committed Oct 21, 2021
1 parent a652be8 commit dc32710
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 10 deletions.
2 changes: 1 addition & 1 deletion internal/fusefrontend/xattr_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion internal/nametransform/badname.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
41 changes: 41 additions & 0 deletions internal/nametransform/longnames_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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)
}
}
}
}
28 changes: 21 additions & 7 deletions internal/nametransform/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package nametransform
import (
"crypto/aes"
"encoding/base64"
"math"
"path/filepath"
"syscall"

Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit dc32710

Please sign in to comment.