encryption_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. "testing"
  14. "github.com/syncthing/syncthing/lib/rand"
  15. )
  16. var testKeyGen = NewKeyGenerator()
  17. func TestEnDecryptName(t *testing.T) {
  18. pattern := regexp.MustCompile(
  19. fmt.Sprintf("^[0-9A-V]%s/[0-9A-V]{2}/([0-9A-V]{%d}/)*[0-9A-V]{1,%d}$",
  20. regexp.QuoteMeta(encryptedDirExtension),
  21. maxPathComponent, maxPathComponent-1))
  22. makeName := func(n int) string {
  23. b := make([]byte, n)
  24. for i := range b {
  25. b[i] = byte('a' + i%26)
  26. }
  27. return string(b)
  28. }
  29. var key [32]byte
  30. cases := []string{
  31. "",
  32. "foo",
  33. "a longer name/with/slashes and spaces",
  34. makeName(maxPathComponent),
  35. makeName(1 + maxPathComponent),
  36. makeName(2 * maxPathComponent),
  37. makeName(1 + 2*maxPathComponent),
  38. }
  39. for _, tc := range cases {
  40. var prev string
  41. for i := 0; i < 5; i++ {
  42. enc := encryptName(tc, &key)
  43. if prev != "" && prev != enc {
  44. t.Error("name should always encrypt the same")
  45. }
  46. prev = enc
  47. if tc != "" && strings.Contains(enc, tc) {
  48. t.Error("shouldn't contain plaintext")
  49. }
  50. if !pattern.MatchString(enc) {
  51. t.Fatalf("encrypted name %s doesn't match %s",
  52. enc, pattern)
  53. }
  54. dec, err := decryptName(enc, &key)
  55. if err != nil {
  56. t.Error(err)
  57. }
  58. if dec != tc {
  59. t.Error("mismatch after decryption")
  60. }
  61. t.Logf("%q encrypts as %q", tc, enc)
  62. }
  63. }
  64. }
  65. func TestKeyDerivation(t *testing.T) {
  66. folderKey := testKeyGen.KeyFromPassword("my folder", "my password")
  67. encryptedName := encryptDeterministic([]byte("filename.txt"), folderKey, nil)
  68. if base32Hex.EncodeToString(encryptedName) != "3T5957I4IOA20VEIEER6JSQG0PEPIRV862II3K7LOF75Q" {
  69. t.Error("encrypted name mismatch")
  70. }
  71. fileKey := testKeyGen.FileKey("filename.txt", folderKey)
  72. // fmt.Println(base32Hex.EncodeToString(encryptBytes([]byte("hello world"), fileKey))) => A1IPD...
  73. const encrypted = `A1IPD28ISL7VNPRSSSQM2L31L3IJPC08283RO89J5UG0TI9P38DO9RFGK12DK0KD7PKQP6U51UL2B6H96O`
  74. bs, _ := base32Hex.DecodeString(encrypted)
  75. dec, err := DecryptBytes(bs, fileKey)
  76. if err != nil {
  77. t.Error(err)
  78. }
  79. if string(dec) != "hello world" {
  80. t.Error("decryption mismatch")
  81. }
  82. }
  83. func TestDecryptNameInvalid(t *testing.T) {
  84. key := new([32]byte)
  85. for _, c := range []string{
  86. "T.syncthing-enc/OD",
  87. "T.syncthing-enc/OD/",
  88. "T.wrong-extension/OD/PHVDD67S7FI2K5QQMPSOFSK",
  89. "OD/PHVDD67S7FI2K5QQMPSOFSK",
  90. } {
  91. if _, err := decryptName(c, key); err == nil {
  92. t.Errorf("no error for %q", c)
  93. }
  94. }
  95. }
  96. func TestEnDecryptBytes(t *testing.T) {
  97. var key [32]byte
  98. cases := [][]byte{
  99. {},
  100. {1, 2, 3, 4, 5},
  101. }
  102. for _, tc := range cases {
  103. var prev []byte
  104. for i := 0; i < 5; i++ {
  105. enc := encryptBytes(tc, &key)
  106. if bytes.Equal(enc, prev) {
  107. t.Error("encryption should not repeat")
  108. }
  109. prev = enc
  110. if len(tc) > 0 && bytes.Contains(enc, tc) {
  111. t.Error("shouldn't contain plaintext")
  112. }
  113. dec, err := DecryptBytes(enc, &key)
  114. if err != nil {
  115. t.Error(err)
  116. }
  117. if !bytes.Equal(dec, tc) {
  118. t.Error("mismatch after decryption")
  119. }
  120. }
  121. }
  122. }
  123. func encFileInfo() FileInfo {
  124. return FileInfo{
  125. Name: "hello",
  126. Size: 45,
  127. Permissions: 0o755,
  128. ModifiedS: 8080,
  129. Sequence: 1000,
  130. Blocks: []BlockInfo{
  131. {
  132. Offset: 0,
  133. Size: 45,
  134. Hash: []byte{1, 2, 3},
  135. },
  136. {
  137. Offset: 45,
  138. Size: 45,
  139. Hash: []byte{1, 2, 3},
  140. },
  141. },
  142. }
  143. }
  144. func TestEnDecryptFileInfo(t *testing.T) {
  145. var key [32]byte
  146. fi := encFileInfo()
  147. enc := encryptFileInfo(testKeyGen, fi, &key)
  148. if bytes.Equal(enc.Blocks[0].Hash, enc.Blocks[1].Hash) {
  149. t.Error("block hashes should not repeat when on different offsets")
  150. }
  151. if enc.RawBlockSize < MinBlockSize {
  152. t.Error("Too small raw block size:", enc.RawBlockSize)
  153. }
  154. if enc.Sequence != fi.Sequence {
  155. t.Error("encrypted fileinfo didn't maintain sequence number")
  156. }
  157. again := encryptFileInfo(testKeyGen, fi, &key)
  158. if !bytes.Equal(enc.Blocks[0].Hash, again.Blocks[0].Hash) {
  159. t.Error("block hashes should remain stable (0)")
  160. }
  161. if !bytes.Equal(enc.Blocks[1].Hash, again.Blocks[1].Hash) {
  162. t.Error("block hashes should remain stable (1)")
  163. }
  164. // Simulate the remote setting the sequence number when writing to db
  165. enc.Sequence = 10
  166. dec, err := DecryptFileInfo(testKeyGen, enc, &key)
  167. if err != nil {
  168. t.Error(err)
  169. }
  170. if dec.Sequence != enc.Sequence {
  171. t.Error("decrypted fileinfo didn't maintain sequence number")
  172. }
  173. dec.Sequence = fi.Sequence
  174. if !reflect.DeepEqual(fi, dec) {
  175. t.Error("mismatch after decryption")
  176. }
  177. }
  178. func TestEncryptedFileInfoConsistency(t *testing.T) {
  179. var key [32]byte
  180. files := []FileInfo{
  181. encFileInfo(),
  182. encFileInfo(),
  183. }
  184. files[1].SetIgnored()
  185. for i, f := range files {
  186. enc := encryptFileInfo(testKeyGen, f, &key)
  187. if err := checkFileInfoConsistency(enc); err != nil {
  188. t.Errorf("%v: %v", i, err)
  189. }
  190. }
  191. }
  192. func TestIsEncryptedParent(t *testing.T) {
  193. comp := rand.String(maxPathComponent)
  194. cases := []struct {
  195. path string
  196. is bool
  197. }{
  198. {"", false},
  199. {".", false},
  200. {"/", false},
  201. {"12" + encryptedDirExtension, false},
  202. {"1" + encryptedDirExtension, true},
  203. {"1" + encryptedDirExtension + "/b", false},
  204. {"1" + encryptedDirExtension + "/bc", true},
  205. {"1" + encryptedDirExtension + "/bcd", false},
  206. {"1" + encryptedDirExtension + "/bc/foo", false},
  207. {"1.12/22", false},
  208. {"1" + encryptedDirExtension + "/bc/" + comp, true},
  209. {"1" + encryptedDirExtension + "/bc/" + comp + "/" + comp, true},
  210. {"1" + encryptedDirExtension + "/bc/" + comp + "a", false},
  211. {"1" + encryptedDirExtension + "/bc/" + comp + "/a/" + comp, false},
  212. }
  213. for _, tc := range cases {
  214. if res := IsEncryptedParent(strings.Split(tc.path, "/")); res != tc.is {
  215. t.Errorf("%v: got %v, expected %v", tc.path, res, tc.is)
  216. }
  217. }
  218. }