Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit edae20f

Browse files
authored
Improve robustness when handling a perspective key response by deduplicating received server keys. (#15423)
* Change `store_server_verify_keys` to take a `Mapping[(str, str), FKR]` This is because we already can't handle duplicate keys — leads to cardinality violation * Newsfile Signed-off-by: Olivier Wilkinson (reivilibre) <[email protected]> --------- Signed-off-by: Olivier Wilkinson (reivilibre) <[email protected]>
1 parent 38272be commit edae20f

File tree

6 files changed

+43
-28
lines changed

6 files changed

+43
-28
lines changed

changelog.d/15423.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve robustness when handling a perspective key response by deduplicating received server keys.

synapse/crypto/keyring.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ async def get_server_verify_key_v2_indirect(
721721
)
722722

723723
keys: Dict[str, Dict[str, FetchKeyResult]] = {}
724-
added_keys: List[Tuple[str, str, FetchKeyResult]] = []
724+
added_keys: Dict[Tuple[str, str], FetchKeyResult] = {}
725725

726726
time_now_ms = self.clock.time_msec()
727727

@@ -752,9 +752,27 @@ async def get_server_verify_key_v2_indirect(
752752
# we continue to process the rest of the response
753753
continue
754754

755-
added_keys.extend(
756-
(server_name, key_id, key) for key_id, key in processed_response.items()
757-
)
755+
for key_id, key in processed_response.items():
756+
dict_key = (server_name, key_id)
757+
if dict_key in added_keys:
758+
already_present_key = added_keys[dict_key]
759+
logger.warning(
760+
"Duplicate server keys for %s (%s) from perspective %s (%r, %r)",
761+
server_name,
762+
key_id,
763+
perspective_name,
764+
already_present_key,
765+
key,
766+
)
767+
768+
if already_present_key.valid_until_ts > key.valid_until_ts:
769+
# Favour the entry with the largest valid_until_ts,
770+
# as `old_verify_keys` are also collected from this
771+
# response.
772+
continue
773+
774+
added_keys[dict_key] = key
775+
758776
keys.setdefault(server_name, {}).update(processed_response)
759777

760778
await self.store.store_server_verify_keys(

synapse/storage/databases/main/keys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import itertools
1717
import logging
18-
from typing import Any, Dict, Iterable, List, Optional, Tuple
18+
from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple
1919

2020
from signedjson.key import decode_verify_key_bytes
2121

@@ -95,7 +95,7 @@ async def store_server_verify_keys(
9595
self,
9696
from_server: str,
9797
ts_added_ms: int,
98-
verify_keys: Iterable[Tuple[str, str, FetchKeyResult]],
98+
verify_keys: Mapping[Tuple[str, str], FetchKeyResult],
9999
) -> None:
100100
"""Stores NACL verification keys for remote servers.
101101
Args:
@@ -108,7 +108,7 @@ async def store_server_verify_keys(
108108
key_values = []
109109
value_values = []
110110
invalidations = []
111-
for server_name, key_id, fetch_result in verify_keys:
111+
for (server_name, key_id), fetch_result in verify_keys.items():
112112
key_values.append((server_name, key_id))
113113
value_values.append(
114114
(

tests/crypto/test_keyring.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def test_verify_json_for_server(self) -> None:
193193
r = self.hs.get_datastores().main.store_server_verify_keys(
194194
"server9",
195195
int(time.time() * 1000),
196-
[("server9", get_key_id(key1), FetchKeyResult(get_verify_key(key1), 1000))],
196+
{("server9", get_key_id(key1)): FetchKeyResult(get_verify_key(key1), 1000)},
197197
)
198198
self.get_success(r)
199199

@@ -291,7 +291,7 @@ def test_verify_json_for_server_with_null_valid_until_ms(self) -> None:
291291
# None is not a valid value in FetchKeyResult, but we're abusing this
292292
# API to insert null values into the database. The nulls get converted
293293
# to 0 when fetched in KeyStore.get_server_verify_keys.
294-
[("server9", get_key_id(key1), FetchKeyResult(get_verify_key(key1), None))], # type: ignore[arg-type]
294+
{("server9", get_key_id(key1)): FetchKeyResult(get_verify_key(key1), None)}, # type: ignore[arg-type]
295295
)
296296
self.get_success(r)
297297

tests/storage/test_keys.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ def test_get_server_verify_keys(self) -> None:
4646
store.store_server_verify_keys(
4747
"from_server",
4848
10,
49-
[
50-
("server1", key_id_1, FetchKeyResult(KEY_1, 100)),
51-
("server1", key_id_2, FetchKeyResult(KEY_2, 200)),
52-
],
49+
{
50+
("server1", key_id_1): FetchKeyResult(KEY_1, 100),
51+
("server1", key_id_2): FetchKeyResult(KEY_2, 200),
52+
},
5353
)
5454
)
5555

@@ -90,10 +90,10 @@ def test_cache(self) -> None:
9090
store.store_server_verify_keys(
9191
"from_server",
9292
0,
93-
[
94-
("srv1", key_id_1, FetchKeyResult(KEY_1, 100)),
95-
("srv1", key_id_2, FetchKeyResult(KEY_2, 200)),
96-
],
93+
{
94+
("srv1", key_id_1): FetchKeyResult(KEY_1, 100),
95+
("srv1", key_id_2): FetchKeyResult(KEY_2, 200),
96+
},
9797
)
9898
)
9999

@@ -119,7 +119,7 @@ def test_cache(self) -> None:
119119
signedjson.key.generate_signing_key("key2")
120120
)
121121
d = store.store_server_verify_keys(
122-
"from_server", 10, [("srv1", key_id_2, FetchKeyResult(new_key_2, 300))]
122+
"from_server", 10, {("srv1", key_id_2): FetchKeyResult(new_key_2, 300)}
123123
)
124124
self.get_success(d)
125125

tests/unittest.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -793,16 +793,12 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
793793
hs.get_datastores().main.store_server_verify_keys(
794794
from_server=self.OTHER_SERVER_NAME,
795795
ts_added_ms=clock.time_msec(),
796-
verify_keys=[
797-
(
798-
self.OTHER_SERVER_NAME,
799-
verify_key_id,
800-
FetchKeyResult(
801-
verify_key=verify_key,
802-
valid_until_ts=clock.time_msec() + 10000,
803-
),
804-
)
805-
],
796+
verify_keys={
797+
(self.OTHER_SERVER_NAME, verify_key_id): FetchKeyResult(
798+
verify_key=verify_key,
799+
valid_until_ts=clock.time_msec() + 10000,
800+
),
801+
},
806802
)
807803
)
808804

0 commit comments

Comments
 (0)