Always communicate device OTK counts to clients (#10485)
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
parent
92a882254b
commit
74d09a43d9
|
@ -0,0 +1 @@
|
||||||
|
Fix a long-standing bug where Synapse would not inform clients that a device had exhausted its one-time-key pool, potentially causing problems decrypting events.
|
|
@ -127,6 +127,14 @@ class ToDeviceEventTypes:
|
||||||
RoomKeyRequest = "m.room_key_request"
|
RoomKeyRequest = "m.room_key_request"
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceKeyAlgorithms:
|
||||||
|
"""Spec'd algorithms for the generation of per-device keys"""
|
||||||
|
|
||||||
|
ED25519 = "ed25519"
|
||||||
|
CURVE25519 = "curve25519"
|
||||||
|
SIGNED_CURVE25519 = "signed_curve25519"
|
||||||
|
|
||||||
|
|
||||||
class EduTypes:
|
class EduTypes:
|
||||||
Presence = "m.presence"
|
Presence = "m.presence"
|
||||||
|
|
||||||
|
|
|
@ -1093,6 +1093,10 @@ class SyncHandler:
|
||||||
one_time_key_counts: JsonDict = {}
|
one_time_key_counts: JsonDict = {}
|
||||||
unused_fallback_key_types: List[str] = []
|
unused_fallback_key_types: List[str] = []
|
||||||
if device_id:
|
if device_id:
|
||||||
|
# TODO: We should have a way to let clients differentiate between the states of:
|
||||||
|
# * no change in OTK count since the provided since token
|
||||||
|
# * the server has zero OTKs left for this device
|
||||||
|
# Spec issue: https://github.com/matrix-org/matrix-doc/issues/3298
|
||||||
one_time_key_counts = await self.store.count_e2e_one_time_keys(
|
one_time_key_counts = await self.store.count_e2e_one_time_keys(
|
||||||
user_id, device_id
|
user_id, device_id
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@ from canonicaljson import encode_canonical_json
|
||||||
|
|
||||||
from twisted.enterprise.adbapi import Connection
|
from twisted.enterprise.adbapi import Connection
|
||||||
|
|
||||||
|
from synapse.api.constants import DeviceKeyAlgorithms
|
||||||
from synapse.logging.opentracing import log_kv, set_tag, trace
|
from synapse.logging.opentracing import log_kv, set_tag, trace
|
||||||
from synapse.storage._base import SQLBaseStore, db_to_json
|
from synapse.storage._base import SQLBaseStore, db_to_json
|
||||||
from synapse.storage.database import DatabasePool, make_in_list_sql_clause
|
from synapse.storage.database import DatabasePool, make_in_list_sql_clause
|
||||||
|
@ -381,9 +382,15 @@ class EndToEndKeyWorkerStore(EndToEndKeyBackgroundStore):
|
||||||
" GROUP BY algorithm"
|
" GROUP BY algorithm"
|
||||||
)
|
)
|
||||||
txn.execute(sql, (user_id, device_id))
|
txn.execute(sql, (user_id, device_id))
|
||||||
result = {}
|
|
||||||
|
# Initially set the key count to 0. This ensures that the client will always
|
||||||
|
# receive *some count*, even if it's 0.
|
||||||
|
result = {DeviceKeyAlgorithms.SIGNED_CURVE25519: 0}
|
||||||
|
|
||||||
|
# Override entries with the count of any keys we pulled from the database
|
||||||
for algorithm, key_count in txn:
|
for algorithm, key_count in txn:
|
||||||
result[algorithm] = key_count
|
result[algorithm] = key_count
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return await self.db_pool.runInteraction(
|
return await self.db_pool.runInteraction(
|
||||||
|
|
|
@ -47,12 +47,16 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
"alg2:k3": {"key": "key3"},
|
"alg2:k3": {"key": "key3"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Note that "signed_curve25519" is always returned in key count responses. This is necessary until
|
||||||
|
# https://github.com/matrix-org/matrix-doc/issues/3298 is fixed.
|
||||||
res = self.get_success(
|
res = self.get_success(
|
||||||
self.handler.upload_keys_for_user(
|
self.handler.upload_keys_for_user(
|
||||||
local_user, device_id, {"one_time_keys": keys}
|
local_user, device_id, {"one_time_keys": keys}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
|
self.assertDictEqual(
|
||||||
|
res, {"one_time_key_counts": {"alg1": 1, "alg2": 2, "signed_curve25519": 0}}
|
||||||
|
)
|
||||||
|
|
||||||
# we should be able to change the signature without a problem
|
# we should be able to change the signature without a problem
|
||||||
keys["alg2:k2"]["signatures"]["k1"] = "sig2"
|
keys["alg2:k2"]["signatures"]["k1"] = "sig2"
|
||||||
|
@ -61,7 +65,9 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
local_user, device_id, {"one_time_keys": keys}
|
local_user, device_id, {"one_time_keys": keys}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
|
self.assertDictEqual(
|
||||||
|
res, {"one_time_key_counts": {"alg1": 1, "alg2": 2, "signed_curve25519": 0}}
|
||||||
|
)
|
||||||
|
|
||||||
def test_change_one_time_keys(self):
|
def test_change_one_time_keys(self):
|
||||||
"""attempts to change one-time-keys should be rejected"""
|
"""attempts to change one-time-keys should be rejected"""
|
||||||
|
@ -79,7 +85,9 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
local_user, device_id, {"one_time_keys": keys}
|
local_user, device_id, {"one_time_keys": keys}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1, "alg2": 2}})
|
self.assertDictEqual(
|
||||||
|
res, {"one_time_key_counts": {"alg1": 1, "alg2": 2, "signed_curve25519": 0}}
|
||||||
|
)
|
||||||
|
|
||||||
# Error when changing string key
|
# Error when changing string key
|
||||||
self.get_failure(
|
self.get_failure(
|
||||||
|
@ -89,7 +97,7 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
SynapseError,
|
SynapseError,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Error when replacing dict key with strin
|
# Error when replacing dict key with string
|
||||||
self.get_failure(
|
self.get_failure(
|
||||||
self.handler.upload_keys_for_user(
|
self.handler.upload_keys_for_user(
|
||||||
local_user, device_id, {"one_time_keys": {"alg2:k3": "key2"}}
|
local_user, device_id, {"one_time_keys": {"alg2:k3": "key2"}}
|
||||||
|
@ -131,7 +139,9 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
|
||||||
local_user, device_id, {"one_time_keys": keys}
|
local_user, device_id, {"one_time_keys": keys}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertDictEqual(res, {"one_time_key_counts": {"alg1": 1}})
|
self.assertDictEqual(
|
||||||
|
res, {"one_time_key_counts": {"alg1": 1, "signed_curve25519": 0}}
|
||||||
|
)
|
||||||
|
|
||||||
res2 = self.get_success(
|
res2 = self.get_success(
|
||||||
self.handler.claim_one_time_keys(
|
self.handler.claim_one_time_keys(
|
||||||
|
|
Loading…
Reference in New Issue