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

Remove fetching keys via the deprecated v1 kex method #4120

Merged
merged 3 commits into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4120.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Synapse will no longer fetch keys using the fallback deprecated v1 key exchange method and will now always use v2.
8 changes: 5 additions & 3 deletions synapse/crypto/keyclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import logging

from six.moves import urllib

from canonicaljson import json

from twisted.internet import defer, reactor
Expand All @@ -28,15 +30,15 @@

logger = logging.getLogger(__name__)

KEY_API_V1 = b"/_matrix/key/v1/"
KEY_API_V2 = "/_matrix/key/v2/server/%s"


@defer.inlineCallbacks
def fetch_server_key(server_name, tls_client_options_factory, path=KEY_API_V1):
def fetch_server_key(server_name, tls_client_options_factory, key_id):
"""Fetch the keys for a remote server."""

factory = SynapseKeyClientFactory()
factory.path = path
factory.path = KEY_API_V2 % (urllib.parse.quote(key_id), )
factory.host = server_name
endpoint = matrix_federation_endpoint(
reactor, server_name, tls_client_options_factory, timeout=30
Expand Down
110 changes: 7 additions & 103 deletions synapse/crypto/keyring.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
# Copyright 2017 New Vector Ltd.
# Copyright 2017, 2018 New Vector Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -18,8 +18,6 @@
import logging
from collections import namedtuple

from six.moves import urllib

from signedjson.key import (
decode_verify_key_bytes,
encode_verify_key_base64,
Expand Down Expand Up @@ -395,32 +393,13 @@ def get_key(perspective_name, perspective_keys):

@defer.inlineCallbacks
def get_keys_from_server(self, server_name_and_key_ids):
@defer.inlineCallbacks
def get_key(server_name, key_ids):
keys = None
try:
keys = yield self.get_server_verify_key_v2_direct(
server_name, key_ids
)
except Exception as e:
logger.info(
"Unable to get key %r for %r directly: %s %s",
key_ids, server_name,
type(e).__name__, str(e),
)

if not keys:
keys = yield self.get_server_verify_key_v1_direct(
server_name, key_ids
)

keys = {server_name: keys}

defer.returnValue(keys)

results = yield logcontext.make_deferred_yieldable(defer.gatherResults(
[
run_in_background(get_key, server_name, key_ids)
run_in_background(
self.get_server_verify_key_v2_direct,
server_name,
key_ids,
)
for server_name, key_ids in server_name_and_key_ids
],
consumeErrors=True,
Expand Down Expand Up @@ -525,10 +504,7 @@ def get_server_verify_key_v2_direct(self, server_name, key_ids):
continue

(response, tls_certificate) = yield fetch_server_key(
server_name, self.hs.tls_client_options_factory,
path=("/_matrix/key/v2/server/%s" % (
urllib.parse.quote(requested_key_id),
)).encode("ascii"),
server_name, self.hs.tls_client_options_factory, requested_key_id
)

if (u"signatures" not in response
Expand Down Expand Up @@ -657,78 +633,6 @@ def process_v2_response(self, from_server, response_json,

defer.returnValue(results)

@defer.inlineCallbacks
def get_server_verify_key_v1_direct(self, server_name, key_ids):
"""Finds a verification key for the server with one of the key ids.
Args:
server_name (str): The name of the server to fetch a key for.
keys_ids (list of str): The key_ids to check for.
"""

# Try to fetch the key from the remote server.

(response, tls_certificate) = yield fetch_server_key(
server_name, self.hs.tls_client_options_factory
)

# Check the response.

x509_certificate_bytes = crypto.dump_certificate(
crypto.FILETYPE_ASN1, tls_certificate
)

if ("signatures" not in response
or server_name not in response["signatures"]):
raise KeyLookupError("Key response not signed by remote server")

if "tls_certificate" not in response:
raise KeyLookupError("Key response missing TLS certificate")

tls_certificate_b64 = response["tls_certificate"]

if encode_base64(x509_certificate_bytes) != tls_certificate_b64:
raise KeyLookupError("TLS certificate doesn't match")

# Cache the result in the datastore.

time_now_ms = self.clock.time_msec()

verify_keys = {}
for key_id, key_base64 in response["verify_keys"].items():
if is_signing_algorithm_supported(key_id):
key_bytes = decode_base64(key_base64)
verify_key = decode_verify_key_bytes(key_id, key_bytes)
verify_key.time_added = time_now_ms
verify_keys[key_id] = verify_key

for key_id in response["signatures"][server_name]:
if key_id not in response["verify_keys"]:
raise KeyLookupError(
"Key response must include verification keys for all"
" signatures"
)
if key_id in verify_keys:
verify_signed_json(
response,
server_name,
verify_keys[key_id]
)

yield self.store.store_server_certificate(
server_name,
server_name,
time_now_ms,
tls_certificate,
)

yield self.store_keys(
server_name=server_name,
from_server=server_name,
verify_keys=verify_keys,
)

defer.returnValue(verify_keys)

def store_keys(self, server_name, from_server, verify_keys):
"""Store a collection of verify keys for a given server
Args:
Expand Down