test_e2e_room_keys.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 OpenMarket Ltd
  3. # Copyright 2017 New Vector Ltd
  4. # Copyright 2019 Matrix.org Foundation C.I.C.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import copy
  18. import mock
  19. from twisted.internet import defer
  20. import synapse.api.errors
  21. import synapse.handlers.e2e_room_keys
  22. import synapse.storage
  23. from synapse.api import errors
  24. from tests import unittest, utils
  25. # sample room_key data for use in the tests
  26. room_keys = {
  27. "rooms": {
  28. "!abc:matrix.org": {
  29. "sessions": {
  30. "c0ff33": {
  31. "first_message_index": 1,
  32. "forwarded_count": 1,
  33. "is_verified": False,
  34. "session_data": "SSBBTSBBIEZJU0gK",
  35. }
  36. }
  37. }
  38. }
  39. }
  40. class E2eRoomKeysHandlerTestCase(unittest.TestCase):
  41. def __init__(self, *args, **kwargs):
  42. super().__init__(*args, **kwargs)
  43. self.hs = None # type: synapse.server.HomeServer
  44. self.handler = None # type: synapse.handlers.e2e_keys.E2eRoomKeysHandler
  45. @defer.inlineCallbacks
  46. def setUp(self):
  47. self.hs = yield utils.setup_test_homeserver(
  48. self.addCleanup, replication_layer=mock.Mock()
  49. )
  50. self.handler = synapse.handlers.e2e_room_keys.E2eRoomKeysHandler(self.hs)
  51. self.local_user = "@boris:" + self.hs.hostname
  52. @defer.inlineCallbacks
  53. def test_get_missing_current_version_info(self):
  54. """Check that we get a 404 if we ask for info about the current version
  55. if there is no version.
  56. """
  57. res = None
  58. try:
  59. yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  60. except errors.SynapseError as e:
  61. res = e.code
  62. self.assertEqual(res, 404)
  63. @defer.inlineCallbacks
  64. def test_get_missing_version_info(self):
  65. """Check that we get a 404 if we ask for info about a specific version
  66. if it doesn't exist.
  67. """
  68. res = None
  69. try:
  70. yield defer.ensureDeferred(
  71. self.handler.get_version_info(self.local_user, "bogus_version")
  72. )
  73. except errors.SynapseError as e:
  74. res = e.code
  75. self.assertEqual(res, 404)
  76. @defer.inlineCallbacks
  77. def test_create_version(self):
  78. """Check that we can create and then retrieve versions.
  79. """
  80. res = yield defer.ensureDeferred(
  81. self.handler.create_version(
  82. self.local_user,
  83. {
  84. "algorithm": "m.megolm_backup.v1",
  85. "auth_data": "first_version_auth_data",
  86. },
  87. )
  88. )
  89. self.assertEqual(res, "1")
  90. # check we can retrieve it as the current version
  91. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  92. version_etag = res["etag"]
  93. self.assertIsInstance(version_etag, str)
  94. del res["etag"]
  95. self.assertDictEqual(
  96. res,
  97. {
  98. "version": "1",
  99. "algorithm": "m.megolm_backup.v1",
  100. "auth_data": "first_version_auth_data",
  101. "count": 0,
  102. },
  103. )
  104. # check we can retrieve it as a specific version
  105. res = yield defer.ensureDeferred(
  106. self.handler.get_version_info(self.local_user, "1")
  107. )
  108. self.assertEqual(res["etag"], version_etag)
  109. del res["etag"]
  110. self.assertDictEqual(
  111. res,
  112. {
  113. "version": "1",
  114. "algorithm": "m.megolm_backup.v1",
  115. "auth_data": "first_version_auth_data",
  116. "count": 0,
  117. },
  118. )
  119. # upload a new one...
  120. res = yield defer.ensureDeferred(
  121. self.handler.create_version(
  122. self.local_user,
  123. {
  124. "algorithm": "m.megolm_backup.v1",
  125. "auth_data": "second_version_auth_data",
  126. },
  127. )
  128. )
  129. self.assertEqual(res, "2")
  130. # check we can retrieve it as the current version
  131. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  132. del res["etag"]
  133. self.assertDictEqual(
  134. res,
  135. {
  136. "version": "2",
  137. "algorithm": "m.megolm_backup.v1",
  138. "auth_data": "second_version_auth_data",
  139. "count": 0,
  140. },
  141. )
  142. @defer.inlineCallbacks
  143. def test_update_version(self):
  144. """Check that we can update versions.
  145. """
  146. version = yield defer.ensureDeferred(
  147. self.handler.create_version(
  148. self.local_user,
  149. {
  150. "algorithm": "m.megolm_backup.v1",
  151. "auth_data": "first_version_auth_data",
  152. },
  153. )
  154. )
  155. self.assertEqual(version, "1")
  156. res = yield defer.ensureDeferred(
  157. self.handler.update_version(
  158. self.local_user,
  159. version,
  160. {
  161. "algorithm": "m.megolm_backup.v1",
  162. "auth_data": "revised_first_version_auth_data",
  163. "version": version,
  164. },
  165. )
  166. )
  167. self.assertDictEqual(res, {})
  168. # check we can retrieve it as the current version
  169. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  170. del res["etag"]
  171. self.assertDictEqual(
  172. res,
  173. {
  174. "algorithm": "m.megolm_backup.v1",
  175. "auth_data": "revised_first_version_auth_data",
  176. "version": version,
  177. "count": 0,
  178. },
  179. )
  180. @defer.inlineCallbacks
  181. def test_update_missing_version(self):
  182. """Check that we get a 404 on updating nonexistent versions
  183. """
  184. res = None
  185. try:
  186. yield defer.ensureDeferred(
  187. self.handler.update_version(
  188. self.local_user,
  189. "1",
  190. {
  191. "algorithm": "m.megolm_backup.v1",
  192. "auth_data": "revised_first_version_auth_data",
  193. "version": "1",
  194. },
  195. )
  196. )
  197. except errors.SynapseError as e:
  198. res = e.code
  199. self.assertEqual(res, 404)
  200. @defer.inlineCallbacks
  201. def test_update_omitted_version(self):
  202. """Check that the update succeeds if the version is missing from the body
  203. """
  204. version = yield defer.ensureDeferred(
  205. self.handler.create_version(
  206. self.local_user,
  207. {
  208. "algorithm": "m.megolm_backup.v1",
  209. "auth_data": "first_version_auth_data",
  210. },
  211. )
  212. )
  213. self.assertEqual(version, "1")
  214. yield defer.ensureDeferred(
  215. self.handler.update_version(
  216. self.local_user,
  217. version,
  218. {
  219. "algorithm": "m.megolm_backup.v1",
  220. "auth_data": "revised_first_version_auth_data",
  221. },
  222. )
  223. )
  224. # check we can retrieve it as the current version
  225. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  226. del res["etag"] # etag is opaque, so don't test its contents
  227. self.assertDictEqual(
  228. res,
  229. {
  230. "algorithm": "m.megolm_backup.v1",
  231. "auth_data": "revised_first_version_auth_data",
  232. "version": version,
  233. "count": 0,
  234. },
  235. )
  236. @defer.inlineCallbacks
  237. def test_update_bad_version(self):
  238. """Check that we get a 400 if the version in the body doesn't match
  239. """
  240. version = yield defer.ensureDeferred(
  241. self.handler.create_version(
  242. self.local_user,
  243. {
  244. "algorithm": "m.megolm_backup.v1",
  245. "auth_data": "first_version_auth_data",
  246. },
  247. )
  248. )
  249. self.assertEqual(version, "1")
  250. res = None
  251. try:
  252. yield defer.ensureDeferred(
  253. self.handler.update_version(
  254. self.local_user,
  255. version,
  256. {
  257. "algorithm": "m.megolm_backup.v1",
  258. "auth_data": "revised_first_version_auth_data",
  259. "version": "incorrect",
  260. },
  261. )
  262. )
  263. except errors.SynapseError as e:
  264. res = e.code
  265. self.assertEqual(res, 400)
  266. @defer.inlineCallbacks
  267. def test_delete_missing_version(self):
  268. """Check that we get a 404 on deleting nonexistent versions
  269. """
  270. res = None
  271. try:
  272. yield defer.ensureDeferred(
  273. self.handler.delete_version(self.local_user, "1")
  274. )
  275. except errors.SynapseError as e:
  276. res = e.code
  277. self.assertEqual(res, 404)
  278. @defer.inlineCallbacks
  279. def test_delete_missing_current_version(self):
  280. """Check that we get a 404 on deleting nonexistent current version
  281. """
  282. res = None
  283. try:
  284. yield defer.ensureDeferred(self.handler.delete_version(self.local_user))
  285. except errors.SynapseError as e:
  286. res = e.code
  287. self.assertEqual(res, 404)
  288. @defer.inlineCallbacks
  289. def test_delete_version(self):
  290. """Check that we can create and then delete versions.
  291. """
  292. res = yield defer.ensureDeferred(
  293. self.handler.create_version(
  294. self.local_user,
  295. {
  296. "algorithm": "m.megolm_backup.v1",
  297. "auth_data": "first_version_auth_data",
  298. },
  299. )
  300. )
  301. self.assertEqual(res, "1")
  302. # check we can delete it
  303. yield defer.ensureDeferred(self.handler.delete_version(self.local_user, "1"))
  304. # check that it's gone
  305. res = None
  306. try:
  307. yield defer.ensureDeferred(
  308. self.handler.get_version_info(self.local_user, "1")
  309. )
  310. except errors.SynapseError as e:
  311. res = e.code
  312. self.assertEqual(res, 404)
  313. @defer.inlineCallbacks
  314. def test_get_missing_backup(self):
  315. """Check that we get a 404 on querying missing backup
  316. """
  317. res = None
  318. try:
  319. yield defer.ensureDeferred(
  320. self.handler.get_room_keys(self.local_user, "bogus_version")
  321. )
  322. except errors.SynapseError as e:
  323. res = e.code
  324. self.assertEqual(res, 404)
  325. @defer.inlineCallbacks
  326. def test_get_missing_room_keys(self):
  327. """Check we get an empty response from an empty backup
  328. """
  329. version = yield defer.ensureDeferred(
  330. self.handler.create_version(
  331. self.local_user,
  332. {
  333. "algorithm": "m.megolm_backup.v1",
  334. "auth_data": "first_version_auth_data",
  335. },
  336. )
  337. )
  338. self.assertEqual(version, "1")
  339. res = yield defer.ensureDeferred(
  340. self.handler.get_room_keys(self.local_user, version)
  341. )
  342. self.assertDictEqual(res, {"rooms": {}})
  343. # TODO: test the locking semantics when uploading room_keys,
  344. # although this is probably best done in sytest
  345. @defer.inlineCallbacks
  346. def test_upload_room_keys_no_versions(self):
  347. """Check that we get a 404 on uploading keys when no versions are defined
  348. """
  349. res = None
  350. try:
  351. yield defer.ensureDeferred(
  352. self.handler.upload_room_keys(self.local_user, "no_version", room_keys)
  353. )
  354. except errors.SynapseError as e:
  355. res = e.code
  356. self.assertEqual(res, 404)
  357. @defer.inlineCallbacks
  358. def test_upload_room_keys_bogus_version(self):
  359. """Check that we get a 404 on uploading keys when an nonexistent version
  360. is specified
  361. """
  362. version = yield defer.ensureDeferred(
  363. self.handler.create_version(
  364. self.local_user,
  365. {
  366. "algorithm": "m.megolm_backup.v1",
  367. "auth_data": "first_version_auth_data",
  368. },
  369. )
  370. )
  371. self.assertEqual(version, "1")
  372. res = None
  373. try:
  374. yield defer.ensureDeferred(
  375. self.handler.upload_room_keys(
  376. self.local_user, "bogus_version", room_keys
  377. )
  378. )
  379. except errors.SynapseError as e:
  380. res = e.code
  381. self.assertEqual(res, 404)
  382. @defer.inlineCallbacks
  383. def test_upload_room_keys_wrong_version(self):
  384. """Check that we get a 403 on uploading keys for an old version
  385. """
  386. version = yield defer.ensureDeferred(
  387. self.handler.create_version(
  388. self.local_user,
  389. {
  390. "algorithm": "m.megolm_backup.v1",
  391. "auth_data": "first_version_auth_data",
  392. },
  393. )
  394. )
  395. self.assertEqual(version, "1")
  396. version = yield defer.ensureDeferred(
  397. self.handler.create_version(
  398. self.local_user,
  399. {
  400. "algorithm": "m.megolm_backup.v1",
  401. "auth_data": "second_version_auth_data",
  402. },
  403. )
  404. )
  405. self.assertEqual(version, "2")
  406. res = None
  407. try:
  408. yield defer.ensureDeferred(
  409. self.handler.upload_room_keys(self.local_user, "1", room_keys)
  410. )
  411. except errors.SynapseError as e:
  412. res = e.code
  413. self.assertEqual(res, 403)
  414. @defer.inlineCallbacks
  415. def test_upload_room_keys_insert(self):
  416. """Check that we can insert and retrieve keys for a session
  417. """
  418. version = yield defer.ensureDeferred(
  419. self.handler.create_version(
  420. self.local_user,
  421. {
  422. "algorithm": "m.megolm_backup.v1",
  423. "auth_data": "first_version_auth_data",
  424. },
  425. )
  426. )
  427. self.assertEqual(version, "1")
  428. yield defer.ensureDeferred(
  429. self.handler.upload_room_keys(self.local_user, version, room_keys)
  430. )
  431. res = yield defer.ensureDeferred(
  432. self.handler.get_room_keys(self.local_user, version)
  433. )
  434. self.assertDictEqual(res, room_keys)
  435. # check getting room_keys for a given room
  436. res = yield defer.ensureDeferred(
  437. self.handler.get_room_keys(
  438. self.local_user, version, room_id="!abc:matrix.org"
  439. )
  440. )
  441. self.assertDictEqual(res, room_keys)
  442. # check getting room_keys for a given session_id
  443. res = yield defer.ensureDeferred(
  444. self.handler.get_room_keys(
  445. self.local_user, version, room_id="!abc:matrix.org", session_id="c0ff33"
  446. )
  447. )
  448. self.assertDictEqual(res, room_keys)
  449. @defer.inlineCallbacks
  450. def test_upload_room_keys_merge(self):
  451. """Check that we can upload a new room_key for an existing session and
  452. have it correctly merged"""
  453. version = yield defer.ensureDeferred(
  454. self.handler.create_version(
  455. self.local_user,
  456. {
  457. "algorithm": "m.megolm_backup.v1",
  458. "auth_data": "first_version_auth_data",
  459. },
  460. )
  461. )
  462. self.assertEqual(version, "1")
  463. yield defer.ensureDeferred(
  464. self.handler.upload_room_keys(self.local_user, version, room_keys)
  465. )
  466. # get the etag to compare to future versions
  467. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  468. backup_etag = res["etag"]
  469. self.assertEqual(res["count"], 1)
  470. new_room_keys = copy.deepcopy(room_keys)
  471. new_room_key = new_room_keys["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]
  472. # test that increasing the message_index doesn't replace the existing session
  473. new_room_key["first_message_index"] = 2
  474. new_room_key["session_data"] = "new"
  475. yield defer.ensureDeferred(
  476. self.handler.upload_room_keys(self.local_user, version, new_room_keys)
  477. )
  478. res = yield defer.ensureDeferred(
  479. self.handler.get_room_keys(self.local_user, version)
  480. )
  481. self.assertEqual(
  482. res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"],
  483. "SSBBTSBBIEZJU0gK",
  484. )
  485. # the etag should be the same since the session did not change
  486. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  487. self.assertEqual(res["etag"], backup_etag)
  488. # test that marking the session as verified however /does/ replace it
  489. new_room_key["is_verified"] = True
  490. yield defer.ensureDeferred(
  491. self.handler.upload_room_keys(self.local_user, version, new_room_keys)
  492. )
  493. res = yield defer.ensureDeferred(
  494. self.handler.get_room_keys(self.local_user, version)
  495. )
  496. self.assertEqual(
  497. res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new"
  498. )
  499. # the etag should NOT be equal now, since the key changed
  500. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  501. self.assertNotEqual(res["etag"], backup_etag)
  502. backup_etag = res["etag"]
  503. # test that a session with a higher forwarded_count doesn't replace one
  504. # with a lower forwarding count
  505. new_room_key["forwarded_count"] = 2
  506. new_room_key["session_data"] = "other"
  507. yield defer.ensureDeferred(
  508. self.handler.upload_room_keys(self.local_user, version, new_room_keys)
  509. )
  510. res = yield defer.ensureDeferred(
  511. self.handler.get_room_keys(self.local_user, version)
  512. )
  513. self.assertEqual(
  514. res["rooms"]["!abc:matrix.org"]["sessions"]["c0ff33"]["session_data"], "new"
  515. )
  516. # the etag should be the same since the session did not change
  517. res = yield defer.ensureDeferred(self.handler.get_version_info(self.local_user))
  518. self.assertEqual(res["etag"], backup_etag)
  519. # TODO: check edge cases as well as the common variations here
  520. @defer.inlineCallbacks
  521. def test_delete_room_keys(self):
  522. """Check that we can insert and delete keys for a session
  523. """
  524. version = yield defer.ensureDeferred(
  525. self.handler.create_version(
  526. self.local_user,
  527. {
  528. "algorithm": "m.megolm_backup.v1",
  529. "auth_data": "first_version_auth_data",
  530. },
  531. )
  532. )
  533. self.assertEqual(version, "1")
  534. # check for bulk-delete
  535. yield defer.ensureDeferred(
  536. self.handler.upload_room_keys(self.local_user, version, room_keys)
  537. )
  538. yield defer.ensureDeferred(
  539. self.handler.delete_room_keys(self.local_user, version)
  540. )
  541. res = yield defer.ensureDeferred(
  542. self.handler.get_room_keys(
  543. self.local_user, version, room_id="!abc:matrix.org", session_id="c0ff33"
  544. )
  545. )
  546. self.assertDictEqual(res, {"rooms": {}})
  547. # check for bulk-delete per room
  548. yield defer.ensureDeferred(
  549. self.handler.upload_room_keys(self.local_user, version, room_keys)
  550. )
  551. yield defer.ensureDeferred(
  552. self.handler.delete_room_keys(
  553. self.local_user, version, room_id="!abc:matrix.org"
  554. )
  555. )
  556. res = yield defer.ensureDeferred(
  557. self.handler.get_room_keys(
  558. self.local_user, version, room_id="!abc:matrix.org", session_id="c0ff33"
  559. )
  560. )
  561. self.assertDictEqual(res, {"rooms": {}})
  562. # check for bulk-delete per session
  563. yield defer.ensureDeferred(
  564. self.handler.upload_room_keys(self.local_user, version, room_keys)
  565. )
  566. yield defer.ensureDeferred(
  567. self.handler.delete_room_keys(
  568. self.local_user, version, room_id="!abc:matrix.org", session_id="c0ff33"
  569. )
  570. )
  571. res = yield defer.ensureDeferred(
  572. self.handler.get_room_keys(
  573. self.local_user, version, room_id="!abc:matrix.org", session_id="c0ff33"
  574. )
  575. )
  576. self.assertDictEqual(res, {"rooms": {}})