crypt: Added option to encrypt directory names or leave them intact - #1240

This commit is contained in:
ishuah 2017-11-06 10:35:53 +03:00 committed by Nick Craig-Wood
parent 72072d7d6b
commit dfaee55ef3
4 changed files with 140 additions and 64 deletions

View file

@ -139,13 +139,15 @@ type cipher struct {
mode NameEncryptionMode mode NameEncryptionMode
buffers sync.Pool // encrypt/decrypt buffers buffers sync.Pool // encrypt/decrypt buffers
cryptoRand io.Reader // read crypto random numbers from here cryptoRand io.Reader // read crypto random numbers from here
dirNameEncrypt bool
} }
// newCipher initialises the cipher. If salt is "" then it uses a built in salt val // newCipher initialises the cipher. If salt is "" then it uses a built in salt val
func newCipher(mode NameEncryptionMode, password, salt string) (*cipher, error) { func newCipher(mode NameEncryptionMode, password, salt string, dirNameEncrypt bool) (*cipher, error) {
c := &cipher{ c := &cipher{
mode: mode, mode: mode,
cryptoRand: rand.Reader, cryptoRand: rand.Reader,
dirNameEncrypt: dirNameEncrypt,
} }
c.buffers.New = func() interface{} { c.buffers.New = func() interface{} {
return make([]byte, blockSize) return make([]byte, blockSize)
@ -469,6 +471,11 @@ func (c *cipher) deobfuscateSegment(ciphertext string) (string, error) {
func (c *cipher) encryptFileName(in string) string { func (c *cipher) encryptFileName(in string) string {
segments := strings.Split(in, "/") segments := strings.Split(in, "/")
for i := range segments { for i := range segments {
// Skip directory name encryption if the user chose to
// leave them intact
if !c.dirNameEncrypt && i != (len(segments)-1) {
continue
}
if c.mode == NameEncryptionStandard { if c.mode == NameEncryptionStandard {
segments[i] = c.encryptSegment(segments[i]) segments[i] = c.encryptSegment(segments[i])
} else { } else {
@ -488,7 +495,7 @@ func (c *cipher) EncryptFileName(in string) string {
// EncryptDirName encrypts a directory path // EncryptDirName encrypts a directory path
func (c *cipher) EncryptDirName(in string) string { func (c *cipher) EncryptDirName(in string) string {
if c.mode == NameEncryptionOff { if c.mode == NameEncryptionOff || !c.dirNameEncrypt {
return in return in
} }
return c.encryptFileName(in) return c.encryptFileName(in)
@ -499,6 +506,11 @@ func (c *cipher) decryptFileName(in string) (string, error) {
segments := strings.Split(in, "/") segments := strings.Split(in, "/")
for i := range segments { for i := range segments {
var err error var err error
// Skip directory name decryption if the user chose to
// leave them intact
if !c.dirNameEncrypt && i != (len(segments)-1) {
continue
}
if c.mode == NameEncryptionStandard { if c.mode == NameEncryptionStandard {
segments[i], err = c.decryptSegment(segments[i]) segments[i], err = c.decryptSegment(segments[i])
} else { } else {
@ -526,7 +538,7 @@ func (c *cipher) DecryptFileName(in string) (string, error) {
// DecryptDirName decrypts a directory path // DecryptDirName decrypts a directory path
func (c *cipher) DecryptDirName(in string) (string, error) { func (c *cipher) DecryptDirName(in string) (string, error) {
if c.mode == NameEncryptionOff { if c.mode == NameEncryptionOff || !c.dirNameEncrypt {
return in, nil return in, nil
} }
return c.decryptFileName(in) return c.decryptFileName(in)

View file

@ -157,7 +157,7 @@ func TestDecodeFileName(t *testing.T) {
} }
func TestEncryptSegment(t *testing.T) { func TestEncryptSegment(t *testing.T) {
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
for _, test := range []struct { for _, test := range []struct {
in string in string
expected string expected string
@ -194,7 +194,7 @@ func TestEncryptSegment(t *testing.T) {
func TestDecryptSegment(t *testing.T) { func TestDecryptSegment(t *testing.T) {
// We've tested the forwards above, now concentrate on the errors // We've tested the forwards above, now concentrate on the errors
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
for _, test := range []struct { for _, test := range []struct {
in string in string
expectedErr error expectedErr error
@ -214,41 +214,54 @@ func TestDecryptSegment(t *testing.T) {
func TestEncryptFileName(t *testing.T) { func TestEncryptFileName(t *testing.T) {
// First standard mode // First standard mode
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptFileName("1")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptFileName("1"))
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptFileName("1/12")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptFileName("1/12"))
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptFileName("1/12/123")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptFileName("1/12/123"))
// Standard mode with directory name encryption off
c, _ = newCipher(NameEncryptionStandard, "", "", false)
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptFileName("1"))
assert.Equal(t, "1/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptFileName("1/12"))
assert.Equal(t, "1/12/qgm4avr35m5loi1th53ato71v0", c.EncryptFileName("1/12/123"))
// Now off mode // Now off mode
c, _ = newCipher(NameEncryptionOff, "", "") c, _ = newCipher(NameEncryptionOff, "", "", true)
assert.Equal(t, "1/12/123.bin", c.EncryptFileName("1/12/123")) assert.Equal(t, "1/12/123.bin", c.EncryptFileName("1/12/123"))
// Obfuscation mode // Obfuscation mode
c, _ = newCipher(NameEncryptionObfuscated, "", "") c, _ = newCipher(NameEncryptionObfuscated, "", "", true)
assert.Equal(t, "49.6/99.23/150.890/53.!!lipps", c.EncryptFileName("1/12/123/!hello")) assert.Equal(t, "49.6/99.23/150.890/53.!!lipps", c.EncryptFileName("1/12/123/!hello"))
assert.Equal(t, "161.\u00e4", c.EncryptFileName("\u00a1")) assert.Equal(t, "161.\u00e4", c.EncryptFileName("\u00a1"))
assert.Equal(t, "160.\u03c2", c.EncryptFileName("\u03a0")) assert.Equal(t, "160.\u03c2", c.EncryptFileName("\u03a0"))
// Obfuscation mode with directory name encryption off
c, _ = newCipher(NameEncryptionObfuscated, "", "", false)
assert.Equal(t, "1/12/123/53.!!lipps", c.EncryptFileName("1/12/123/!hello"))
assert.Equal(t, "161.\u00e4", c.EncryptFileName("\u00a1"))
assert.Equal(t, "160.\u03c2", c.EncryptFileName("\u03a0"))
} }
func TestDecryptFileName(t *testing.T) { func TestDecryptFileName(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
mode NameEncryptionMode mode NameEncryptionMode
dirNameEncrypt bool
in string in string
expected string expected string
expectedErr error expectedErr error
}{ }{
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil},
{NameEncryptionStandard, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil}, {NameEncryptionStandard, true, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize},
{NameEncryptionOff, "1/12/123.bin", "1/12/123", nil}, {NameEncryptionStandard, false, "1/12/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil},
{NameEncryptionOff, "1/12/123.bix", "", ErrorNotAnEncryptedFile}, {NameEncryptionOff, true, "1/12/123.bin", "1/12/123", nil},
{NameEncryptionOff, ".bin", "", ErrorNotAnEncryptedFile}, {NameEncryptionOff, true, "1/12/123.bix", "", ErrorNotAnEncryptedFile},
{NameEncryptionObfuscated, "!.hello", "hello", nil}, {NameEncryptionOff, true, ".bin", "", ErrorNotAnEncryptedFile},
{NameEncryptionObfuscated, "hello", "", ErrorNotAnEncryptedFile}, {NameEncryptionObfuscated, true, "!.hello", "hello", nil},
{NameEncryptionObfuscated, "161.\u00e4", "\u00a1", nil}, {NameEncryptionObfuscated, true, "hello", "", ErrorNotAnEncryptedFile},
{NameEncryptionObfuscated, "160.\u03c2", "\u03a0", nil}, {NameEncryptionObfuscated, true, "161.\u00e4", "\u00a1", nil},
{NameEncryptionObfuscated, true, "160.\u03c2", "\u03a0", nil},
{NameEncryptionObfuscated, false, "1/12/123/53.!!lipps", "1/12/123/!hello", nil},
} { } {
c, _ := newCipher(test.mode, "", "") c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt)
actual, actualErr := c.DecryptFileName(test.in) actual, actualErr := c.DecryptFileName(test.in)
what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode)
assert.Equal(t, test.expected, actual, what) assert.Equal(t, test.expected, actual, what)
@ -266,7 +279,7 @@ func TestEncDecMatches(t *testing.T) {
{NameEncryptionObfuscated, "1/2/3/4/!hello\u03a0"}, {NameEncryptionObfuscated, "1/2/3/4/!hello\u03a0"},
{NameEncryptionObfuscated, "Avatar The Last Airbender"}, {NameEncryptionObfuscated, "Avatar The Last Airbender"},
} { } {
c, _ := newCipher(test.mode, "", "") c, _ := newCipher(test.mode, "", "", true)
out, err := c.DecryptFileName(c.EncryptFileName(test.in)) out, err := c.DecryptFileName(c.EncryptFileName(test.in))
what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode)
assert.Equal(t, out, test.in, what) assert.Equal(t, out, test.in, what)
@ -276,32 +289,39 @@ func TestEncDecMatches(t *testing.T) {
func TestEncryptDirName(t *testing.T) { func TestEncryptDirName(t *testing.T) {
// First standard mode // First standard mode
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptDirName("1")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptDirName("1"))
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptDirName("1/12")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptDirName("1/12"))
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptDirName("1/12/123")) assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptDirName("1/12/123"))
// Standard mode with dir name encryption off
c, _ = newCipher(NameEncryptionStandard, "", "", false)
assert.Equal(t, "1/12", c.EncryptDirName("1/12"))
assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123"))
// Now off mode // Now off mode
c, _ = newCipher(NameEncryptionOff, "", "") c, _ = newCipher(NameEncryptionOff, "", "", true)
assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123")) assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123"))
} }
func TestDecryptDirName(t *testing.T) { func TestDecryptDirName(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
mode NameEncryptionMode mode NameEncryptionMode
dirNameEncrypt bool
in string in string
expected string expected string
expectedErr error expectedErr error
}{ }{
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil},
{NameEncryptionStandard, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil}, {NameEncryptionStandard, true, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil},
{NameEncryptionStandard, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize}, {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize},
{NameEncryptionOff, "1/12/123.bin", "1/12/123.bin", nil}, {NameEncryptionStandard, false, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", nil},
{NameEncryptionOff, "1/12/123", "1/12/123", nil}, {NameEncryptionStandard, false, "1/12/123", "1/12/123", nil},
{NameEncryptionOff, ".bin", ".bin", nil}, {NameEncryptionOff, true, "1/12/123.bin", "1/12/123.bin", nil},
{NameEncryptionOff, true, "1/12/123", "1/12/123", nil},
{NameEncryptionOff, true, ".bin", ".bin", nil},
} { } {
c, _ := newCipher(test.mode, "", "") c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt)
actual, actualErr := c.DecryptDirName(test.in) actual, actualErr := c.DecryptDirName(test.in)
what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode)
assert.Equal(t, test.expected, actual, what) assert.Equal(t, test.expected, actual, what)
@ -310,7 +330,7 @@ func TestDecryptDirName(t *testing.T) {
} }
func TestEncryptedSize(t *testing.T) { func TestEncryptedSize(t *testing.T) {
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
for _, test := range []struct { for _, test := range []struct {
in int64 in int64
expected int64 expected int64
@ -334,7 +354,7 @@ func TestEncryptedSize(t *testing.T) {
func TestDecryptedSize(t *testing.T) { func TestDecryptedSize(t *testing.T) {
// Test the errors since we tested the reverse above // Test the errors since we tested the reverse above
c, _ := newCipher(NameEncryptionStandard, "", "") c, _ := newCipher(NameEncryptionStandard, "", "", true)
for _, test := range []struct { for _, test := range []struct {
in int64 in int64
expectedErr error expectedErr error
@ -705,7 +725,7 @@ func (z *zeroes) Read(p []byte) (n int, err error) {
// Test encrypt decrypt with different buffer sizes // Test encrypt decrypt with different buffer sizes
func testEncryptDecrypt(t *testing.T, bufSize int, copySize int64) { func testEncryptDecrypt(t *testing.T, bufSize int, copySize int64) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
c.cryptoRand = &zeroes{} // zero out the nonce c.cryptoRand = &zeroes{} // zero out the nonce
buf := make([]byte, bufSize) buf := make([]byte, bufSize)
@ -775,7 +795,7 @@ func TestEncryptData(t *testing.T) {
{[]byte{1}, file1}, {[]byte{1}, file1},
{[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, file16}, {[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, file16},
} { } {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
@ -798,7 +818,7 @@ func TestEncryptData(t *testing.T) {
} }
func TestNewEncrypter(t *testing.T) { func TestNewEncrypter(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
@ -820,7 +840,7 @@ func TestNewEncrypter(t *testing.T) {
// Test the stream returning 0, io.ErrUnexpectedEOF - this used to // Test the stream returning 0, io.ErrUnexpectedEOF - this used to
// cause a fatal loop // cause a fatal loop
func TestNewEncrypterErrUnexpectedEOF(t *testing.T) { func TestNewEncrypterErrUnexpectedEOF(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
in := &errorReader{io.ErrUnexpectedEOF} in := &errorReader{io.ErrUnexpectedEOF}
@ -857,7 +877,7 @@ func (c *closeDetector) Close() error {
} }
func TestNewDecrypter(t *testing.T) { func TestNewDecrypter(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
@ -900,7 +920,7 @@ func TestNewDecrypter(t *testing.T) {
// Test the stream returning 0, io.ErrUnexpectedEOF // Test the stream returning 0, io.ErrUnexpectedEOF
func TestNewDecrypterErrUnexpectedEOF(t *testing.T) { func TestNewDecrypterErrUnexpectedEOF(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
in2 := &errorReader{io.ErrUnexpectedEOF} in2 := &errorReader{io.ErrUnexpectedEOF}
@ -916,7 +936,7 @@ func TestNewDecrypterErrUnexpectedEOF(t *testing.T) {
} }
func TestNewDecrypterSeek(t *testing.T) { func TestNewDecrypterSeek(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
c.cryptoRand = &zeroes{} // nodge the crypto rand generator c.cryptoRand = &zeroes{} // nodge the crypto rand generator
@ -976,7 +996,7 @@ func TestNewDecrypterSeek(t *testing.T) {
} }
func TestDecrypterRead(t *testing.T) { func TestDecrypterRead(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
// Test truncating the file at each possible point // Test truncating the file at each possible point
@ -1040,7 +1060,7 @@ func TestDecrypterRead(t *testing.T) {
} }
func TestDecrypterClose(t *testing.T) { func TestDecrypterClose(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
cd := newCloseDetector(bytes.NewBuffer(file16)) cd := newCloseDetector(bytes.NewBuffer(file16))
@ -1078,7 +1098,7 @@ func TestDecrypterClose(t *testing.T) {
} }
func TestPutGetBlock(t *testing.T) { func TestPutGetBlock(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
block := c.getBlock() block := c.getBlock()
@ -1089,7 +1109,7 @@ func TestPutGetBlock(t *testing.T) {
} }
func TestKey(t *testing.T) { func TestKey(t *testing.T) {
c, err := newCipher(NameEncryptionStandard, "", "") c, err := newCipher(NameEncryptionStandard, "", "", true)
assert.NoError(t, err) assert.NoError(t, err)
// Check zero keys OK // Check zero keys OK

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"path" "path"
"strconv"
"strings" "strings"
"github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs"
@ -41,6 +42,19 @@ func init() {
Help: "Very simple filename obfuscation.", Help: "Very simple filename obfuscation.",
}, },
}, },
}, {
Name: "directory_name_encryption",
Help: "Option to either encrypt directory names or leave them intact.",
Examples: []fs.OptionExample{
{
Value: "true",
Help: "Encrypt directory names.",
},
{
Value: "false",
Help: "Don't encrypt directory names, leave them intact.",
},
},
}, { }, {
Name: "password", Name: "password",
Help: "Password or pass phrase for encryption.", Help: "Password or pass phrase for encryption.",
@ -60,6 +74,10 @@ func NewFs(name, rpath string) (fs.Fs, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
dirNameEncrypt, err := strconv.ParseBool(fs.ConfigFileGet(name, "directory_name_encryption", "true"))
if err != nil {
return nil, err
}
password := fs.ConfigFileGet(name, "password", "") password := fs.ConfigFileGet(name, "password", "")
if password == "" { if password == "" {
return nil, errors.New("password not set in config file") return nil, errors.New("password not set in config file")
@ -75,7 +93,7 @@ func NewFs(name, rpath string) (fs.Fs, error) {
return nil, errors.Wrap(err, "failed to decrypt password2") return nil, errors.Wrap(err, "failed to decrypt password2")
} }
} }
cipher, err := newCipher(mode, password, salt) cipher, err := newCipher(mode, password, salt, dirNameEncrypt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to make cipher") return nil, errors.Wrap(err, "failed to make cipher")
} }

View file

@ -74,6 +74,13 @@ Choose a number from below, or type in your own value
3 / Very simple filename obfuscation. 3 / Very simple filename obfuscation.
\ "obfuscate" \ "obfuscate"
filename_encryption> 2 filename_encryption> 2
Option to either encrypt directory names or leave them intact.
Choose a number from below, or type in your own value
1 / Encrypt directory names.
\ "true"
2 / Don't encrypt directory names, leave them intact.
\ "false"
filename_encryption> 1
Password or pass phrase for encryption. Password or pass phrase for encryption.
y) Yes type in my own password y) Yes type in my own password
g) Generate random password g) Generate random password
@ -256,6 +263,25 @@ characters in length then you should be OK on all providers.
There may be an even more secure file name encryption mode in the There may be an even more secure file name encryption mode in the
future which will address the long file name problem. future which will address the long file name problem.
### Directory name encryption ###
Crypt offers the option of encrypting dir names or leaving them intact.
There are two options:
True
Encrypts the whole file path including directory names
Example:
`1/12/123.txt` is encrypted to
`p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0`
False
Only encrypts file names, skips directory names
Example:
`1/12/123/txt` is encrypted to
`1/12/qgm4avr35m5loi1th53ato71v0`
### Modified time and hashes ### ### Modified time and hashes ###
Crypt stores modification times using the underlying remote so support Crypt stores modification times using the underlying remote so support