|
|
|
@ -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. |
|
|
|
@ -18,8 +18,6 @@ import hashlib |
|
|
|
|
import logging |
|
|
|
|
from collections import namedtuple |
|
|
|
|
|
|
|
|
|
from six.moves import urllib |
|
|
|
|
|
|
|
|
|
from signedjson.key import ( |
|
|
|
|
decode_verify_key_bytes, |
|
|
|
|
encode_verify_key_base64, |
|
|
|
@ -395,32 +393,13 @@ class Keyring(object): |
|
|
|
|
|
|
|
|
|
@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, |
|
|
|
@ -525,10 +504,7 @@ class Keyring(object): |
|
|
|
|
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 |
|
|
|
@ -657,78 +633,6 @@ class Keyring(object): |
|
|
|
|
|
|
|
|
|
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: |
|
|
|
|