def _get_keys(txn, batch): """Processes a batch of keys to fetch, and adds the result to `keys`.""" # batch_iter always returns tuples so it's safe to do len(batch) sql = ( "SELECT server_name, key_id, verify_key, ts_valid_until_ms " "FROM server_signature_keys WHERE 1=0" ) + " OR (server_name=? AND key_id=?)" * len(batch) txn.execute(sql, tuple(itertools.chain.from_iterable(batch))) for row in txn: server_name, key_id, key_bytes, ts_valid_until_ms = row if ts_valid_until_ms is None: # Old keys may be stored with a ts_valid_until_ms of null, # in which case we treat this as if it was set to `0`, i.e. # it won't match key requests that define a minimum # `ts_valid_until_ms`. ts_valid_until_ms = 0 res = FetchKeyResult( verify_key=decode_verify_key_bytes(key_id, bytes(key_bytes)), valid_until_ts=ts_valid_until_ms, ) keys[(server_name, key_id)] = res
def _parse_key_servers(key_servers, federation_verify_certificates): try: jsonschema.validate(key_servers, TRUSTED_KEY_SERVERS_SCHEMA) except jsonschema.ValidationError as e: raise ConfigError("Unable to parse 'trusted_key_servers': " + e.message) for server in key_servers: server_name = server["server_name"] result = TrustedKeyServer(server_name=server_name) verify_keys = server.get("verify_keys") if verify_keys is not None: result.verify_keys = {} for key_id, key_base64 in verify_keys.items(): if not is_signing_algorithm_supported(key_id): raise ConfigError( "Unsupported signing algorithm on key %s for server %s in " "trusted_key_servers" % (key_id, server_name)) try: key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) except Exception as e: raise ConfigError( "Unable to parse key %s for server %s in " "trusted_key_servers: %s" % (key_id, server_name, e)) result.verify_keys[key_id] = verify_key if not federation_verify_certificates and not server.get( "accept_keys_insecurely"): _assert_keyserver_has_verify_keys(result) yield result
def _verify_third_party_invite(self, event, auth_events): """ Validates that the invite event is authorized by a previous third-party invite. Checks that the public key, and keyserver, match those in the third party invite, and that the invite event has a signature issued using that public key. Args: event: The m.room.member join event being validated. auth_events: All relevant previous context events which may be used for authorization decisions. Return: True if the event fulfills the expectations of a previous third party invite event. """ if "third_party_invite" not in event.content: return False if "signed" not in event.content["third_party_invite"]: return False signed = event.content["third_party_invite"]["signed"] for key in {"mxid", "token"}: if key not in signed: return False token = signed["token"] invite_event = auth_events.get( (EventTypes.ThirdPartyInvite, token,) ) if not invite_event: return False if event.user_id != invite_event.user_id: return False try: public_key = invite_event.content["public_key"] if signed["mxid"] != event.state_key: return False if signed["token"] != token: return False for server, signature_block in signed["signatures"].items(): for key_name, encoded_signature in signature_block.items(): if not key_name.startswith("ed25519:"): return False verify_key = decode_verify_key_bytes( key_name, decode_base64(public_key) ) verify_signed_json(signed, server, verify_key) # We got the public key from the invite, so we know that the # correct server signed the signed bundle. # The caller is responsible for checking that the signing # server has not revoked that public key. return True return False except (KeyError, SignatureVerifyException,): return False
def get_server_keys(server_name, target, port): url = "https://%s:%i/_matrix/key/v1" % (target, port) keys = json.load(urllib2.urlopen(url)) verify_keys = {} for key_id, key_base64 in keys["verify_keys"].items(): verify_key = decode_verify_key_bytes(key_id, decode_base64(key_base64)) verify_signed_json(keys, server_name, verify_key) verify_keys[key_id] = verify_key return verify_keys
def read_perspectives(self, perspectives_servers): servers = {} for server_name, server_config in perspectives_servers.items(): for key_id, key_data in server_config["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) servers.setdefault(server_name, {})[key_id] = verify_key return servers
def get_all_server_verify_keys(self, server_name): rows = yield self._simple_select_list( table="server_signature_keys", keyvalues={"server_name": server_name}, retcols=["key_id", "verify_key"], desc="get_all_server_verify_keys", ) defer.returnValue( {row["key_id"]: decode_verify_key_bytes(row["key_id"], str(row["verify_key"])) for row in rows} )
def _get_keys(txn, batch): """Processes a batch of keys to fetch, and adds the result to `keys`.""" # batch_iter always returns tuples so it's safe to do len(batch) sql = ( "SELECT server_name, key_id, verify_key FROM server_signature_keys " "WHERE 1=0") + " OR (server_name=? AND key_id=?)" * len(batch) txn.execute(sql, tuple(itertools.chain.from_iterable(batch))) for row in txn: server_name, key_id, key_bytes = row keys[(server_name, key_id)] = decode_verify_key_bytes( key_id, bytes(key_bytes))
def read_old_signing_keys(self, old_signing_keys): keys = {} for key_id, key_data in old_signing_keys.items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_key.expired_ts = key_data["expired_ts"] keys[key_id] = verify_key else: raise ConfigError( "Unsupported signing algorithm for old key: %r" % (key_id,) ) return keys
def get_all_server_verify_keys(self, server_name): rows = yield self._simple_select_list( table="server_signature_keys", keyvalues={ "server_name": server_name, }, retcols=["key_id", "verify_key"], desc="get_all_server_verify_keys", ) defer.returnValue({ row["key_id"]: decode_verify_key_bytes(row["key_id"], str(row["verify_key"])) for row in rows })
def _get_server_verify_key(self, server_name, key_id): verify_key_bytes = yield self._simple_select_one_onecol( table="server_signature_keys", keyvalues={ "server_name": server_name, "key_id": key_id, }, retcol="verify_key", desc="_get_server_verify_key", allow_none=True, ) if verify_key_bytes: defer.returnValue( decode_verify_key_bytes(key_id, bytes(verify_key_bytes)))
def _get_server_verify_key(self, server_name, key_id): verify_key_bytes = yield self._simple_select_one_onecol( table="server_signature_keys", keyvalues={ "server_name": server_name, "key_id": key_id, }, retcol="verify_key", desc="_get_server_verify_key", allow_none=True, ) if verify_key_bytes: defer.returnValue(decode_verify_key_bytes( key_id, str(verify_key_bytes) ))
def _get_keys(txn, batch): """Processes a batch of keys to fetch, and adds the result to `keys`.""" # batch_iter always returns tuples so it's safe to do len(batch) sql = ( "SELECT server_name, key_id, verify_key FROM server_signature_keys " "WHERE 1=0" ) + " OR (server_name=? AND key_id=?)" * len(batch) txn.execute(sql, tuple(itertools.chain.from_iterable(batch))) for row in txn: server_name, key_id, key_bytes = row keys[(server_name, key_id)] = decode_verify_key_bytes( key_id, bytes(key_bytes) )
def verify_any_signature(self, data, server_hostname): if server_hostname not in data["signatures"]: raise AuthError(401, "No signature from server %s" % (server_hostname,)) for key_name, signature in data["signatures"][server_hostname].items(): key_data = yield self.hs.get_simple_http_client().get_json( "%s%s/_matrix/identity/api/v1/pubkey/%s" % (id_server_scheme, server_hostname, key_name,), ) if "public_key" not in key_data: raise AuthError(401, "No public key named %s from %s" % (key_name, server_hostname,)) verify_signed_json( data, server_hostname, decode_verify_key_bytes(key_name, decode_base64(key_data["public_key"])) ) return
def _verify_any_signature(self, data, server_hostname): if server_hostname not in data["signatures"]: raise AuthError(401, "No signature from server %s" % (server_hostname,)) for key_name, signature in data["signatures"][server_hostname].items(): key_data = yield self.simple_http_client.get_json( "%s%s/_matrix/identity/api/v1/pubkey/%s" % (id_server_scheme, server_hostname, key_name,), ) if "public_key" not in key_data: raise AuthError(401, "No public key named %s from %s" % (key_name, server_hostname,)) verify_signed_json( data, server_hostname, decode_verify_key_bytes(key_name, decode_base64(key_data["public_key"])) ) return
def read_old_signing_keys( self, old_signing_keys: Optional[JsonDict] ) -> Dict[str, "VerifyKeyWithExpiry"]: if old_signing_keys is None: return {} keys = {} for key_id, key_data in old_signing_keys.items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key: "VerifyKeyWithExpiry" = decode_verify_key_bytes( key_id, key_bytes) # type: ignore[assignment] verify_key.expired = key_data["expired_ts"] keys[key_id] = verify_key else: raise ConfigError( "Unsupported signing algorithm for old key: %r" % (key_id, )) return keys
def _check_master_key_signature( self, user_id, master_key_id, signed_master_key, stored_master_key, devices ): """Check signatures of a user's master key made by their devices. Args: user_id (string): the user whose master key is being checked master_key_id (string): the ID of the user's master key signed_master_key (dict): the user's signed master key that was uploaded stored_master_key (dict): our previously-stored copy of the user's master key devices (iterable(dict)): the user's devices Returns: list[SignatureListItem]: a list of signatures to store Raises: SynapseError: if a signature is invalid """ # for each device that signed the master key, check the signature. master_key_signature_list = [] sigs = signed_master_key["signatures"] for signing_key_id, signature in sigs[user_id].items(): _, signing_device_id = signing_key_id.split(":", 1) if ( signing_device_id not in devices or signing_key_id not in devices[signing_device_id]["keys"] ): # signed by an unknown device, or the # device does not have the key raise SynapseError(400, "Invalid signature", Codes.INVALID_SIGNATURE) # get the key and check the signature pubkey = devices[signing_device_id]["keys"][signing_key_id] verify_key = decode_verify_key_bytes(signing_key_id, decode_base64(pubkey)) _check_device_signature( user_id, verify_key, signed_master_key, stored_master_key ) master_key_signature_list.append( SignatureListItem(signing_key_id, user_id, master_key_id, signature) ) return master_key_signature_list
def get_verify_key_from_cross_signing_key(key_info): """Get the key ID and signedjson verify key from a cross-signing key dict Args: key_info (dict): a cross-signing key dict, which must have a "keys" property that has exactly one item in it Returns: (str, VerifyKey): the key ID and verify key for the cross-signing key """ # make sure that exactly one key is provided if "keys" not in key_info: raise ValueError("Invalid key") keys = key_info["keys"] if len(keys) != 1: raise ValueError("Invalid key") # and return that one key for key_id, key_data in keys.items(): return key_id, decode_verify_key_bytes(key_id, decode_base64(key_data))
def get_verify_key_from_cross_signing_key( key_info: Mapping[str, Any]) -> Tuple[str, VerifyKey]: """Get the key ID and signedjson verify key from a cross-signing key dict Args: key_info: a cross-signing key dict, which must have a "keys" property that has exactly one item in it Returns: the key ID and verify key for the cross-signing key """ # make sure that a `keys` field is provided if "keys" not in key_info: raise ValueError("Invalid key") keys = key_info["keys"] # and that it contains exactly one key if len(keys) == 1: key_id, key_data = next(iter(keys.items())) return key_id, decode_verify_key_bytes(key_id, decode_base64(key_data)) else: raise ValueError("Invalid key")
def _verify_any_signature(self, data, server_hostname): if server_hostname not in data["signatures"]: raise AuthError( 401, "No signature from server %s" % (server_hostname, )) for key_name, signature in data["signatures"][server_hostname].items(): try: key_data = yield self.blacklisting_http_client.get_json( "%s%s/_matrix/identity/api/v1/pubkey/%s" % (id_server_scheme, server_hostname, key_name)) except TimeoutError: raise SynapseError(500, "Timed out contacting identity server") if "public_key" not in key_data: raise AuthError( 401, "No public key named %s from %s" % (key_name, server_hostname)) verify_signed_json( data, server_hostname, decode_verify_key_bytes(key_name, decode_base64(key_data["public_key"])), ) return
def process_v2_response( self, from_server, response_json, requested_ids=[], ): """Parse a 'Server Keys' structure from the result of a /key request This is used to parse either the entirety of the response from GET /_matrix/key/v2/server, or a single entry from the list returned by POST /_matrix/key/v2/query. Checks that each signature in the response that claims to come from the origin server is valid. (Does not check that there actually is such a signature, for some reason.) Stores the json in server_keys_json so that it can be used for future responses to /_matrix/key/v2/query. Args: from_server (str): the name of the server producing this result: either the origin server for a /_matrix/key/v2/server request, or the notary for a /_matrix/key/v2/query. response_json (dict): the json-decoded Server Keys response object requested_ids (iterable[str]): a list of the key IDs that were requested. We will store the json for these key ids as well as any that are actually in the response Returns: Deferred[dict[str, nacl.signing.VerifyKey]]: map from key_id to key object """ time_now_ms = self.clock.time_msec() response_keys = {} verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] 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 old_verify_keys = {} for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_key.expired = key_data["expired_ts"] verify_key.time_added = time_now_ms old_verify_keys[key_id] = verify_key server_name = response_json["server_name"] for key_id in response_json["signatures"].get(server_name, {}): if key_id not in response_json["verify_keys"]: raise KeyLookupError( "Key response must include verification keys for all" " signatures") if key_id in verify_keys: verify_signed_json(response_json, server_name, verify_keys[key_id]) signed_key_json = sign_json( response_json, self.config.server_name, self.config.signing_key[0], ) signed_key_json_bytes = encode_canonical_json(signed_key_json) ts_valid_until_ms = signed_key_json[u"valid_until_ts"] updated_key_ids = set(requested_ids) updated_key_ids.update(verify_keys) updated_key_ids.update(old_verify_keys) response_keys.update(verify_keys) response_keys.update(old_verify_keys) yield logcontext.make_deferred_yieldable( defer.gatherResults( [ run_in_background( self.store.store_server_keys_json, server_name=server_name, key_id=key_id, from_server=from_server, ts_now_ms=time_now_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=signed_key_json_bytes, ) for key_id in updated_key_ids ], consumeErrors=True, ).addErrback(unwrapFirstError)) defer.returnValue(response_keys)
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_server_context_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 ValueError("Key response not signed by remote server") if "tls_certificate" not in response: raise ValueError("Key response missing TLS certificate") tls_certificate_b64 = response["tls_certificate"] if encode_base64(x509_certificate_bytes) != tls_certificate_b64: raise ValueError("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 ValueError( "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 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_server_context_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)
async def process_v2_response( self, from_server: str, response_json: JsonDict, time_added_ms: int) -> Dict[str, FetchKeyResult]: """Parse a 'Server Keys' structure from the result of a /key request This is used to parse either the entirety of the response from GET /_matrix/key/v2/server, or a single entry from the list returned by POST /_matrix/key/v2/query. Checks that each signature in the response that claims to come from the origin server is valid, and that there is at least one such signature. Stores the json in server_keys_json so that it can be used for future responses to /_matrix/key/v2/query. Args: from_server: the name of the server producing this result: either the origin server for a /_matrix/key/v2/server request, or the notary for a /_matrix/key/v2/query. response_json: the json-decoded Server Keys response object time_added_ms: the timestamp to record in server_keys_json Returns: Map from key_id to result object """ ts_valid_until_ms = response_json["valid_until_ts"] # start by extracting the keys from the response, since they may be required # to validate the signature on the response. verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_keys[key_id] = FetchKeyResult( verify_key=verify_key, valid_until_ts=ts_valid_until_ms) server_name = response_json["server_name"] verified = False for key_id in response_json["signatures"].get(server_name, {}): key = verify_keys.get(key_id) if not key: # the key may not be present in verify_keys if: # * we got the key from the notary server, and: # * the key belongs to the notary server, and: # * the notary server is using a different key to sign notary # responses. continue verify_signed_json(response_json, server_name, key.verify_key) verified = True break if not verified: raise KeyLookupError( "Key response for %s is not signed by the origin server" % (server_name, )) for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_keys[key_id] = FetchKeyResult( verify_key=verify_key, valid_until_ts=key_data["expired_ts"]) key_json_bytes = encode_canonical_json(response_json) await make_deferred_yieldable( defer.gatherResults( [ run_in_background( self.store.store_server_keys_json, server_name=server_name, key_id=key_id, from_server=from_server, ts_now_ms=time_added_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=key_json_bytes, ) for key_id in verify_keys ], consumeErrors=True, ).addErrback(unwrapFirstError)) return verify_keys
def test_decode_verify_invalid_algorithm(self): with self.assertRaises(Exception): decode_verify_key_bytes("not a valid alg", self.verify_key)
def process_v2_response(self, from_server, response_json, time_added_ms): """Parse a 'Server Keys' structure from the result of a /key request This is used to parse either the entirety of the response from GET /_matrix/key/v2/server, or a single entry from the list returned by POST /_matrix/key/v2/query. Checks that each signature in the response that claims to come from the origin server is valid, and that there is at least one such signature. Stores the json in server_keys_json so that it can be used for future responses to /_matrix/key/v2/query. Args: from_server (str): the name of the server producing this result: either the origin server for a /_matrix/key/v2/server request, or the notary for a /_matrix/key/v2/query. response_json (dict): the json-decoded Server Keys response object time_added_ms (int): the timestamp to record in server_keys_json Returns: Deferred[dict[str, FetchKeyResult]]: map from key_id to result object """ ts_valid_until_ms = response_json["valid_until_ts"] # start by extracting the keys from the response, since they may be required # to validate the signature on the response. verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_keys[key_id] = FetchKeyResult( verify_key=verify_key, valid_until_ts=ts_valid_until_ms) server_name = response_json["server_name"] verified = False for key_id in response_json["signatures"].get(server_name, {}): # each of the keys used for the signature must be present in the response # json. key = verify_keys.get(key_id) if not key: raise KeyLookupError( "Key response is signed by key id %s:%s but that key is not " "present in the response" % (server_name, key_id)) verify_signed_json(response_json, server_name, key.verify_key) verified = True if not verified: raise KeyLookupError( "Key response for %s is not signed by the origin server" % (server_name, )) for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_keys[key_id] = FetchKeyResult( verify_key=verify_key, valid_until_ts=key_data["expired_ts"]) # re-sign the json with our own key, so that it is ready if we are asked to # give it out as a notary server signed_key_json = sign_json(response_json, self.config.server_name, self.config.signing_key[0]) signed_key_json_bytes = encode_canonical_json(signed_key_json) yield make_deferred_yieldable( defer.gatherResults( [ run_in_background( self.store.store_server_keys_json, server_name=server_name, key_id=key_id, from_server=from_server, ts_now_ms=time_added_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=signed_key_json_bytes, ) for key_id in verify_keys ], consumeErrors=True, ).addErrback(unwrapFirstError)) defer.returnValue(verify_keys)
def process_v2_response( self, from_server, response_json, requested_ids=[], ): """Parse a 'Server Keys' structure from the result of a /key request This is used to parse either the entirety of the response from GET /_matrix/key/v2/server, or a single entry from the list returned by POST /_matrix/key/v2/query. Checks that each signature in the response that claims to come from the origin server is valid. (Does not check that there actually is such a signature, for some reason.) Stores the json in server_keys_json so that it can be used for future responses to /_matrix/key/v2/query. Args: from_server (str): the name of the server producing this result: either the origin server for a /_matrix/key/v2/server request, or the notary for a /_matrix/key/v2/query. response_json (dict): the json-decoded Server Keys response object requested_ids (iterable[str]): a list of the key IDs that were requested. We will store the json for these key ids as well as any that are actually in the response Returns: Deferred[dict[str, nacl.signing.VerifyKey]]: map from key_id to key object """ time_now_ms = self.clock.time_msec() response_keys = {} verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] 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 old_verify_keys = {} for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_key.expired = key_data["expired_ts"] verify_key.time_added = time_now_ms old_verify_keys[key_id] = verify_key server_name = response_json["server_name"] for key_id in response_json["signatures"].get(server_name, {}): if key_id not in response_json["verify_keys"]: raise KeyLookupError( "Key response must include verification keys for all" " signatures" ) if key_id in verify_keys: verify_signed_json( response_json, server_name, verify_keys[key_id] ) signed_key_json = sign_json( response_json, self.config.server_name, self.config.signing_key[0], ) signed_key_json_bytes = encode_canonical_json(signed_key_json) ts_valid_until_ms = signed_key_json[u"valid_until_ts"] updated_key_ids = set(requested_ids) updated_key_ids.update(verify_keys) updated_key_ids.update(old_verify_keys) response_keys.update(verify_keys) response_keys.update(old_verify_keys) yield logcontext.make_deferred_yieldable(defer.gatherResults( [ run_in_background( self.store.store_server_keys_json, server_name=server_name, key_id=key_id, from_server=from_server, ts_now_ms=time_now_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=signed_key_json_bytes, ) for key_id in updated_key_ids ], consumeErrors=True, ).addErrback(unwrapFirstError)) defer.returnValue(response_keys)
def process_v2_response(self, from_server, response_json, requested_ids=[], only_from_server=True): time_now_ms = self.clock.time_msec() response_keys = {} verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] 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 old_verify_keys = {} for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_key.expired = key_data["expired_ts"] verify_key.time_added = time_now_ms old_verify_keys[key_id] = verify_key results = {} server_name = response_json["server_name"] if only_from_server: if server_name != from_server: raise ValueError( "Expected a response for server %r not %r" % ( from_server, server_name ) ) for key_id in response_json["signatures"].get(server_name, {}): if key_id not in response_json["verify_keys"]: raise ValueError( "Key response must include verification keys for all" " signatures" ) if key_id in verify_keys: verify_signed_json( response_json, server_name, verify_keys[key_id] ) signed_key_json = sign_json( response_json, self.config.server_name, self.config.signing_key[0], ) signed_key_json_bytes = encode_canonical_json(signed_key_json) ts_valid_until_ms = signed_key_json[u"valid_until_ts"] updated_key_ids = set(requested_ids) updated_key_ids.update(verify_keys) updated_key_ids.update(old_verify_keys) response_keys.update(verify_keys) response_keys.update(old_verify_keys) yield defer.gatherResults( [ preserve_fn(self.store.store_server_keys_json)( server_name=server_name, key_id=key_id, from_server=server_name, ts_now_ms=time_now_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=signed_key_json_bytes, ) for key_id in updated_key_ids ], consumeErrors=True, ).addErrback(unwrapFirstError) results[server_name] = response_keys defer.returnValue(results)
def process_v2_response(self, from_server, response_json, requested_ids=[], only_from_server=True): time_now_ms = self.clock.time_msec() response_keys = {} verify_keys = {} for key_id, key_data in response_json["verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] 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 old_verify_keys = {} for key_id, key_data in response_json["old_verify_keys"].items(): if is_signing_algorithm_supported(key_id): key_base64 = key_data["key"] key_bytes = decode_base64(key_base64) verify_key = decode_verify_key_bytes(key_id, key_bytes) verify_key.expired = key_data["expired_ts"] verify_key.time_added = time_now_ms old_verify_keys[key_id] = verify_key results = {} server_name = response_json["server_name"] if only_from_server: if server_name != from_server: raise KeyLookupError( "Expected a response for server %r not %r" % ( from_server, server_name ) ) for key_id in response_json["signatures"].get(server_name, {}): if key_id not in response_json["verify_keys"]: raise KeyLookupError( "Key response must include verification keys for all" " signatures" ) if key_id in verify_keys: verify_signed_json( response_json, server_name, verify_keys[key_id] ) signed_key_json = sign_json( response_json, self.config.server_name, self.config.signing_key[0], ) signed_key_json_bytes = encode_canonical_json(signed_key_json) ts_valid_until_ms = signed_key_json[u"valid_until_ts"] updated_key_ids = set(requested_ids) updated_key_ids.update(verify_keys) updated_key_ids.update(old_verify_keys) response_keys.update(verify_keys) response_keys.update(old_verify_keys) yield logcontext.make_deferred_yieldable(defer.gatherResults( [ run_in_background( self.store.store_server_keys_json, server_name=server_name, key_id=key_id, from_server=server_name, ts_now_ms=time_now_ms, ts_expires_ms=ts_valid_until_ms, key_json_bytes=signed_key_json_bytes, ) for key_id in updated_key_ids ], consumeErrors=True, ).addErrback(unwrapFirstError)) results[server_name] = response_keys defer.returnValue(results)
def _do_invite(self, roomid, userstring): if not userstring.startswith("@") and self._is_on("complete_usernames"): # TODO: Update to use v2 Identity Service API endpoint url = self._identityServerUrl() + "/_matrix/identity/api/v1/lookup" json_res = yield self.http_client.do_request( "GET", url, qparams={"medium": "email", "address": userstring} ) mxid = None if "mxid" in json_res and "signatures" in json_res: # TODO: Update to use v2 Identity Service API endpoint url = ( self._identityServerUrl() + "/_matrix/identity/api/v1/pubkey/ed25519" ) pubKey = None pubKeyObj = yield self.http_client.do_request("GET", url) if "public_key" in pubKeyObj: pubKey = decode_verify_key_bytes( NACL_ED25519, binascii.unhexlify(pubKeyObj["public_key"]) ) else: print("No public key found in pubkey response!") sigValid = False if pubKey: for signame in json_res["signatures"]: if signame not in TRUSTED_ID_SERVERS: print( "Ignoring signature from untrusted server %s" % (signame) ) else: try: verify_signed_json(json_res, signame, pubKey) sigValid = True print( "Mapping %s -> %s correctly signed by %s" % (userstring, json_res["mxid"], signame) ) break except SignatureVerifyException as e: print("Invalid signature from %s" % (signame)) print(e) if sigValid: print("Resolved 3pid %s to %s" % (userstring, json_res["mxid"])) mxid = json_res["mxid"] else: print( "Got association for %s but couldn't verify signature" % (userstring) ) if not mxid: mxid = "@" + userstring + ":" + self._domain() self._do_membership_change(roomid, "invite", mxid)