Compare commits

...

5 Commits

Author SHA1 Message Date
Andrew Morgan
db2d8becc5
Merge e31449907405a4b721acedf0c8aa134291ee194e into 0615b64bb49684b846110465052642a46fd27028 2025-10-02 11:11:10 +01:00
dependabot[bot]
0615b64bb4
Bump phonenumbers from 9.0.14 to 9.0.15 (#18991)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 13:50:12 +01:00
Erik Johnston
e314499074
Merge branch 'develop' into anoa-codex/limit-key-upload-to-one-per-device 2025-06-25 10:10:31 +01:00
Andrew Morgan
5be825cb09 newsfile 2025-06-04 13:15:00 +01:00
anoa's Codex Agent
cdf88b5706 Disallow device key changes 2025-06-04 13:09:27 +01:00
5 changed files with 72 additions and 6 deletions

1
changelog.d/18512.misc Normal file
View File

@ -0,0 +1 @@
Prevent users from changing their device keys once they've already been set.

6
poetry.lock generated
View File

@ -1589,14 +1589,14 @@ files = [
[[package]]
name = "phonenumbers"
version = "9.0.14"
version = "9.0.15"
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "phonenumbers-9.0.14-py2.py3-none-any.whl", hash = "sha256:6bdf5c46dbfefa1d941d122432d1958418d1dfe3f8c8c81d4c8e80f5442ea41f"},
{file = "phonenumbers-9.0.14.tar.gz", hash = "sha256:98afb3e86bf9ae02cc7c98ca44fa8827babb72842f90da9884c5d998937572ae"},
{file = "phonenumbers-9.0.15-py2.py3-none-any.whl", hash = "sha256:269b73bc05258e8fd57582770b9559307099ea677c8f1dc5272476f661344776"},
{file = "phonenumbers-9.0.15.tar.gz", hash = "sha256:345ff7f23768332d866f37732f815cdf1d33c7f0961246562a5c5b78c12c3ff3"},
]
[[package]]

View File

@ -43,6 +43,7 @@ import attr
from canonicaljson import encode_canonical_json
from synapse.api.constants import DeviceKeyAlgorithms
from synapse.api.errors import Codes, StoreError
from synapse.appservice import (
TransactionOneTimeKeysCount,
TransactionUnusedFallbackKeys,
@ -1708,9 +1709,16 @@ class EndToEndKeyWorkerStore(EndToEndKeyBackgroundStore, CacheInvalidationWorker
# returns unicode while encode_canonical_json returns bytes.
new_key_json = encode_canonical_json(device_keys).decode("utf-8")
if old_key_json == new_key_json:
log_kv({"Message": "Device key already stored."})
return False
if old_key_json is not None:
if old_key_json == new_key_json:
log_kv({"Message": "Device key already stored."})
return False
raise StoreError(
400,
"Device keys for this device have already been uploaded",
Codes.INVALID_PARAM,
)
self.db_pool.simple_upsert_txn(
txn,

View File

@ -149,6 +149,48 @@ class E2eKeysHandlerTestCase(unittest.HomeserverTestCase):
SynapseError,
)
def test_change_device_keys(self) -> None:
"""uploading different device keys should fail"""
local_user = "@boris:" + self.hs.hostname
device_id = "xyz"
keys1 = {
"user_id": local_user,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2"],
"keys": {"ed25519:" + device_id: "key1"},
}
keys2 = {
"user_id": local_user,
"device_id": device_id,
"algorithms": ["m.olm.curve25519-aes-sha2"],
"keys": {"ed25519:" + device_id: "key2"},
}
# initial upload succeeds
self.get_success(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys1}
)
)
# uploading the same keys again should be fine
self.get_success(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys1}
)
)
# uploading different keys should fail
self.get_failure(
self.handler.upload_keys_for_user(
local_user, device_id, {"device_keys": keys2}
),
SynapseError,
)
def test_claim_one_time_key(self) -> None:
local_user = "@boris:" + self.hs.hostname
device_id = "xyz"

View File

@ -21,6 +21,7 @@
from twisted.internet.testing import MemoryReactor
from synapse.api.errors import StoreError
from synapse.server import HomeServer
from synapse.util.clock import Clock
@ -65,6 +66,20 @@ class EndToEndKeyStoreTestCase(HomeserverTestCase):
)
self.assertFalse(changed)
def test_change_key_rejected(self) -> None:
now = 1470174257070
json = {"key": "value"}
json2 = {"key": "other"}
self.get_success(self.store.store_device("user", "device", None))
self.get_success(self.store.set_e2e_device_keys("user", "device", now, json))
self.get_failure(
self.store.set_e2e_device_keys("user", "device", now, json2),
StoreError,
)
def test_get_key_with_device_name(self) -> None:
now = 1470174257070
json = {"key": "value"}