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 2 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)
hawkowl marked this conversation as resolved.
Show resolved Hide resolved
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