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

Commit e002fae

Browse files
authored
Fetch verify key locally rather than trying to do so over federation if origin and host are the same. (#11129)
* add tests for fetching key locally * add logic to check if origin server is same as host and fetch verify key locally rather than over federation * add changelog * slight refactor, add docstring, change changelog entry * Make changelog entry one line * remove verify_json_locally and push locality check to process_request, add function process_request_locally * remove leftover code reference * refactor to add common call to 'verify_json and associated handling code * add type hint to process_json * add some docstrings + very slight refactor
1 parent adc0d35 commit e002fae

File tree

3 files changed

+58
-29
lines changed

3 files changed

+58
-29
lines changed

changelog.d/11129.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix long-standing bug where verification requests could fail in certain cases if whitelist was in place but did not include your own homeserver.

synapse/crypto/keyring.py

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from signedjson.key import (
2323
decode_verify_key_bytes,
2424
encode_verify_key_base64,
25+
get_verify_key,
2526
is_signing_algorithm_supported,
2627
)
2728
from signedjson.sign import (
@@ -30,6 +31,7 @@
3031
signature_ids,
3132
verify_signed_json,
3233
)
34+
from signedjson.types import VerifyKey
3335
from unpaddedbase64 import decode_base64
3436

3537
from twisted.internet import defer
@@ -177,6 +179,8 @@ def __init__(
177179
clock=hs.get_clock(),
178180
process_batch_callback=self._inner_fetch_key_requests,
179181
)
182+
self.verify_key = get_verify_key(hs.signing_key)
183+
self.hostname = hs.hostname
180184

181185
async def verify_json_for_server(
182186
self,
@@ -196,6 +200,7 @@ async def verify_json_for_server(
196200
validity_time: timestamp at which we require the signing key to
197201
be valid. (0 implies we don't care)
198202
"""
203+
199204
request = VerifyJsonRequest.from_json_object(
200205
server_name,
201206
json_object,
@@ -262,6 +267,11 @@ async def process_request(self, verify_request: VerifyJsonRequest) -> None:
262267
Codes.UNAUTHORIZED,
263268
)
264269

270+
# If we are the originating server don't fetch verify key for self over federation
271+
if verify_request.server_name == self.hostname:
272+
await self._process_json(self.verify_key, verify_request)
273+
return
274+
265275
# Add the keys we need to verify to the queue for retrieval. We queue
266276
# up requests for the same server so we don't end up with many in flight
267277
# requests for the same keys.
@@ -285,35 +295,8 @@ async def process_request(self, verify_request: VerifyJsonRequest) -> None:
285295
if key_result.valid_until_ts < verify_request.minimum_valid_until_ts:
286296
continue
287297

288-
verify_key = key_result.verify_key
289-
json_object = verify_request.get_json_object()
290-
try:
291-
verify_signed_json(
292-
json_object,
293-
verify_request.server_name,
294-
verify_key,
295-
)
296-
verified = True
297-
except SignatureVerifyException as e:
298-
logger.debug(
299-
"Error verifying signature for %s:%s:%s with key %s: %s",
300-
verify_request.server_name,
301-
verify_key.alg,
302-
verify_key.version,
303-
encode_verify_key_base64(verify_key),
304-
str(e),
305-
)
306-
raise SynapseError(
307-
401,
308-
"Invalid signature for server %s with key %s:%s: %s"
309-
% (
310-
verify_request.server_name,
311-
verify_key.alg,
312-
verify_key.version,
313-
str(e),
314-
),
315-
Codes.UNAUTHORIZED,
316-
)
298+
await self._process_json(key_result.verify_key, verify_request)
299+
verified = True
317300

318301
if not verified:
319302
raise SynapseError(
@@ -322,6 +305,39 @@ async def process_request(self, verify_request: VerifyJsonRequest) -> None:
322305
Codes.UNAUTHORIZED,
323306
)
324307

308+
async def _process_json(
309+
self, verify_key: VerifyKey, verify_request: VerifyJsonRequest
310+
) -> None:
311+
"""Processes the `VerifyJsonRequest`. Raises if the signature can't be
312+
verified.
313+
"""
314+
try:
315+
verify_signed_json(
316+
verify_request.get_json_object(),
317+
verify_request.server_name,
318+
verify_key,
319+
)
320+
except SignatureVerifyException as e:
321+
logger.debug(
322+
"Error verifying signature for %s:%s:%s with key %s: %s",
323+
verify_request.server_name,
324+
verify_key.alg,
325+
verify_key.version,
326+
encode_verify_key_base64(verify_key),
327+
str(e),
328+
)
329+
raise SynapseError(
330+
401,
331+
"Invalid signature for server %s with key %s:%s: %s"
332+
% (
333+
verify_request.server_name,
334+
verify_key.alg,
335+
verify_key.version,
336+
str(e),
337+
),
338+
Codes.UNAUTHORIZED,
339+
)
340+
325341
async def _inner_fetch_key_requests(
326342
self, requests: List[_FetchKeyRequest]
327343
) -> Dict[str, Dict[str, FetchKeyResult]]:

tests/crypto/test_keyring.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ def test_verify_json_for_server(self):
197197
# self.assertFalse(d.called)
198198
self.get_success(d)
199199

200+
def test_verify_for_server_locally(self):
201+
"""Ensure that locally signed JSON can be verified without fetching keys
202+
over federation
203+
"""
204+
kr = keyring.Keyring(self.hs)
205+
json1 = {}
206+
signedjson.sign.sign_json(json1, self.hs.hostname, self.hs.signing_key)
207+
208+
# Test that verify_json_for_server succeeds on a object signed by ourselves
209+
d = kr.verify_json_for_server(self.hs.hostname, json1, 0)
210+
self.get_success(d)
211+
200212
def test_verify_json_for_server_with_null_valid_until_ms(self):
201213
"""Tests that we correctly handle key requests for keys we've stored
202214
with a null `ts_valid_until_ms`

0 commit comments

Comments
 (0)