encryption_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package protocol
  7. import (
  8. "bytes"
  9. "fmt"
  10. "reflect"
  11. "regexp"
  12. "strings"
  13. "sync"
  14. "testing"
  15. "github.com/syncthing/syncthing/lib/rand"
  16. "github.com/syncthing/syncthing/lib/sha256"
  17. )
  18. func TestEnDecryptName(t *testing.T) {
  19. pattern := regexp.MustCompile(
  20. fmt.Sprintf("^[0-9A-V]%s/[0-9A-V]{2}/([0-9A-V]{%d}/)*[0-9A-V]{1,%d}$",
  21. regexp.QuoteMeta(encryptedDirExtension),
  22. maxPathComponent, maxPathComponent-1))
  23. makeName := func(n int) string {
  24. b := make([]byte, n)
  25. for i := range b {
  26. b[i] = byte('a' + i%26)
  27. }
  28. return string(b)
  29. }
  30. var key [32]byte
  31. cases := []string{
  32. "",
  33. "foo",
  34. "a longer name/with/slashes and spaces",
  35. makeName(maxPathComponent),
  36. makeName(1 + maxPathComponent),
  37. makeName(2 * maxPathComponent),
  38. makeName(1 + 2*maxPathComponent),
  39. }
  40. for _, tc := range cases {
  41. var prev string
  42. for i := 0; i < 5; i++ {
  43. enc := encryptName(tc, &key)
  44. if prev != "" && prev != enc {
  45. t.Error("name should always encrypt the same")
  46. }
  47. prev = enc
  48. if tc != "" && strings.Contains(enc, tc) {
  49. t.Error("shouldn't contain plaintext")
  50. }
  51. if !pattern.MatchString(enc) {
  52. t.Fatalf("encrypted name %s doesn't match %s",
  53. enc, pattern)
  54. }
  55. dec, err := decryptName(enc, &key)
  56. if err != nil {
  57. t.Error(err)
  58. }
  59. if dec != tc {
  60. t.Error("mismatch after decryption")
  61. }
  62. t.Logf("%q encrypts as %q", tc, enc)
  63. }
  64. }
  65. }
  66. func TestDecryptNameInvalid(t *testing.T) {
  67. key := new([32]byte)
  68. for _, c := range []string{
  69. "T.syncthing-enc/OD",
  70. "T.syncthing-enc/OD/",
  71. "T.wrong-extension/OD/PHVDD67S7FI2K5QQMPSOFSK",
  72. "OD/PHVDD67S7FI2K5QQMPSOFSK",
  73. } {
  74. if _, err := decryptName(c, key); err == nil {
  75. t.Errorf("no error for %q", c)
  76. }
  77. }
  78. }
  79. func TestEnDecryptBytes(t *testing.T) {
  80. var key [32]byte
  81. cases := [][]byte{
  82. {},
  83. {1, 2, 3, 4, 5},
  84. }
  85. for _, tc := range cases {
  86. var prev []byte
  87. for i := 0; i < 5; i++ {
  88. enc := encryptBytes(tc, &key)
  89. if bytes.Equal(enc, prev) {
  90. t.Error("encryption should not repeat")
  91. }
  92. prev = enc
  93. if len(tc) > 0 && bytes.Contains(enc, tc) {
  94. t.Error("shouldn't contain plaintext")
  95. }
  96. dec, err := DecryptBytes(enc, &key)
  97. if err != nil {
  98. t.Error(err)
  99. }
  100. if !bytes.Equal(dec, tc) {
  101. t.Error("mismatch after decryption")
  102. }
  103. }
  104. }
  105. }
  106. func encFileInfo() FileInfo {
  107. return FileInfo{
  108. Name: "hello",
  109. Size: 45,
  110. Permissions: 0755,
  111. ModifiedS: 8080,
  112. Blocks: []BlockInfo{
  113. {
  114. Offset: 0,
  115. Size: 45,
  116. Hash: []byte{1, 2, 3},
  117. },
  118. {
  119. Offset: 45,
  120. Size: 45,
  121. Hash: []byte{1, 2, 3},
  122. },
  123. },
  124. }
  125. }
  126. func TestEnDecryptFileInfo(t *testing.T) {
  127. var key [32]byte
  128. fi := encFileInfo()
  129. enc := encryptFileInfo(fi, &key)
  130. if bytes.Equal(enc.Blocks[0].Hash, enc.Blocks[1].Hash) {
  131. t.Error("block hashes should not repeat when on different offsets")
  132. }
  133. if enc.RawBlockSize < MinBlockSize {
  134. t.Error("Too small raw block size:", enc.RawBlockSize)
  135. }
  136. again := encryptFileInfo(fi, &key)
  137. if !bytes.Equal(enc.Blocks[0].Hash, again.Blocks[0].Hash) {
  138. t.Error("block hashes should remain stable (0)")
  139. }
  140. if !bytes.Equal(enc.Blocks[1].Hash, again.Blocks[1].Hash) {
  141. t.Error("block hashes should remain stable (1)")
  142. }
  143. dec, err := DecryptFileInfo(enc, &key)
  144. if err != nil {
  145. t.Error(err)
  146. }
  147. if !reflect.DeepEqual(fi, dec) {
  148. t.Error("mismatch after decryption")
  149. }
  150. }
  151. func TestEncryptedFileInfoConsistency(t *testing.T) {
  152. var key [32]byte
  153. files := []FileInfo{
  154. encFileInfo(),
  155. encFileInfo(),
  156. }
  157. files[1].SetIgnored()
  158. for i, f := range files {
  159. enc := encryptFileInfo(f, &key)
  160. if err := checkFileInfoConsistency(enc); err != nil {
  161. t.Errorf("%v: %v", i, err)
  162. }
  163. }
  164. }
  165. func TestIsEncryptedParent(t *testing.T) {
  166. comp := rand.String(maxPathComponent)
  167. cases := []struct {
  168. path string
  169. is bool
  170. }{
  171. {"", false},
  172. {".", false},
  173. {"/", false},
  174. {"12" + encryptedDirExtension, false},
  175. {"1" + encryptedDirExtension, true},
  176. {"1" + encryptedDirExtension + "/b", false},
  177. {"1" + encryptedDirExtension + "/bc", true},
  178. {"1" + encryptedDirExtension + "/bcd", false},
  179. {"1" + encryptedDirExtension + "/bc/foo", false},
  180. {"1.12/22", false},
  181. {"1" + encryptedDirExtension + "/bc/" + comp, true},
  182. {"1" + encryptedDirExtension + "/bc/" + comp + "/" + comp, true},
  183. {"1" + encryptedDirExtension + "/bc/" + comp + "a", false},
  184. {"1" + encryptedDirExtension + "/bc/" + comp + "/a/" + comp, false},
  185. }
  186. for _, tc := range cases {
  187. if res := IsEncryptedParent(strings.Split(tc.path, "/")); res != tc.is {
  188. t.Errorf("%v: got %v, expected %v", tc.path, res, tc.is)
  189. }
  190. }
  191. }
  192. var benchmarkFileKey struct {
  193. key [keySize]byte
  194. sync.Once
  195. }
  196. func BenchmarkFileKey(b *testing.B) {
  197. benchmarkFileKey.Do(func() {
  198. sha256.SelectAlgo()
  199. rand.Read(benchmarkFileKey.key[:])
  200. })
  201. b.ResetTimer()
  202. b.ReportAllocs()
  203. for i := 0; i < b.N; i++ {
  204. FileKey("a_kind_of_long_filename.ext", &benchmarkFileKey.key)
  205. }
  206. }