From 8c61a09be2ee5a415b0bb304bb427286d9027a05 Mon Sep 17 00:00:00 2001 From: Sinan Tan Date: Sat, 13 Nov 2021 11:48:34 +0800 Subject: [PATCH] crypt: add test cases and documentation for base64 and base32768 filename encoding #5801 --- backend/crypt/cipher_test.go | 658 +++++++++++++++++++++++++++-------- backend/crypt/crypt_test.go | 44 ++- docs/content/crypt.md | 8 + 3 files changed, 573 insertions(+), 137 deletions(-) diff --git a/backend/crypt/cipher_test.go b/backend/crypt/cipher_test.go index 41c37b2a8..565ef6af5 100644 --- a/backend/crypt/cipher_test.go +++ b/backend/crypt/cipher_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/base32" + "encoding/base64" "errors" "fmt" "io" @@ -11,6 +12,7 @@ import ( "strings" "testing" + "github.com/Max-Sum/base32768" "github.com/rclone/rclone/backend/crypt/pkcs7" "github.com/rclone/rclone/lib/readers" "github.com/stretchr/testify/assert" @@ -45,11 +47,31 @@ func TestNewNameEncryptionModeString(t *testing.T) { assert.Equal(t, NameEncryptionMode(3).String(), "Unknown mode #3") } -func TestEncodeFileName(t *testing.T) { - for _, test := range []struct { - in string - expected string - }{ +type EncodingTestCase struct { + in string + expected string +} + +func testEncodeFileName(t *testing.T, encoding string, testCases []EncodingTestCase, caseInsensitive bool) { + for _, test := range testCases { + enc, err := NewNameEncoding(encoding) + assert.NoError(t, err, "There should be no error creating name encoder for base32.") + actual := enc.EncodeToString([]byte(test.in)) + assert.Equal(t, actual, test.expected, fmt.Sprintf("in=%q", test.in)) + recovered, err := enc.DecodeString(test.expected) + assert.NoError(t, err) + assert.Equal(t, string(recovered), test.in, fmt.Sprintf("reverse=%q", test.expected)) + if caseInsensitive { + in := strings.ToUpper(test.expected) + recovered, err = enc.DecodeString(in) + assert.NoError(t, err) + assert.Equal(t, string(recovered), test.in, fmt.Sprintf("reverse=%q", in)) + } + } +} + +func TestEncodeFileNameBase32(t *testing.T) { + testEncodeFileName(t, "base32", []EncodingTestCase{ {"", ""}, {"1", "64"}, {"12", "64p0"}, @@ -67,20 +89,56 @@ func TestEncodeFileName(t *testing.T) { {"12345678901234", "64p36d1l6orjge9g64p36d0"}, {"123456789012345", "64p36d1l6orjge9g64p36d1l"}, {"1234567890123456", "64p36d1l6orjge9g64p36d1l6o"}, - } { - actual := encodeFileName([]byte(test.in)) - assert.Equal(t, actual, test.expected, fmt.Sprintf("in=%q", test.in)) - recovered, err := decodeFileName(test.expected) - assert.NoError(t, err) - assert.Equal(t, string(recovered), test.in, fmt.Sprintf("reverse=%q", test.expected)) - in := strings.ToUpper(test.expected) - recovered, err = decodeFileName(in) - assert.NoError(t, err) - assert.Equal(t, string(recovered), test.in, fmt.Sprintf("reverse=%q", in)) - } + }, true) } -func TestDecodeFileName(t *testing.T) { +func TestEncodeFileNameBase64(t *testing.T) { + testEncodeFileName(t, "base64", []EncodingTestCase{ + {"", ""}, + {"1", "MQ"}, + {"12", "MTI"}, + {"123", "MTIz"}, + {"1234", "MTIzNA"}, + {"12345", "MTIzNDU"}, + {"123456", "MTIzNDU2"}, + {"1234567", "MTIzNDU2Nw"}, + {"12345678", "MTIzNDU2Nzg"}, + {"123456789", "MTIzNDU2Nzg5"}, + {"1234567890", "MTIzNDU2Nzg5MA"}, + {"12345678901", "MTIzNDU2Nzg5MDE"}, + {"123456789012", "MTIzNDU2Nzg5MDEy"}, + {"1234567890123", "MTIzNDU2Nzg5MDEyMw"}, + {"12345678901234", "MTIzNDU2Nzg5MDEyMzQ"}, + {"123456789012345", "MTIzNDU2Nzg5MDEyMzQ1"}, + {"1234567890123456", "MTIzNDU2Nzg5MDEyMzQ1Ng"}, + }, false) +} + +func TestEncodeFileNameBase32768(t *testing.T) { + testEncodeFileName(t, "base32768", []EncodingTestCase{ + {"", ""}, + {"1", "㼿"}, + {"12", "㻙ɟ"}, + {"123", "㻙ⲿ"}, + {"1234", "㻙ⲍƟ"}, + {"12345", "㻙ⲍ⍟"}, + {"123456", "㻙ⲍ⍆ʏ"}, + {"1234567", "㻙ⲍ⍆觟"}, + {"12345678", "㻙ⲍ⍆觓ɧ"}, + {"123456789", "㻙ⲍ⍆觓栯"}, + {"1234567890", "㻙ⲍ⍆觓栩ɣ"}, + {"12345678901", "㻙ⲍ⍆觓栩朧"}, + {"123456789012", "㻙ⲍ⍆觓栩朤ʅ"}, + {"1234567890123", "㻙ⲍ⍆觓栩朤談"}, + {"12345678901234", "㻙ⲍ⍆觓栩朤諆ɔ"}, + {"123456789012345", "㻙ⲍ⍆觓栩朤諆媕"}, + {"1234567890123456", "㻙ⲍ⍆觓栩朤諆媕䆿"}, + }, false) +} + +func TestDecodeFileNameBase32(t *testing.T) { + enc, err := NewNameEncoding("base32") + assert.NoError(t, err, "There should be no error creating name encoder for base32.") // We've tested decoding the valid ones above, now concentrate on the invalid ones for _, test := range []struct { in string @@ -90,17 +148,65 @@ func TestDecodeFileName(t *testing.T) { {"!", base32.CorruptInputError(0)}, {"hello=hello", base32.CorruptInputError(5)}, } { - actual, actualErr := decodeFileName(test.in) + actual, actualErr := enc.DecodeString(test.in) assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) } } -func TestEncryptSegment(t *testing.T) { - c, _ := newCipher(NameEncryptionStandard, "", "", true) +func TestDecodeFileNameBase64(t *testing.T) { + enc, err := NewNameEncoding("base64") + assert.NoError(t, err, "There should be no error creating name encoder for base32.") + // We've tested decoding the valid ones above, now concentrate on the invalid ones for _, test := range []struct { - in string - expected string + in string + expectedErr error }{ + {"64=", base64.CorruptInputError(2)}, + {"!", base64.CorruptInputError(0)}, + {"Hello=Hello", base64.CorruptInputError(5)}, + } { + actual, actualErr := enc.DecodeString(test.in) + assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) + } +} + +func TestDecodeFileNameBase32768(t *testing.T) { + enc, err := NewNameEncoding("base32768") + assert.NoError(t, err, "There should be no error creating name encoder for base32.") + // We've tested decoding the valid ones above, now concentrate on the invalid ones + for _, test := range []struct { + in string + expectedErr error + }{ + {"㼿c", base32768.CorruptInputError(1)}, + {"!", base32768.CorruptInputError(0)}, + {"㻙ⲿ=㻙ⲿ", base32768.CorruptInputError(2)}, + } { + actual, actualErr := enc.DecodeString(test.in) + assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) + } +} + +func testEncryptSegment(t *testing.T, encoding string, testCases []EncodingTestCase, caseInsensitive bool) { + enc, _ := NewNameEncoding(encoding) + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + for _, test := range testCases { + actual := c.encryptSegment(test.in) + assert.Equal(t, test.expected, actual, fmt.Sprintf("Testing %q", test.in)) + recovered, err := c.decryptSegment(test.expected) + assert.NoError(t, err, fmt.Sprintf("Testing reverse %q", test.expected)) + assert.Equal(t, test.in, recovered, fmt.Sprintf("Testing reverse %q", test.expected)) + if caseInsensitive { + in := strings.ToUpper(test.expected) + recovered, err = c.decryptSegment(in) + assert.NoError(t, err, fmt.Sprintf("Testing reverse %q", in)) + assert.Equal(t, test.in, recovered, fmt.Sprintf("Testing reverse %q", in)) + } + } +} + +func TestEncryptSegmentBase32(t *testing.T) { + testEncryptSegment(t, "base32", []EncodingTestCase{ {"", ""}, {"1", "p0e52nreeaj0a5ea7s64m4j72s"}, {"12", "l42g6771hnv3an9cgc8cr2n1ng"}, @@ -118,26 +224,61 @@ func TestEncryptSegment(t *testing.T) { {"12345678901234", "moq0uqdlqrblrc5pa5u5c7hq9g"}, {"123456789012345", "eeam3li4rnommi3a762h5n7meg"}, {"1234567890123456", "mijbj0frqf6ms7frcr6bd9h0env53jv96pjaaoirk7forcgpt70g"}, - } { - actual := c.encryptSegment(test.in) - assert.Equal(t, test.expected, actual, fmt.Sprintf("Testing %q", test.in)) - recovered, err := c.decryptSegment(test.expected) - assert.NoError(t, err, fmt.Sprintf("Testing reverse %q", test.expected)) - assert.Equal(t, test.in, recovered, fmt.Sprintf("Testing reverse %q", test.expected)) - in := strings.ToUpper(test.expected) - recovered, err = c.decryptSegment(in) - assert.NoError(t, err, fmt.Sprintf("Testing reverse %q", in)) - assert.Equal(t, test.in, recovered, fmt.Sprintf("Testing reverse %q", in)) - } + }, true) } -func TestDecryptSegment(t *testing.T) { +func TestEncryptSegmentBase64(t *testing.T) { + testEncryptSegment(t, "base64", []EncodingTestCase{ + {"", ""}, + {"1", "yBxRX25ypgUVyj8MSxJnFw"}, + {"12", "qQUDHOGN_jVdLIMQzYrhvA"}, + {"123", "1CxFf2Mti1xIPYlGruDh-A"}, + {"1234", "RL-xOTmsxsG7kuTy2XJUxw"}, + {"12345", "3FP_GHoeBJdq0yLgaED8IQ"}, + {"123456", "Xc4T1Gqrs3OVYnrE6dpEWQ"}, + {"1234567", "uZeEzssOnDWHEOzLqjwpog"}, + {"12345678", "8noiTP5WkkbEuijsPhOpxQ"}, + {"123456789", "GeNxgLA0wiaGAKU3U7qL4Q"}, + {"1234567890", "x1DUhdmqoVWYVBLD3dha-A"}, + {"12345678901", "iEyP_3BZR6vvv_2WM6NbZw"}, + {"123456789012", "4OPGvS4SZdjvS568APUaFw"}, + {"1234567890123", "Y8c5Wr8OhYYUo7fPwdojdg"}, + {"12345678901234", "tjQPabXW112wuVF8Vh46TA"}, + {"123456789012345", "c5Vh1kTd8WtIajmFEtz2dA"}, + {"1234567890123456", "tKa5gfvTzW4d-2bMtqYgdf5Rz-k2ZqViW6HfjbIZ6cE"}, + }, false) +} + +func TestEncryptSegmentBase32768(t *testing.T) { + testEncryptSegment(t, "base32768", []EncodingTestCase{ + {"", ""}, + {"1", "詮㪗鐮僀伎作㻖㢧⪟"}, + {"12", "竢朧䉱虃光塬䟛⣡蓟"}, + {"123", "遶㞟鋅缕袡鲅ⵝ蝁ꌟ"}, + {"1234", "䢟銮䵵狌㐜燳谒颴詟"}, + {"12345", "钉Ꞇ㖃蚩憶狫朰杜㜿"}, + {"123456", "啇ᚵⵕ憗䋫➫➓肤卟"}, + {"1234567", "茫螓翁連劘樓㶔抉矟"}, + {"12345678", "龝☳䘊辄岅較络㧩襟"}, + {"123456789", "ⲱ苀㱆犂媐Ꮤ锇惫靟"}, + {"1234567890", "計宁憕偵匢皫╛纺ꌟ"}, + {"12345678901", "檆䨿鑫㪺藝ꡖ勇䦛婟"}, + {"123456789012", "雑頏䰂䲝淚哚鹡魺⪟"}, + {"1234567890123", "塃璶繁躸圅㔟䗃肃懟"}, + {"12345678901234", "腺ᕚ崚鏕鏥讥鼌䑺䲿"}, + {"123456789012345", "怪绕滻蕶肣但⠥荖惟"}, + {"1234567890123456", "肳哀旚挶靏鏻㾭䱠慟㪳ꏆ賊兲铧敻塹魀ʟ"}, + }, false) +} + +func TestDecryptSegmentBase32(t *testing.T) { // We've tested the forwards above, now concentrate on the errors longName := make([]byte, 3328) for i := range longName { longName[i] = 'a' } - c, _ := newCipher(NameEncryptionStandard, "", "", true) + enc, _ := NewNameEncoding("base32") + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) for _, test := range []struct { in string expectedErr error @@ -145,118 +286,371 @@ func TestDecryptSegment(t *testing.T) { {"64=", ErrorBadBase32Encoding}, {"!", base32.CorruptInputError(0)}, {string(longName), ErrorTooLongAfterDecode}, - {encodeFileName([]byte("a")), ErrorNotAMultipleOfBlocksize}, - {encodeFileName([]byte("123456789abcdef")), ErrorNotAMultipleOfBlocksize}, - {encodeFileName([]byte("123456789abcdef0")), pkcs7.ErrorPaddingTooLong}, + {enc.EncodeToString([]byte("a")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef0")), pkcs7.ErrorPaddingTooLong}, } { actual, actualErr := c.decryptSegment(test.in) assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) } } -func TestEncryptFileName(t *testing.T) { +func TestDecryptSegmentBase64(t *testing.T) { + // We've tested the forwards above, now concentrate on the errors + longName := make([]byte, 2816) + for i := range longName { + longName[i] = 'a' + } + enc, _ := NewNameEncoding("base64") + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + for _, test := range []struct { + in string + expectedErr error + }{ + {"6H=", base64.CorruptInputError(2)}, + {"!", base64.CorruptInputError(0)}, + {string(longName), ErrorTooLongAfterDecode}, + {enc.EncodeToString([]byte("a")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef0")), pkcs7.ErrorPaddingTooLong}, + } { + actual, actualErr := c.decryptSegment(test.in) + assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) + } +} + +func TestDecryptSegmentBase32768(t *testing.T) { + // We've tested the forwards above, now concentrate on the errors + longName := strings.Repeat("怪", 1280) + enc, _ := NewNameEncoding("base32768") + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + for _, test := range []struct { + in string + expectedErr error + }{ + {"怪=", base32768.CorruptInputError(1)}, + {"!", base32768.CorruptInputError(0)}, + {longName, ErrorTooLongAfterDecode}, + {enc.EncodeToString([]byte("a")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef")), ErrorNotAMultipleOfBlocksize}, + {enc.EncodeToString([]byte("123456789abcdef0")), pkcs7.ErrorPaddingTooLong}, + } { + actual, actualErr := c.decryptSegment(test.in) + assert.Equal(t, test.expectedErr, actualErr, fmt.Sprintf("in=%q got actual=%q, err = %v %T", test.in, actual, actualErr, actualErr)) + } +} + +func testStandardEncryptFileName(t *testing.T, encoding string, testCasesEncryptDir []EncodingTestCase, testCasesNoEncryptDir []EncodingTestCase) { // First standard mode - c, _ := newCipher(NameEncryptionStandard, "", "", true) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptFileName("1")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptFileName("1/12")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptFileName("1/12/123")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s-v2001-02-03-040506-123", c.EncryptFileName("1-v2001-02-03-040506-123")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng-v2001-02-03-040506-123", c.EncryptFileName("1/12-v2001-02-03-040506-123")) + enc, _ := NewNameEncoding(encoding) + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + for _, test := range testCasesEncryptDir { + assert.Equal(t, test.expected, c.EncryptFileName(test.in)) + } // 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")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s-v2001-02-03-040506-123", c.EncryptFileName("1-v2001-02-03-040506-123")) - assert.Equal(t, "1/l42g6771hnv3an9cgc8cr2n1ng-v2001-02-03-040506-123", c.EncryptFileName("1/12-v2001-02-03-040506-123")) - // Now off mode - c, _ = newCipher(NameEncryptionOff, "", "", true) + c, _ = newCipher(NameEncryptionStandard, "", "", false, enc) + for _, test := range testCasesNoEncryptDir { + assert.Equal(t, test.expected, c.EncryptFileName(test.in)) + } +} + +func TestStandardEncryptFileNameBase32(t *testing.T) { + testStandardEncryptFileName(t, "base32", []EncodingTestCase{ + {"1", "p0e52nreeaj0a5ea7s64m4j72s"}, + {"1/12", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng"}, + {"1/12/123", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0"}, + {"1-v2001-02-03-040506-123", "p0e52nreeaj0a5ea7s64m4j72s-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng-v2001-02-03-040506-123"}, + }, []EncodingTestCase{ + {"1", "p0e52nreeaj0a5ea7s64m4j72s"}, + {"1/12", "1/l42g6771hnv3an9cgc8cr2n1ng"}, + {"1/12/123", "1/12/qgm4avr35m5loi1th53ato71v0"}, + {"1-v2001-02-03-040506-123", "p0e52nreeaj0a5ea7s64m4j72s-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "1/l42g6771hnv3an9cgc8cr2n1ng-v2001-02-03-040506-123"}, + }) +} + +func TestStandardEncryptFileNameBase64(t *testing.T) { + testStandardEncryptFileName(t, "base64", []EncodingTestCase{ + {"1", "yBxRX25ypgUVyj8MSxJnFw"}, + {"1/12", "yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA"}, + {"1/12/123", "yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA/1CxFf2Mti1xIPYlGruDh-A"}, + {"1-v2001-02-03-040506-123", "yBxRX25ypgUVyj8MSxJnFw-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA-v2001-02-03-040506-123"}, + }, []EncodingTestCase{ + {"1", "yBxRX25ypgUVyj8MSxJnFw"}, + {"1/12", "1/qQUDHOGN_jVdLIMQzYrhvA"}, + {"1/12/123", "1/12/1CxFf2Mti1xIPYlGruDh-A"}, + {"1-v2001-02-03-040506-123", "yBxRX25ypgUVyj8MSxJnFw-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "1/qQUDHOGN_jVdLIMQzYrhvA-v2001-02-03-040506-123"}, + }) +} + +func TestStandardEncryptFileNameBase32768(t *testing.T) { + testStandardEncryptFileName(t, "base32768", []EncodingTestCase{ + {"1", "詮㪗鐮僀伎作㻖㢧⪟"}, + {"1/12", "詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟"}, + {"1/12/123", "詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟/遶㞟鋅缕袡鲅ⵝ蝁ꌟ"}, + {"1-v2001-02-03-040506-123", "詮㪗鐮僀伎作㻖㢧⪟-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟-v2001-02-03-040506-123"}, + }, []EncodingTestCase{ + {"1", "詮㪗鐮僀伎作㻖㢧⪟"}, + {"1/12", "1/竢朧䉱虃光塬䟛⣡蓟"}, + {"1/12/123", "1/12/遶㞟鋅缕袡鲅ⵝ蝁ꌟ"}, + {"1-v2001-02-03-040506-123", "詮㪗鐮僀伎作㻖㢧⪟-v2001-02-03-040506-123"}, + {"1/12-v2001-02-03-040506-123", "1/竢朧䉱虃光塬䟛⣡蓟-v2001-02-03-040506-123"}, + }) +} + +func TestNonStandardEncryptFileName(t *testing.T) { + // Off mode + c, _ := newCipher(NameEncryptionOff, "", "", true, nil) assert.Equal(t, "1/12/123.bin", c.EncryptFileName("1/12/123")) // Obfuscation mode - c, _ = newCipher(NameEncryptionObfuscated, "", "", true) + c, _ = newCipher(NameEncryptionObfuscated, "", "", true, nil) 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-v2001-02-03-040506-123.!!lipps", c.EncryptFileName("1/12/123/!hello-v2001-02-03-040506-123")) assert.Equal(t, "49.6/99.23/150.890/162.uryyB-v2001-02-03-040506-123.GKG", c.EncryptFileName("1/12/123/hello-v2001-02-03-040506-123.txt")) assert.Equal(t, "161.\u00e4", c.EncryptFileName("\u00a1")) assert.Equal(t, "160.\u03c2", c.EncryptFileName("\u03a0")) // Obfuscation mode with directory name encryption off - c, _ = newCipher(NameEncryptionObfuscated, "", "", false) + c, _ = newCipher(NameEncryptionObfuscated, "", "", false, nil) assert.Equal(t, "1/12/123/53.!!lipps", c.EncryptFileName("1/12/123/!hello")) assert.Equal(t, "1/12/123/53-v2001-02-03-040506-123.!!lipps", c.EncryptFileName("1/12/123/!hello-v2001-02-03-040506-123")) assert.Equal(t, "161.\u00e4", c.EncryptFileName("\u00a1")) assert.Equal(t, "160.\u03c2", c.EncryptFileName("\u03a0")) } -func TestDecryptFileName(t *testing.T) { - for _, test := range []struct { - mode NameEncryptionMode - dirNameEncrypt bool - in string - expected string - expectedErr error - }{ - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil}, - {NameEncryptionStandard, true, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize}, - {NameEncryptionStandard, false, "1/12/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s-v2001-02-03-040506-123", "1-v2001-02-03-040506-123", nil}, - {NameEncryptionOff, true, "1/12/123.bin", "1/12/123", nil}, - {NameEncryptionOff, true, "1/12/123.bix", "", ErrorNotAnEncryptedFile}, - {NameEncryptionOff, true, ".bin", "", ErrorNotAnEncryptedFile}, - {NameEncryptionOff, true, "1/12/123-v2001-02-03-040506-123.bin", "1/12/123-v2001-02-03-040506-123", nil}, - {NameEncryptionOff, true, "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.bin", "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123", nil}, - {NameEncryptionOff, true, "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.txt.bin", "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.txt", nil}, - {NameEncryptionObfuscated, true, "!.hello", "hello", nil}, - {NameEncryptionObfuscated, true, "hello", "", ErrorNotAnEncryptedFile}, - {NameEncryptionObfuscated, true, "161.\u00e4", "\u00a1", nil}, - {NameEncryptionObfuscated, true, "160.\u03c2", "\u03a0", nil}, - {NameEncryptionObfuscated, false, "1/12/123/53.!!lipps", "1/12/123/!hello", nil}, - {NameEncryptionObfuscated, false, "1/12/123/53-v2001-02-03-040506-123.!!lipps", "1/12/123/!hello-v2001-02-03-040506-123", nil}, - } { - c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt) +func testStandardDecryptFileName(t *testing.T, encoding string, testCases []EncodingTestCase, caseInsensitive bool) { + enc, _ := NewNameEncoding(encoding) + for _, test := range testCases { + // Test when dirNameEncrypt=true + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) actual, actualErr := c.DecryptFileName(test.in) - what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) - assert.Equal(t, test.expected, actual, what) - assert.Equal(t, test.expectedErr, actualErr, what) + assert.NoError(t, actualErr) + assert.Equal(t, test.expected, actual) + if caseInsensitive { + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + actual, actualErr := c.DecryptFileName(strings.ToUpper(test.in)) + assert.NoError(t, actualErr) + assert.Equal(t, test.expected, actual) + } + // Add a character should raise ErrorNotAMultipleOfBlocksize + actual, actualErr = c.DecryptFileName(enc.EncodeToString([]byte("1")) + test.in) + assert.Equal(t, ErrorNotAMultipleOfBlocksize, actualErr) + assert.Equal(t, "", actual) + // Test when dirNameEncrypt=false + noDirEncryptIn := test.in + if strings.LastIndex(test.expected, "/") != -1 { + noDirEncryptIn = test.expected[:strings.LastIndex(test.expected, "/")] + test.in[strings.LastIndex(test.in, "/"):] + } + c, _ = newCipher(NameEncryptionStandard, "", "", false, enc) + actual, actualErr = c.DecryptFileName(noDirEncryptIn) + assert.NoError(t, actualErr) + assert.Equal(t, test.expected, actual) + } +} + +func TestStandardDecryptFileNameBase32(t *testing.T) { + testStandardDecryptFileName(t, "base32", []EncodingTestCase{ + {"p0e52nreeaj0a5ea7s64m4j72s", "1"}, + {"p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12"}, + {"p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123"}, + }, true) +} + +func TestStandardDecryptFileNameBase64(t *testing.T) { + testStandardDecryptFileName(t, "base64", []EncodingTestCase{ + {"yBxRX25ypgUVyj8MSxJnFw", "1"}, + {"yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA", "1/12"}, + {"yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA/1CxFf2Mti1xIPYlGruDh-A", "1/12/123"}, + }, false) +} + +func TestStandardDecryptFileNameBase32768(t *testing.T) { + testStandardDecryptFileName(t, "base32768", []EncodingTestCase{ + {"詮㪗鐮僀伎作㻖㢧⪟", "1"}, + {"詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟", "1/12"}, + {"詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟/遶㞟鋅缕袡鲅ⵝ蝁ꌟ", "1/12/123"}, + }, false) +} + +func TestNonStandardDecryptFileName(t *testing.T) { + for _, encoding := range []string{"base32", "base64", "base32768"} { + enc, _ := NewNameEncoding(encoding) + for _, test := range []struct { + mode NameEncryptionMode + dirNameEncrypt bool + in string + expected string + expectedErr error + }{ + {NameEncryptionOff, true, "1/12/123.bin", "1/12/123", nil}, + {NameEncryptionOff, true, "1/12/123.bix", "", ErrorNotAnEncryptedFile}, + {NameEncryptionOff, true, ".bin", "", ErrorNotAnEncryptedFile}, + {NameEncryptionOff, true, "1/12/123-v2001-02-03-040506-123.bin", "1/12/123-v2001-02-03-040506-123", nil}, + {NameEncryptionOff, true, "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.bin", "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123", nil}, + {NameEncryptionOff, true, "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.txt.bin", "1/12/123-v1970-01-01-010101-123-v2001-02-03-040506-123.txt", nil}, + {NameEncryptionObfuscated, true, "!.hello", "hello", nil}, + {NameEncryptionObfuscated, true, "hello", "", ErrorNotAnEncryptedFile}, + {NameEncryptionObfuscated, true, "161.\u00e4", "\u00a1", nil}, + {NameEncryptionObfuscated, true, "160.\u03c2", "\u03a0", nil}, + {NameEncryptionObfuscated, false, "1/12/123/53.!!lipps", "1/12/123/!hello", nil}, + {NameEncryptionObfuscated, false, "1/12/123/53-v2001-02-03-040506-123.!!lipps", "1/12/123/!hello-v2001-02-03-040506-123", nil}, + } { + c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt, enc) + actual, actualErr := c.DecryptFileName(test.in) + what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) + assert.Equal(t, test.expected, actual, what) + assert.Equal(t, test.expectedErr, actualErr, what) + } } } func TestEncDecMatches(t *testing.T) { - for _, test := range []struct { - mode NameEncryptionMode - in string - }{ - {NameEncryptionStandard, "1/2/3/4"}, - {NameEncryptionOff, "1/2/3/4"}, - {NameEncryptionObfuscated, "1/2/3/4/!hello\u03a0"}, - {NameEncryptionObfuscated, "Avatar The Last Airbender"}, - } { - c, _ := newCipher(test.mode, "", "", true) - out, err := c.DecryptFileName(c.EncryptFileName(test.in)) - what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) - assert.Equal(t, out, test.in, what) - assert.Equal(t, err, nil, what) + for _, encoding := range []string{"base32", "base64", "base32768"} { + enc, _ := NewNameEncoding(encoding) + for _, test := range []struct { + mode NameEncryptionMode + in string + }{ + {NameEncryptionStandard, "1/2/3/4"}, + {NameEncryptionOff, "1/2/3/4"}, + {NameEncryptionObfuscated, "1/2/3/4/!hello\u03a0"}, + {NameEncryptionObfuscated, "Avatar The Last Airbender"}, + } { + c, _ := newCipher(test.mode, "", "", true, enc) + out, err := c.DecryptFileName(c.EncryptFileName(test.in)) + what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) + assert.Equal(t, out, test.in, what) + assert.Equal(t, err, nil, what) + } } } -func TestEncryptDirName(t *testing.T) { +func testStandardEncryptDirName(t *testing.T, encoding string, testCases []EncodingTestCase) { + enc, _ := NewNameEncoding(encoding) + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) // First standard mode - c, _ := newCipher(NameEncryptionStandard, "", "", true) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptDirName("1")) - assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptDirName("1/12")) - 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 - c, _ = newCipher(NameEncryptionOff, "", "", true) - assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123")) + for _, test := range testCases { + assert.Equal(t, test.expected, c.EncryptDirName(test.in)) + } } -func TestDecryptDirName(t *testing.T) { +func TestStandardEncryptDirNameBase32(t *testing.T) { + testStandardEncryptDirName(t, "base32", []EncodingTestCase{ + {"1", "p0e52nreeaj0a5ea7s64m4j72s"}, + {"1/12", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng"}, + {"1/12/123", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0"}, + }) +} + +func TestStandardEncryptDirNameBase64(t *testing.T) { + testStandardEncryptDirName(t, "base64", []EncodingTestCase{ + {"1", "yBxRX25ypgUVyj8MSxJnFw"}, + {"1/12", "yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA"}, + {"1/12/123", "yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA/1CxFf2Mti1xIPYlGruDh-A"}, + }) +} + +func TestStandardEncryptDirNameBase32768(t *testing.T) { + testStandardEncryptDirName(t, "base32768", []EncodingTestCase{ + {"1", "詮㪗鐮僀伎作㻖㢧⪟"}, + {"1/12", "詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟"}, + {"1/12/123", "詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟/遶㞟鋅缕袡鲅ⵝ蝁ꌟ"}, + }) +} + +func TestNonStandardEncryptDirName(t *testing.T) { + for _, encoding := range []string{"base32", "base64", "base32768"} { + enc, _ := NewNameEncoding(encoding) + c, _ := newCipher(NameEncryptionStandard, "", "", false, enc) + assert.Equal(t, "1/12", c.EncryptDirName("1/12")) + assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123")) + // Now off mode + c, _ = newCipher(NameEncryptionOff, "", "", true, enc) + assert.Equal(t, "1/12/123", c.EncryptDirName("1/12/123")) + } +} + +func testStandardDecryptDirName(t *testing.T, encoding string, testCases []EncodingTestCase, caseInsensitive bool) { + enc, _ := NewNameEncoding(encoding) + for _, test := range testCases { + // Test dirNameEncrypt=true + c, _ := newCipher(NameEncryptionStandard, "", "", true, enc) + actual, actualErr := c.DecryptDirName(test.in) + assert.Equal(t, test.expected, actual) + assert.NoError(t, actualErr) + if caseInsensitive { + actual, actualErr := c.DecryptDirName(strings.ToUpper(test.in)) + assert.Equal(t, actual, test.expected) + assert.NoError(t, actualErr) + } + actual, actualErr = c.DecryptDirName(enc.EncodeToString([]byte("1")) + test.in) + assert.Equal(t, "", actual) + assert.Equal(t, ErrorNotAMultipleOfBlocksize, actualErr) + // Test dirNameEncrypt=false + c, _ = newCipher(NameEncryptionStandard, "", "", false, enc) + actual, actualErr = c.DecryptDirName(test.in) + assert.Equal(t, test.in, actual) + assert.NoError(t, actualErr) + actual, actualErr = c.DecryptDirName(test.expected) + assert.Equal(t, test.expected, actual) + assert.NoError(t, actualErr) + // Test dirNameEncrypt=false + } +} + +/* +enc, _ := NewNameEncoding(encoding) +for _, test := range []struct { + mode NameEncryptionMode + dirNameEncrypt bool + in string + expected string + expectedErr error +}{ + {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil}, + {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil}, + {NameEncryptionStandard, true, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil}, + {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, + {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize}, + {NameEncryptionStandard, false, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", nil}, + {NameEncryptionStandard, false, "1/12/123", "1/12/123", nil}, +} { + c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt, enc) + actual, actualErr := c.DecryptDirName(test.in) + what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) + assert.Equal(t, test.expected, actual, what) + assert.Equal(t, test.expectedErr, actualErr, what) +} +*/ + +func TestStandardDecryptDirNameBase32(t *testing.T) { + testStandardDecryptDirName(t, "base32", []EncodingTestCase{ + {"p0e52nreeaj0a5ea7s64m4j72s", "1"}, + {"p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12"}, + {"p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123"}, + }, true) +} + +func TestStandardDecryptDirNameBase64(t *testing.T) { + testStandardDecryptDirName(t, "base64", []EncodingTestCase{ + {"yBxRX25ypgUVyj8MSxJnFw", "1"}, + {"yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA", "1/12"}, + {"yBxRX25ypgUVyj8MSxJnFw/qQUDHOGN_jVdLIMQzYrhvA/1CxFf2Mti1xIPYlGruDh-A", "1/12/123"}, + }, false) +} + +func TestStandardDecryptDirNameBase32768(t *testing.T) { + testStandardDecryptDirName(t, "base32768", []EncodingTestCase{ + {"詮㪗鐮僀伎作㻖㢧⪟", "1"}, + {"詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟", "1/12"}, + {"詮㪗鐮僀伎作㻖㢧⪟/竢朧䉱虃光塬䟛⣡蓟/遶㞟鋅缕袡鲅ⵝ蝁ꌟ", "1/12/123"}, + }, false) +} + +func TestNonStandardDecryptDirName(t *testing.T) { for _, test := range []struct { mode NameEncryptionMode dirNameEncrypt bool @@ -264,18 +658,11 @@ func TestDecryptDirName(t *testing.T) { expected string expectedErr error }{ - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s", "1", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "1/12", nil}, - {NameEncryptionStandard, true, "p0e52nreeAJ0A5EA7S64M4J72S/L42G6771HNv3an9cgc8cr2n1ng", "1/12", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", "1/12/123", nil}, - {NameEncryptionStandard, true, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1/qgm4avr35m5loi1th53ato71v0", "", ErrorNotAMultipleOfBlocksize}, - {NameEncryptionStandard, false, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", nil}, - {NameEncryptionStandard, false, "1/12/123", "1/12/123", 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, "", "", test.dirNameEncrypt) + c, _ := newCipher(test.mode, "", "", test.dirNameEncrypt, nil) actual, actualErr := c.DecryptDirName(test.in) what := fmt.Sprintf("Testing %q (mode=%v)", test.in, test.mode) assert.Equal(t, test.expected, actual, what) @@ -284,7 +671,7 @@ func TestDecryptDirName(t *testing.T) { } func TestEncryptedSize(t *testing.T) { - c, _ := newCipher(NameEncryptionStandard, "", "", true) + c, _ := newCipher(NameEncryptionStandard, "", "", true, nil) for _, test := range []struct { in int64 expected int64 @@ -308,7 +695,7 @@ func TestEncryptedSize(t *testing.T) { func TestDecryptedSize(t *testing.T) { // Test the errors since we tested the reverse above - c, _ := newCipher(NameEncryptionStandard, "", "", true) + c, _ := newCipher(NameEncryptionStandard, "", "", true, nil) for _, test := range []struct { in int64 expectedErr error @@ -679,7 +1066,7 @@ func (z *zeroes) Read(p []byte) (n int, err error) { // Test encrypt decrypt with different buffer sizes func testEncryptDecrypt(t *testing.T, bufSize int, copySize int64) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) c.cryptoRand = &zeroes{} // zero out the nonce buf := make([]byte, bufSize) @@ -749,7 +1136,7 @@ func TestEncryptData(t *testing.T) { {[]byte{1}, file1}, {[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, file16}, } { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) c.cryptoRand = newRandomSource(1e8) // nodge the crypto rand generator @@ -772,7 +1159,7 @@ func TestEncryptData(t *testing.T) { } func TestNewEncrypter(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) c.cryptoRand = newRandomSource(1e8) // nodge the crypto rand generator @@ -788,13 +1175,12 @@ func TestNewEncrypter(t *testing.T) { fh, err = c.newEncrypter(z, nil) assert.Nil(t, fh) assert.Error(t, err, "short read of nonce") - } // Test the stream returning 0, io.ErrUnexpectedEOF - this used to // cause a fatal loop func TestNewEncrypterErrUnexpectedEOF(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) in := &readers.ErrorReader{Err: io.ErrUnexpectedEOF} @@ -823,7 +1209,7 @@ func (c *closeDetector) Close() error { } func TestNewDecrypter(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) c.cryptoRand = newRandomSource(1e8) // nodge the crypto rand generator @@ -866,7 +1252,7 @@ func TestNewDecrypter(t *testing.T) { // Test the stream returning 0, io.ErrUnexpectedEOF func TestNewDecrypterErrUnexpectedEOF(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) in2 := &readers.ErrorReader{Err: io.ErrUnexpectedEOF} @@ -882,7 +1268,7 @@ func TestNewDecrypterErrUnexpectedEOF(t *testing.T) { } func TestNewDecrypterSeekLimit(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) c.cryptoRand = &zeroes{} // nodge the crypto rand generator @@ -1088,7 +1474,7 @@ func TestDecrypterCalculateUnderlying(t *testing.T) { } func TestDecrypterRead(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) // Test truncating the file at each possible point @@ -1152,7 +1538,7 @@ func TestDecrypterRead(t *testing.T) { } func TestDecrypterClose(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) cd := newCloseDetector(bytes.NewBuffer(file16)) @@ -1190,7 +1576,7 @@ func TestDecrypterClose(t *testing.T) { } func TestPutGetBlock(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) block := c.getBlock() @@ -1201,7 +1587,7 @@ func TestPutGetBlock(t *testing.T) { } func TestKey(t *testing.T) { - c, err := newCipher(NameEncryptionStandard, "", "", true) + c, err := newCipher(NameEncryptionStandard, "", "", true, nil) assert.NoError(t, err) // Check zero keys OK diff --git a/backend/crypt/crypt_test.go b/backend/crypt/crypt_test.go index 24eaa2722..8369a60f7 100644 --- a/backend/crypt/crypt_test.go +++ b/backend/crypt/crypt_test.go @@ -29,7 +29,7 @@ func TestIntegration(t *testing.T) { } // TestStandard runs integration tests against the remote -func TestStandard(t *testing.T) { +func TestStandardBase32(t *testing.T) { if *fstest.RemoteName != "" { t.Skip("Skipping as -remote set") } @@ -49,6 +49,48 @@ func TestStandard(t *testing.T) { }) } +func TestStandardBase64(t *testing.T) { + if *fstest.RemoteName != "" { + t.Skip("Skipping as -remote set") + } + tempdir := filepath.Join(os.TempDir(), "rclone-crypt-test-standard") + name := "TestCrypt" + fstests.Run(t, &fstests.Opt{ + RemoteName: name + ":", + NilObject: (*crypt.Object)(nil), + ExtraConfig: []fstests.ExtraConfigItem{ + {Name: name, Key: "type", Value: "crypt"}, + {Name: name, Key: "remote", Value: tempdir}, + {Name: name, Key: "password", Value: obscure.MustObscure("potato")}, + {Name: name, Key: "filename_encryption", Value: "standard"}, + {Name: name, Key: "filename_encoding", Value: "base64"}, + }, + UnimplementableFsMethods: []string{"OpenWriterAt"}, + UnimplementableObjectMethods: []string{"MimeType"}, + }) +} + +func TestStandardBase32768(t *testing.T) { + if *fstest.RemoteName != "" { + t.Skip("Skipping as -remote set") + } + tempdir := filepath.Join(os.TempDir(), "rclone-crypt-test-standard") + name := "TestCrypt" + fstests.Run(t, &fstests.Opt{ + RemoteName: name + ":", + NilObject: (*crypt.Object)(nil), + ExtraConfig: []fstests.ExtraConfigItem{ + {Name: name, Key: "type", Value: "crypt"}, + {Name: name, Key: "remote", Value: tempdir}, + {Name: name, Key: "password", Value: obscure.MustObscure("potato")}, + {Name: name, Key: "filename_encryption", Value: "standard"}, + {Name: name, Key: "filename_encoding", Value: "base32768"}, + }, + UnimplementableFsMethods: []string{"OpenWriterAt"}, + UnimplementableObjectMethods: []string{"MimeType"}, + }) +} + // TestOff runs integration tests against the remote func TestOff(t *testing.T) { if *fstest.RemoteName != "" { diff --git a/docs/content/crypt.md b/docs/content/crypt.md index 0d22100b7..dc6b80fe5 100644 --- a/docs/content/crypt.md +++ b/docs/content/crypt.md @@ -373,6 +373,14 @@ total path length which rclone is more likely to breach using characters in length issues should not be encountered, irrespective of cloud storage provider. +An experimental advanced option `filename_encoding` is now provided to +address this problem to a certain degree. +For cloud storage systems with case sensitive file names (e.g. Google Drive), +`base64` can be used to reduce file name length. +For cloud storage systems using UTF-16 to store file names internally +(e.g. OneDrive), `base32768` can be used to drastically reduce +file name length. + An alternative, future rclone file name encryption mode may tolerate backend provider path length limits.