test_e2e_keys.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 OpenMarket Ltd
  3. # Copyright 2019 New Vector Ltd
  4. # Copyright 2019 The 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 mock
  18. from signedjson import key as key, sign as sign
  19. from twisted.internet import defer
  20. import synapse.handlers.e2e_keys
  21. import synapse.storage
  22. from synapse.api import errors
  23. from synapse.api.constants import RoomEncryptionAlgorithms
  24. from tests import unittest, utils
  25. class E2eKeysHandlerTestCase(unittest.TestCase):
  26. def __init__(self, *args, **kwargs):
  27. super().__init__(*args, **kwargs)
  28. self.hs = None # type: synapse.server.HomeServer
  29. self.handler = None # type: synapse.handlers.e2e_keys.E2eKeysHandler
  30. self.store = None # type: synapse.storage.Storage
  31. @defer.inlineCallbacks
  32. def setUp(self):
  33. self.hs = yield utils.setup_test_homeserver(
  34. self.addCleanup, federation_client=mock.Mock()
  35. )
  36. self.handler = synapse.handlers.e2e_keys.E2eKeysHandler(self.hs)
  37. self.store = self.hs.get_datastore()
  38. @defer.inlineCallbacks
  39. def test_query_local_devices_no_devices(self):
  40. """If the user has no devices, we expect an empty list.
  41. """
  42. local_user = "@boris:" + self.hs.hostname
  43. res = yield defer.ensureDeferred(
  44. self.handler.query_local_devices({local_user: None})
  45. )
  46. self.assertDictEqual(res, {local_user: {}})
  47. @defer.inlineCallbacks
  48. def test_reupload_one_time_keys(self):
  49. """we should be able to re-upload the same keys"""
  50. local_user = "@boris:" + self.hs.hostname
  51. device_id = "xyz"
  52. keys = {
  53. "alg1:k1": "key1",
  54. "alg2:k2": {"key": "key2", "signatures": {"k1": "sig1"}},
  55. "alg2:k3": {"key": "key3"},
  56. }
  57. res = yield defer.ensureDeferred(
  58. self.handler.upload_keys_for_user(
  59. local_user, device_id, {"one_time_keys": keys}
  60. )
  61. )
  62. self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
  63. # we should be able to change the signature without a problem
  64. keys["alg2:k2"]["signatures"]["k1"] = "sig2"
  65. res = yield defer.ensureDeferred(
  66. self.handler.upload_keys_for_user(
  67. local_user, device_id, {"one_time_keys": keys}
  68. )
  69. )
  70. self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
  71. @defer.inlineCallbacks
  72. def test_change_one_time_keys(self):
  73. """attempts to change one-time-keys should be rejected"""
  74. local_user = "@boris:" + self.hs.hostname
  75. device_id = "xyz"
  76. keys = {
  77. "alg1:k1": "key1",
  78. "alg2:k2": {"key": "key2", "signatures": {"k1": "sig1"}},
  79. "alg2:k3": {"key": "key3"},
  80. }
  81. res = yield defer.ensureDeferred(
  82. self.handler.upload_keys_for_user(
  83. local_user, device_id, {"one_time_keys": keys}
  84. )
  85. )
  86. self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
  87. try:
  88. yield defer.ensureDeferred(
  89. self.handler.upload_keys_for_user(
  90. local_user, device_id, {"one_time_keys": {"alg1:k1": "key2"}}
  91. )
  92. )
  93. self.fail("No error when changing string key")
  94. except errors.SynapseError:
  95. pass
  96. try:
  97. yield defer.ensureDeferred(
  98. self.handler.upload_keys_for_user(
  99. local_user, device_id, {"one_time_keys": {"alg2:k3": "key2"}}
  100. )
  101. )
  102. self.fail("No error when replacing dict key with string")
  103. except errors.SynapseError:
  104. pass
  105. try:
  106. yield defer.ensureDeferred(
  107. self.handler.upload_keys_for_user(
  108. local_user,
  109. device_id,
  110. {"one_time_keys": {"alg1:k1": {"key": "key"}}},
  111. )
  112. )
  113. self.fail("No error when replacing string key with dict")
  114. except errors.SynapseError:
  115. pass
  116. try:
  117. yield defer.ensureDeferred(
  118. self.handler.upload_keys_for_user(
  119. local_user,
  120. device_id,
  121. {
  122. "one_time_keys": {
  123. "alg2:k2": {"key": "key3", "signatures": {"k1": "sig1"}}
  124. }
  125. },
  126. )
  127. )
  128. self.fail("No error when replacing dict key")
  129. except errors.SynapseError:
  130. pass
  131. @defer.inlineCallbacks
  132. def test_claim_one_time_key(self):
  133. local_user = "@boris:" + self.hs.hostname
  134. device_id = "xyz"
  135. keys = {"alg1:k1": "key1"}
  136. res = yield defer.ensureDeferred(
  137. self.handler.upload_keys_for_user(
  138. local_user, device_id, {"one_time_keys": keys}
  139. )
  140. )
  141. self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1}})
  142. res2 = yield defer.ensureDeferred(
  143. self.handler.claim_one_time_keys(
  144. {"one_time_keys": {local_user: {device_id: "alg1"}}}, timeout=None
  145. )
  146. )
  147. self.assertEqual(
  148. res2,
  149. {
  150. "failures": {},
  151. "one_time_keys": {local_user: {device_id: {"alg1:k1": "key1"}}},
  152. },
  153. )
  154. @defer.inlineCallbacks
  155. def test_fallback_key(self):
  156. local_user = "@boris:" + self.hs.hostname
  157. device_id = "xyz"
  158. fallback_key = {"alg1:k1": "key1"}
  159. otk = {"alg1:k2": "key2"}
  160. # we shouldn't have any unused fallback keys yet
  161. res = yield defer.ensureDeferred(
  162. self.store.get_e2e_unused_fallback_key_types(local_user, device_id)
  163. )
  164. self.assertEqual(res, [])
  165. yield defer.ensureDeferred(
  166. self.handler.upload_keys_for_user(
  167. local_user,
  168. device_id,
  169. {"org.matrix.msc2732.fallback_keys": fallback_key},
  170. )
  171. )
  172. # we should now have an unused alg1 key
  173. res = yield defer.ensureDeferred(
  174. self.store.get_e2e_unused_fallback_key_types(local_user, device_id)
  175. )
  176. self.assertEqual(res, ["alg1"])
  177. # claiming an OTK when no OTKs are available should return the fallback
  178. # key
  179. res = yield defer.ensureDeferred(
  180. self.handler.claim_one_time_keys(
  181. {"one_time_keys": {local_user: {device_id: "alg1"}}}, timeout=None
  182. )
  183. )
  184. self.assertEqual(
  185. res,
  186. {"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
  187. )
  188. # we shouldn't have any unused fallback keys again
  189. res = yield defer.ensureDeferred(
  190. self.store.get_e2e_unused_fallback_key_types(local_user, device_id)
  191. )
  192. self.assertEqual(res, [])
  193. # claiming an OTK again should return the same fallback key
  194. res = yield defer.ensureDeferred(
  195. self.handler.claim_one_time_keys(
  196. {"one_time_keys": {local_user: {device_id: "alg1"}}}, timeout=None
  197. )
  198. )
  199. self.assertEqual(
  200. res,
  201. {"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
  202. )
  203. # if the user uploads a one-time key, the next claim should fetch the
  204. # one-time key, and then go back to the fallback
  205. yield defer.ensureDeferred(
  206. self.handler.upload_keys_for_user(
  207. local_user, device_id, {"one_time_keys": otk}
  208. )
  209. )
  210. res = yield defer.ensureDeferred(
  211. self.handler.claim_one_time_keys(
  212. {"one_time_keys": {local_user: {device_id: "alg1"}}}, timeout=None
  213. )
  214. )
  215. self.assertEqual(
  216. res, {"failures": {}, "one_time_keys": {local_user: {device_id: otk}}},
  217. )
  218. res = yield defer.ensureDeferred(
  219. self.handler.claim_one_time_keys(
  220. {"one_time_keys": {local_user: {device_id: "alg1"}}}, timeout=None
  221. )
  222. )
  223. self.assertEqual(
  224. res,
  225. {"failures": {}, "one_time_keys": {local_user: {device_id: fallback_key}}},
  226. )
  227. @defer.inlineCallbacks
  228. def test_replace_master_key(self):
  229. """uploading a new signing key should make the old signing key unavailable"""
  230. local_user = "@boris:" + self.hs.hostname
  231. keys1 = {
  232. "master_key": {
  233. # private key: 2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0
  234. "user_id": local_user,
  235. "usage": ["master"],
  236. "keys": {
  237. "ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk"
  238. },
  239. }
  240. }
  241. yield defer.ensureDeferred(
  242. self.handler.upload_signing_keys_for_user(local_user, keys1)
  243. )
  244. keys2 = {
  245. "master_key": {
  246. # private key: 4TL4AjRYwDVwD3pqQzcor+ez/euOB1/q78aTJ+czDNs
  247. "user_id": local_user,
  248. "usage": ["master"],
  249. "keys": {
  250. "ed25519:Hq6gL+utB4ET+UvD5ci0kgAwsX6qP/zvf8v6OInU5iw": "Hq6gL+utB4ET+UvD5ci0kgAwsX6qP/zvf8v6OInU5iw"
  251. },
  252. }
  253. }
  254. yield defer.ensureDeferred(
  255. self.handler.upload_signing_keys_for_user(local_user, keys2)
  256. )
  257. devices = yield defer.ensureDeferred(
  258. self.handler.query_devices({"device_keys": {local_user: []}}, 0, local_user)
  259. )
  260. self.assertDictEqual(devices["master_keys"], {local_user: keys2["master_key"]})
  261. @defer.inlineCallbacks
  262. def test_reupload_signatures(self):
  263. """re-uploading a signature should not fail"""
  264. local_user = "@boris:" + self.hs.hostname
  265. keys1 = {
  266. "master_key": {
  267. # private key: HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8
  268. "user_id": local_user,
  269. "usage": ["master"],
  270. "keys": {
  271. "ed25519:EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ": "EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ"
  272. },
  273. },
  274. "self_signing_key": {
  275. # private key: 2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0
  276. "user_id": local_user,
  277. "usage": ["self_signing"],
  278. "keys": {
  279. "ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk"
  280. },
  281. },
  282. }
  283. master_signing_key = key.decode_signing_key_base64(
  284. "ed25519",
  285. "EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ",
  286. "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8",
  287. )
  288. sign.sign_json(keys1["self_signing_key"], local_user, master_signing_key)
  289. signing_key = key.decode_signing_key_base64(
  290. "ed25519",
  291. "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk",
  292. "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0",
  293. )
  294. yield defer.ensureDeferred(
  295. self.handler.upload_signing_keys_for_user(local_user, keys1)
  296. )
  297. # upload two device keys, which will be signed later by the self-signing key
  298. device_key_1 = {
  299. "user_id": local_user,
  300. "device_id": "abc",
  301. "algorithms": [
  302. "m.olm.curve25519-aes-sha2",
  303. RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
  304. ],
  305. "keys": {
  306. "ed25519:abc": "base64+ed25519+key",
  307. "curve25519:abc": "base64+curve25519+key",
  308. },
  309. "signatures": {local_user: {"ed25519:abc": "base64+signature"}},
  310. }
  311. device_key_2 = {
  312. "user_id": local_user,
  313. "device_id": "def",
  314. "algorithms": [
  315. "m.olm.curve25519-aes-sha2",
  316. RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
  317. ],
  318. "keys": {
  319. "ed25519:def": "base64+ed25519+key",
  320. "curve25519:def": "base64+curve25519+key",
  321. },
  322. "signatures": {local_user: {"ed25519:def": "base64+signature"}},
  323. }
  324. yield defer.ensureDeferred(
  325. self.handler.upload_keys_for_user(
  326. local_user, "abc", {"device_keys": device_key_1}
  327. )
  328. )
  329. yield defer.ensureDeferred(
  330. self.handler.upload_keys_for_user(
  331. local_user, "def", {"device_keys": device_key_2}
  332. )
  333. )
  334. # sign the first device key and upload it
  335. del device_key_1["signatures"]
  336. sign.sign_json(device_key_1, local_user, signing_key)
  337. yield defer.ensureDeferred(
  338. self.handler.upload_signatures_for_device_keys(
  339. local_user, {local_user: {"abc": device_key_1}}
  340. )
  341. )
  342. # sign the second device key and upload both device keys. The server
  343. # should ignore the first device key since it already has a valid
  344. # signature for it
  345. del device_key_2["signatures"]
  346. sign.sign_json(device_key_2, local_user, signing_key)
  347. yield defer.ensureDeferred(
  348. self.handler.upload_signatures_for_device_keys(
  349. local_user, {local_user: {"abc": device_key_1, "def": device_key_2}}
  350. )
  351. )
  352. device_key_1["signatures"][local_user]["ed25519:abc"] = "base64+signature"
  353. device_key_2["signatures"][local_user]["ed25519:def"] = "base64+signature"
  354. devices = yield defer.ensureDeferred(
  355. self.handler.query_devices({"device_keys": {local_user: []}}, 0, local_user)
  356. )
  357. del devices["device_keys"][local_user]["abc"]["unsigned"]
  358. del devices["device_keys"][local_user]["def"]["unsigned"]
  359. self.assertDictEqual(devices["device_keys"][local_user]["abc"], device_key_1)
  360. self.assertDictEqual(devices["device_keys"][local_user]["def"], device_key_2)
  361. @defer.inlineCallbacks
  362. def test_self_signing_key_doesnt_show_up_as_device(self):
  363. """signing keys should be hidden when fetching a user's devices"""
  364. local_user = "@boris:" + self.hs.hostname
  365. keys1 = {
  366. "master_key": {
  367. # private key: 2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0
  368. "user_id": local_user,
  369. "usage": ["master"],
  370. "keys": {
  371. "ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk"
  372. },
  373. }
  374. }
  375. yield defer.ensureDeferred(
  376. self.handler.upload_signing_keys_for_user(local_user, keys1)
  377. )
  378. res = None
  379. try:
  380. yield defer.ensureDeferred(
  381. self.hs.get_device_handler().check_device_registered(
  382. user_id=local_user,
  383. device_id="nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk",
  384. initial_device_display_name="new display name",
  385. )
  386. )
  387. except errors.SynapseError as e:
  388. res = e.code
  389. self.assertEqual(res, 400)
  390. res = yield defer.ensureDeferred(
  391. self.handler.query_local_devices({local_user: None})
  392. )
  393. self.assertDictEqual(res, {local_user: {}})
  394. @defer.inlineCallbacks
  395. def test_upload_signatures(self):
  396. """should check signatures that are uploaded"""
  397. # set up a user with cross-signing keys and a device. This user will
  398. # try uploading signatures
  399. local_user = "@boris:" + self.hs.hostname
  400. device_id = "xyz"
  401. # private key: OMkooTr76ega06xNvXIGPbgvvxAOzmQncN8VObS7aBA
  402. device_pubkey = "NnHhnqiMFQkq969szYkooLaBAXW244ZOxgukCvm2ZeY"
  403. device_key = {
  404. "user_id": local_user,
  405. "device_id": device_id,
  406. "algorithms": [
  407. "m.olm.curve25519-aes-sha2",
  408. RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
  409. ],
  410. "keys": {"curve25519:xyz": "curve25519+key", "ed25519:xyz": device_pubkey},
  411. "signatures": {local_user: {"ed25519:xyz": "something"}},
  412. }
  413. device_signing_key = key.decode_signing_key_base64(
  414. "ed25519", "xyz", "OMkooTr76ega06xNvXIGPbgvvxAOzmQncN8VObS7aBA"
  415. )
  416. yield defer.ensureDeferred(
  417. self.handler.upload_keys_for_user(
  418. local_user, device_id, {"device_keys": device_key}
  419. )
  420. )
  421. # private key: 2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0
  422. master_pubkey = "nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk"
  423. master_key = {
  424. "user_id": local_user,
  425. "usage": ["master"],
  426. "keys": {"ed25519:" + master_pubkey: master_pubkey},
  427. }
  428. master_signing_key = key.decode_signing_key_base64(
  429. "ed25519", master_pubkey, "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0"
  430. )
  431. usersigning_pubkey = "Hq6gL+utB4ET+UvD5ci0kgAwsX6qP/zvf8v6OInU5iw"
  432. usersigning_key = {
  433. # private key: 4TL4AjRYwDVwD3pqQzcor+ez/euOB1/q78aTJ+czDNs
  434. "user_id": local_user,
  435. "usage": ["user_signing"],
  436. "keys": {"ed25519:" + usersigning_pubkey: usersigning_pubkey},
  437. }
  438. usersigning_signing_key = key.decode_signing_key_base64(
  439. "ed25519", usersigning_pubkey, "4TL4AjRYwDVwD3pqQzcor+ez/euOB1/q78aTJ+czDNs"
  440. )
  441. sign.sign_json(usersigning_key, local_user, master_signing_key)
  442. # private key: HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8
  443. selfsigning_pubkey = "EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ"
  444. selfsigning_key = {
  445. "user_id": local_user,
  446. "usage": ["self_signing"],
  447. "keys": {"ed25519:" + selfsigning_pubkey: selfsigning_pubkey},
  448. }
  449. selfsigning_signing_key = key.decode_signing_key_base64(
  450. "ed25519", selfsigning_pubkey, "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8"
  451. )
  452. sign.sign_json(selfsigning_key, local_user, master_signing_key)
  453. cross_signing_keys = {
  454. "master_key": master_key,
  455. "user_signing_key": usersigning_key,
  456. "self_signing_key": selfsigning_key,
  457. }
  458. yield defer.ensureDeferred(
  459. self.handler.upload_signing_keys_for_user(local_user, cross_signing_keys)
  460. )
  461. # set up another user with a master key. This user will be signed by
  462. # the first user
  463. other_user = "@otherboris:" + self.hs.hostname
  464. other_master_pubkey = "fHZ3NPiKxoLQm5OoZbKa99SYxprOjNs4TwJUKP+twCM"
  465. other_master_key = {
  466. # private key: oyw2ZUx0O4GifbfFYM0nQvj9CL0b8B7cyN4FprtK8OI
  467. "user_id": other_user,
  468. "usage": ["master"],
  469. "keys": {"ed25519:" + other_master_pubkey: other_master_pubkey},
  470. }
  471. yield defer.ensureDeferred(
  472. self.handler.upload_signing_keys_for_user(
  473. other_user, {"master_key": other_master_key}
  474. )
  475. )
  476. # test various signature failures (see below)
  477. ret = yield defer.ensureDeferred(
  478. self.handler.upload_signatures_for_device_keys(
  479. local_user,
  480. {
  481. local_user: {
  482. # fails because the signature is invalid
  483. # should fail with INVALID_SIGNATURE
  484. device_id: {
  485. "user_id": local_user,
  486. "device_id": device_id,
  487. "algorithms": [
  488. "m.olm.curve25519-aes-sha2",
  489. RoomEncryptionAlgorithms.MEGOLM_V1_AES_SHA2,
  490. ],
  491. "keys": {
  492. "curve25519:xyz": "curve25519+key",
  493. # private key: OMkooTr76ega06xNvXIGPbgvvxAOzmQncN8VObS7aBA
  494. "ed25519:xyz": device_pubkey,
  495. },
  496. "signatures": {
  497. local_user: {
  498. "ed25519:" + selfsigning_pubkey: "something"
  499. }
  500. },
  501. },
  502. # fails because device is unknown
  503. # should fail with NOT_FOUND
  504. "unknown": {
  505. "user_id": local_user,
  506. "device_id": "unknown",
  507. "signatures": {
  508. local_user: {
  509. "ed25519:" + selfsigning_pubkey: "something"
  510. }
  511. },
  512. },
  513. # fails because the signature is invalid
  514. # should fail with INVALID_SIGNATURE
  515. master_pubkey: {
  516. "user_id": local_user,
  517. "usage": ["master"],
  518. "keys": {"ed25519:" + master_pubkey: master_pubkey},
  519. "signatures": {
  520. local_user: {"ed25519:" + device_pubkey: "something"}
  521. },
  522. },
  523. },
  524. other_user: {
  525. # fails because the device is not the user's master-signing key
  526. # should fail with NOT_FOUND
  527. "unknown": {
  528. "user_id": other_user,
  529. "device_id": "unknown",
  530. "signatures": {
  531. local_user: {
  532. "ed25519:" + usersigning_pubkey: "something"
  533. }
  534. },
  535. },
  536. other_master_pubkey: {
  537. # fails because the key doesn't match what the server has
  538. # should fail with UNKNOWN
  539. "user_id": other_user,
  540. "usage": ["master"],
  541. "keys": {
  542. "ed25519:" + other_master_pubkey: other_master_pubkey
  543. },
  544. "something": "random",
  545. "signatures": {
  546. local_user: {
  547. "ed25519:" + usersigning_pubkey: "something"
  548. }
  549. },
  550. },
  551. },
  552. },
  553. )
  554. )
  555. user_failures = ret["failures"][local_user]
  556. self.assertEqual(
  557. user_failures[device_id]["errcode"], errors.Codes.INVALID_SIGNATURE
  558. )
  559. self.assertEqual(
  560. user_failures[master_pubkey]["errcode"], errors.Codes.INVALID_SIGNATURE
  561. )
  562. self.assertEqual(user_failures["unknown"]["errcode"], errors.Codes.NOT_FOUND)
  563. other_user_failures = ret["failures"][other_user]
  564. self.assertEqual(
  565. other_user_failures["unknown"]["errcode"], errors.Codes.NOT_FOUND
  566. )
  567. self.assertEqual(
  568. other_user_failures[other_master_pubkey]["errcode"], errors.Codes.UNKNOWN
  569. )
  570. # test successful signatures
  571. del device_key["signatures"]
  572. sign.sign_json(device_key, local_user, selfsigning_signing_key)
  573. sign.sign_json(master_key, local_user, device_signing_key)
  574. sign.sign_json(other_master_key, local_user, usersigning_signing_key)
  575. ret = yield defer.ensureDeferred(
  576. self.handler.upload_signatures_for_device_keys(
  577. local_user,
  578. {
  579. local_user: {device_id: device_key, master_pubkey: master_key},
  580. other_user: {other_master_pubkey: other_master_key},
  581. },
  582. )
  583. )
  584. self.assertEqual(ret["failures"], {})
  585. # fetch the signed keys/devices and make sure that the signatures are there
  586. ret = yield defer.ensureDeferred(
  587. self.handler.query_devices(
  588. {"device_keys": {local_user: [], other_user: []}}, 0, local_user
  589. )
  590. )
  591. self.assertEqual(
  592. ret["device_keys"][local_user]["xyz"]["signatures"][local_user][
  593. "ed25519:" + selfsigning_pubkey
  594. ],
  595. device_key["signatures"][local_user]["ed25519:" + selfsigning_pubkey],
  596. )
  597. self.assertEqual(
  598. ret["master_keys"][local_user]["signatures"][local_user][
  599. "ed25519:" + device_id
  600. ],
  601. master_key["signatures"][local_user]["ed25519:" + device_id],
  602. )
  603. self.assertEqual(
  604. ret["master_keys"][other_user]["signatures"][local_user][
  605. "ed25519:" + usersigning_pubkey
  606. ],
  607. other_master_key["signatures"][local_user]["ed25519:" + usersigning_pubkey],
  608. )