Esempio n. 1
0
def _check_cross_signing_key(key, user_id, key_type, signing_key=None):
    """Check a cross-signing key uploaded by a user.  Performs some basic sanity
    checking, and ensures that it is signed, if a signature is required.

    Args:
        key (dict): the key data to verify
        user_id (str): the user whose key is being checked
        key_type (str): the type of key that the key should be
        signing_key (VerifyKey): (optional) the signing key that the key should
            be signed with.  If omitted, signatures will not be checked.
    """
    if (
        key.get("user_id") != user_id
        or key_type not in key.get("usage", [])
        or len(key.get("keys", {})) != 1
    ):
        raise SynapseError(400, ("Invalid %s key" % (key_type,)), Codes.INVALID_PARAM)

    if signing_key:
        try:
            verify_signed_json(key, user_id, signing_key)
        except SignatureVerifyException:
            raise SynapseError(
                400, ("Invalid signature on %s key" % key_type), Codes.INVALID_SIGNATURE
            )
Esempio n. 2
0
def main():

    parser = argparse.ArgumentParser()
    parser.add_argument("signature_name")
    parser.add_argument("input_json",
                        nargs="?",
                        type=argparse.FileType('r'),
                        default=sys.stdin)

    args = parser.parse_args()
    logging.basicConfig()

    server_name = args.signature_name
    keys = {}
    for target, port in get_targets(server_name):
        try:
            keys = get_server_keys(server_name, target, port)
            print("Using keys from https://%s:%s/_matrix/key/v1" %
                  (target, port))
            write_signing_keys(sys.stdout, keys.values())
            break
        except Exception:
            logging.exception("Error talking to %s:%s", target, port)

    json_to_check = json.load(args.input_json)
    print("Checking JSON:")
    for key_id in json_to_check["signatures"][args.signature_name]:
        try:
            key = keys[key_id]
            verify_signed_json(json_to_check, args.signature_name, key)
            print("PASS %s" % (key_id, ))
        except Exception:
            logging.exception("Check for key %s failed" % (key_id, ))
            print("FAIL %s" % (key_id, ))
Esempio n. 3
0
 async def _process_json(self, verify_key: VerifyKey,
                         verify_request: VerifyJsonRequest) -> None:
     """Processes the `VerifyJsonRequest`. Raises if the signature can't be
     verified.
     """
     try:
         verify_signed_json(
             verify_request.get_json_object(),
             verify_request.server_name,
             verify_key,
         )
     except SignatureVerifyException as e:
         logger.debug(
             "Error verifying signature for %s:%s:%s with key %s: %s",
             verify_request.server_name,
             verify_key.alg,
             verify_key.version,
             encode_verify_key_base64(verify_key),
             str(e),
         )
         raise SynapseError(
             401,
             "Invalid signature for server %s with key %s:%s: %s" % (
                 verify_request.server_name,
                 verify_key.alg,
                 verify_key.version,
                 str(e),
             ),
             Codes.UNAUTHORIZED,
         )
Esempio n. 4
0
    def _validate_perspectives_response(self, key_server: TrustedKeyServer,
                                        response: JsonDict) -> None:
        """Optionally check the signature on the result of a /key/query request

        Args:
            key_server: the notary server that produced this result

            response: the json-decoded Server Keys response object
        """
        perspective_name = key_server.server_name
        perspective_keys = key_server.verify_keys

        if perspective_keys is None:
            # signature checking is disabled on this server
            return

        if ("signatures" not in response
                or perspective_name not in response["signatures"]):
            raise KeyLookupError("Response not signed by the notary server")

        verified = False
        for key_id in response["signatures"][perspective_name]:
            if key_id in perspective_keys:
                verify_signed_json(response, perspective_name,
                                   perspective_keys[key_id])
                verified = True

        if not verified:
            raise KeyLookupError(
                "Response not signed with a known key: signed with: %r, known keys: %r"
                % (
                    list(response["signatures"][perspective_name].keys()),
                    list(perspective_keys.keys()),
                ))
Esempio n. 5
0
def main():

    parser = argparse.ArgumentParser()
    parser.add_argument("signature_name")
    parser.add_argument("input_json", nargs="?", type=argparse.FileType('r'),
                        default=sys.stdin)

    args = parser.parse_args()
    logging.basicConfig()

    server_name = args.signature_name
    keys = {}
    for target, port in get_targets(server_name):
        try:
            keys = get_server_keys(server_name, target, port)
            print "Using keys from https://%s:%s/_matrix/key/v1" % (target, port)
            write_signing_keys(sys.stdout, keys.values())
            break
        except:
            logging.exception("Error talking to %s:%s", target, port)

    json_to_check = json.load(args.input_json)
    print "Checking JSON:"
    for key_id in json_to_check["signatures"][args.signature_name]:
        try:
            key = keys[key_id]
            verify_signed_json(json_to_check, args.signature_name, key)
            print "PASS %s" % (key_id,)
        except:
            logging.exception("Check for key %s failed" % (key_id,))
            print "FAIL %s" % (key_id,)
Esempio n. 6
0
def _handle_key_deferred(verify_request):
    """Waits for the key to become available, and then performs a verification

    Args:
        verify_request (VerifyJsonRequest):

    Returns:
        Deferred[None]

    Raises:
        SynapseError if there was a problem performing the verification
    """
    server_name = verify_request.server_name
    with PreserveLoggingContext():
        _, key_id, verify_key = yield verify_request.key_ready

    json_object = verify_request.json_object

    try:
        verify_signed_json(json_object, server_name, verify_key)
    except SignatureVerifyException as e:
        logger.debug(
            "Error verifying signature for %s:%s:%s with key %s: %s",
            server_name,
            verify_key.alg,
            verify_key.version,
            encode_verify_key_base64(verify_key),
            str(e),
        )
        raise SynapseError(
            401,
            "Invalid signature for server %s with key %s:%s: %s" %
            (server_name, verify_key.alg, verify_key.version, str(e)),
            Codes.UNAUTHORIZED,
        )
Esempio n. 7
0
    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
Esempio n. 8
0
    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
Esempio n. 9
0
 def test_sign_and_verify(self):
     self.assertIn('signatures', self.signed)
     self.assertIn('Alice', self.signed['signatures'])
     self.assertIn('mock:test', self.signed['signatures']['Alice'])
     self.assertEqual(self.signed['signatures']['Alice']['mock:test'],
                      encode_base64(b'x_______'))
     self.assertEqual(self.sigkey.signed_bytes, b'{"foo":"bar"}')
     verify_signed_json(self.signed, 'Alice', self.verkey)
Esempio n. 10
0
def _handle_key_deferred(verify_request):
    """Waits for the key to become available, and then performs a verification

    Args:
        verify_request (VerifyKeyRequest):

    Returns:
        Deferred[None]

    Raises:
        SynapseError if there was a problem performing the verification
    """
    server_name = verify_request.server_name
    try:
        with PreserveLoggingContext():
            _, key_id, verify_key = yield verify_request.deferred
    except (IOError, RequestSendFailed) as e:
        logger.warn(
            "Got IOError when downloading keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            502,
            "Error downloading keys for %s" % (server_name,),
            Codes.UNAUTHORIZED,
        )
    except Exception as e:
        logger.exception(
            "Got Exception when downloading keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            401,
            "No key for %s with id %s" % (server_name, verify_request.key_ids),
            Codes.UNAUTHORIZED,
        )

    json_object = verify_request.json_object

    logger.debug("Got key %s %s:%s for server %s, verifying" % (
        key_id, verify_key.alg, verify_key.version, server_name,
    ))
    try:
        verify_signed_json(json_object, server_name, verify_key)
    except SignatureVerifyException as e:
        logger.debug(
            "Error verifying signature for %s:%s:%s with key %s: %s",
            server_name, verify_key.alg, verify_key.version,
            encode_verify_key_base64(verify_key),
            str(e),
        )
        raise SynapseError(
            401,
            "Invalid signature for server %s with key %s:%s: %s" % (
                server_name, verify_key.alg, verify_key.version, str(e),
            ),
            Codes.UNAUTHORIZED,
        )
Esempio n. 11
0
def _handle_key_deferred(verify_request):
    """Waits for the key to become available, and then performs a verification

    Args:
        verify_request (VerifyKeyRequest):

    Returns:
        Deferred[None]

    Raises:
        SynapseError if there was a problem performing the verification
    """
    server_name = verify_request.server_name
    try:
        with PreserveLoggingContext():
            _, key_id, verify_key = yield verify_request.deferred
    except KeyLookupError as e:
        logger.warn(
            "Failed to download keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            502,
            "Error downloading keys for %s" % (server_name,),
            Codes.UNAUTHORIZED,
        )
    except Exception as e:
        logger.exception(
            "Got Exception when downloading keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            401,
            "No key for %s with id %s" % (server_name, verify_request.key_ids),
            Codes.UNAUTHORIZED,
        )

    json_object = verify_request.json_object

    logger.debug("Got key %s %s:%s for server %s, verifying" % (
        key_id, verify_key.alg, verify_key.version, server_name,
    ))
    try:
        verify_signed_json(json_object, server_name, verify_key)
    except SignatureVerifyException as e:
        logger.debug(
            "Error verifying signature for %s:%s:%s with key %s: %s",
            server_name, verify_key.alg, verify_key.version,
            encode_verify_key_base64(verify_key),
            str(e),
        )
        raise SynapseError(
            401,
            "Invalid signature for server %s with key %s:%s: %s" % (
                server_name, verify_key.alg, verify_key.version, str(e),
            ),
            Codes.UNAUTHORIZED,
        )
Esempio n. 12
0
    def _do_invite(self, roomid, userstring):
        if (not userstring.startswith('@')
                and self._is_on("complete_usernames")):
            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:
                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 = nacl.signing.VerifyKey(
                        pubKeyObj['public_key'],
                        encoder=nacl.encoding.HexEncoder)
                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)
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
0
 def test_sign_and_verify(self):
     self.assertIn('signatures', self.signed)
     self.assertIn('Alice', self.signed['signatures'])
     self.assertIn('mock:test', self.signed['signatures']['Alice'])
     self.assertEqual(
         self.signed['signatures']['Alice']['mock:test'],
         encode_base64(b'x_______')
     )
     self.assertEqual(self.sigkey.signed_bytes, b'{"foo":"bar"}')
     verify_signed_json(self.signed, 'Alice', self.verkey)
Esempio n. 16
0
def _handle_key_deferred(verify_request):
    server_name = verify_request.server_name
    try:
        with PreserveLoggingContext():
            _, key_id, verify_key = yield verify_request.deferred
    except IOError as e:
        logger.warn(
            "Got IOError when downloading keys for %s: %s %s",
            server_name,
            type(e).__name__,
            str(e.message),
        )
        raise SynapseError(
            502,
            "Error downloading keys for %s" % (server_name, ),
            Codes.UNAUTHORIZED,
        )
    except Exception as e:
        logger.exception(
            "Got Exception when downloading keys for %s: %s %s",
            server_name,
            type(e).__name__,
            str(e.message),
        )
        raise SynapseError(
            401,
            "No key for %s with id %s" % (server_name, verify_request.key_ids),
            Codes.UNAUTHORIZED,
        )

    json_object = verify_request.json_object

    logger.debug("Got key %s %s:%s for server %s, verifying" % (
        key_id,
        verify_key.alg,
        verify_key.version,
        server_name,
    ))
    try:
        verify_signed_json(json_object, server_name, verify_key)
    except:
        raise SynapseError(
            401,
            "Invalid signature for server %s with key %s:%s" %
            (server_name, verify_key.alg, verify_key.version),
            Codes.UNAUTHORIZED,
        )
Esempio n. 17
0
 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
Esempio n. 18
0
 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
Esempio n. 19
0
    def _do_invite(self, roomid, userstring):
        if (not userstring.startswith('@') and
                    self._is_on("complete_usernames")):
            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:
                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 = nacl.signing.VerifyKey(pubKeyObj['public_key'], encoder=nacl.encoding.HexEncoder)
                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)
Esempio n. 20
0
def _check_device_signature(user_id, verify_key, signed_device, stored_device):
    """Check that a signature on a device or cross-signing key is correct and
    matches the copy of the device/key that we have stored.  Throws an
    exception if an error is detected.

    Args:
        user_id (str): the user ID whose signature is being checked
        verify_key (VerifyKey): the key to verify the device with
        signed_device (dict): the uploaded signed device data
        stored_device (dict): our previously stored copy of the device

    Raises:
        SynapseError: if the signature was invalid or the sent device is not the
            same as the stored device

    """

    # make sure that the device submitted matches what we have stored
    stripped_signed_device = {
        k: v
        for k, v in signed_device.items()
        if k not in ["signatures", "unsigned"]
    }
    stripped_stored_device = {
        k: v
        for k, v in stored_device.items()
        if k not in ["signatures", "unsigned"]
    }
    if stripped_signed_device != stripped_stored_device:
        logger.debug(
            "upload signatures: key does not match %s vs %s",
            signed_device,
            stored_device,
        )
        raise SynapseError(400, "Key does not match")

    try:
        verify_signed_json(signed_device, user_id, verify_key)
    except SignatureVerifyException:
        logger.debug("invalid signature on key")
        raise SynapseError(400, "Invalid signature", Codes.INVALID_SIGNATURE)
Esempio n. 21
0
def _handle_key_deferred(verify_request):
    server_name = verify_request.server_name
    try:
        with PreserveLoggingContext():
            _, key_id, verify_key = yield verify_request.deferred
    except IOError as e:
        logger.warn(
            "Got IOError when downloading keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            502,
            "Error downloading keys for %s" % (server_name,),
            Codes.UNAUTHORIZED,
        )
    except Exception as e:
        logger.exception(
            "Got Exception when downloading keys for %s: %s %s",
            server_name, type(e).__name__, str(e),
        )
        raise SynapseError(
            401,
            "No key for %s with id %s" % (server_name, verify_request.key_ids),
            Codes.UNAUTHORIZED,
        )

    json_object = verify_request.json_object

    logger.debug("Got key %s %s:%s for server %s, verifying" % (
        key_id, verify_key.alg, verify_key.version, server_name,
    ))
    try:
        verify_signed_json(json_object, server_name, verify_key)
    except Exception:
        raise SynapseError(
            401,
            "Invalid signature for server %s with key %s:%s" % (
                server_name, verify_key.alg, verify_key.version
            ),
            Codes.UNAUTHORIZED,
        )
Esempio n. 22
0
        def handle_key_deferred(group, deferred):
            server_name = group.server_name
            try:
                _, _, key_id, verify_key = yield deferred
            except IOError as e:
                logger.warn(
                    "Got IOError when downloading keys for %s: %s %s",
                    server_name,
                    type(e).__name__,
                    str(e.message),
                )
                raise SynapseError(
                    502,
                    "Error downloading keys for %s" % (server_name, ),
                    Codes.UNAUTHORIZED,
                )
            except Exception as e:
                logger.exception(
                    "Got Exception when downloading keys for %s: %s %s",
                    server_name,
                    type(e).__name__,
                    str(e.message),
                )
                raise SynapseError(
                    401,
                    "No key for %s with id %s" % (server_name, key_ids),
                    Codes.UNAUTHORIZED,
                )

            json_object = group_id_to_json[group.group_id]

            try:
                verify_signed_json(json_object, server_name, verify_key)
            except:
                raise SynapseError(
                    401,
                    "Invalid signature for server %s with key %s:%s" %
                    (server_name, verify_key.alg, verify_key.version),
                    Codes.UNAUTHORIZED,
                )
Esempio n. 23
0
 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
Esempio n. 24
0
        def handle_key_deferred(group, deferred):
            server_name = group.server_name
            try:
                _, _, key_id, verify_key = yield deferred
            except IOError as e:
                logger.warn(
                    "Got IOError when downloading keys for %s: %s %s",
                    server_name, type(e).__name__, str(e.message),
                )
                raise SynapseError(
                    502,
                    "Error downloading keys for %s" % (server_name,),
                    Codes.UNAUTHORIZED,
                )
            except Exception as e:
                logger.exception(
                    "Got Exception when downloading keys for %s: %s %s",
                    server_name, type(e).__name__, str(e.message),
                )
                raise SynapseError(
                    401,
                    "No key for %s with id %s" % (server_name, key_ids),
                    Codes.UNAUTHORIZED,
                )

            json_object = group_id_to_json[group.group_id]

            try:
                verify_signed_json(json_object, server_name, verify_key)
            except:
                raise SynapseError(
                    401,
                    "Invalid signature for server %s with key %s:%s" % (
                        server_name, verify_key.alg, verify_key.version
                    ),
                    Codes.UNAUTHORIZED,
                )
Esempio n. 25
0
    async def process_request(self, verify_request: VerifyJsonRequest) -> None:
        """Processes the `VerifyJsonRequest`. Raises if the object is not signed
        by the server, the signatures don't match or we failed to fetch the
        necessary keys.
        """

        if not verify_request.key_ids:
            raise SynapseError(
                400,
                f"Not signed by {verify_request.server_name}",
                Codes.UNAUTHORIZED,
            )

        # Add the keys we need to verify to the queue for retrieval. We queue
        # up requests for the same server so we don't end up with many in flight
        # requests for the same keys.
        key_request = verify_request.to_fetch_key_request()
        found_keys_by_server = await self._server_queue.add_to_queue(
            key_request, key=verify_request.server_name
        )

        # Since we batch up requests the returned set of keys may contain keys
        # from other servers, so we pull out only the ones we care about.s
        found_keys = found_keys_by_server.get(verify_request.server_name, {})

        # Verify each signature we got valid keys for, raising if we can't
        # verify any of them.
        verified = False
        for key_id in verify_request.key_ids:
            key_result = found_keys.get(key_id)
            if not key_result:
                continue

            if key_result.valid_until_ts < verify_request.minimum_valid_until_ts:
                continue

            verify_key = key_result.verify_key
            json_object = verify_request.get_json_object()
            try:
                verify_signed_json(
                    json_object,
                    verify_request.server_name,
                    verify_key,
                )
                verified = True
            except SignatureVerifyException as e:
                logger.debug(
                    "Error verifying signature for %s:%s:%s with key %s: %s",
                    verify_request.server_name,
                    verify_key.alg,
                    verify_key.version,
                    encode_verify_key_base64(verify_key),
                    str(e),
                )
                raise SynapseError(
                    401,
                    "Invalid signature for server %s with key %s:%s: %s"
                    % (
                        verify_request.server_name,
                        verify_key.alg,
                        verify_key.version,
                        str(e),
                    ),
                    Codes.UNAUTHORIZED,
                )

        if not verified:
            raise SynapseError(
                401,
                f"Failed to find any key to satisfy: {key_request}",
                Codes.UNAUTHORIZED,
            )
        y = arr
      else:
        return sum(arr) 


print(summer_69([1, 3, 5]))
print(summer_69([4, 5, 6, 7, 8, 9]))
print(summer_69([2, 1, 6, 9, 11]))
print(summer_69([]))
'''
print("\n")
from signedjson.key import generate_signing_key, get_verify_key, encode_signing_key_base64
from signedjson.sign import (
    sign_json, verify_signed_json, SignatureVerifyException
)

signing_key = generate_signing_key('zxcvb')
base_64_signing_key = encode_signing_key_base64(signing_key)
print(base_64_signing_key)
signed_json = sign_json({'my_key': 'my_data'},'Drew', signing_key)
print(signed_json)
verify_key = get_verify_key(signing_key)
base_64_verify_key = encode_signing_key_base64(verify_key)
print(base_64_verify_key)
#check = get_verify_key(base_64_signing_key)

try:
    verify_signed_json(signed_json, 'Drew', verify_key)
    print('Signature is valid')
except SignatureVerifyException:
    print('Signature is invalid')
Esempio n. 27
0
 def test_verify_fail_no_signature_for_alice(self):
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json({'signatures': {}}, 'Alice', self.verkey)
Esempio n. 28
0
    def get_server_verify_key_v2_indirect(self, server_names_and_key_ids,
                                          perspective_name,
                                          perspective_keys):
        # TODO(mark): Set the minimum_valid_until_ts to that needed by
        # the events being validated or the current time if validating
        # an incoming request.
        try:
            query_response = yield self.client.post_json(
                destination=perspective_name,
                path="/_matrix/key/v2/query",
                data={
                    u"server_keys": {
                        server_name: {
                            key_id: {
                                u"minimum_valid_until_ts": 0
                            } for key_id in key_ids
                        }
                        for server_name, key_ids in server_names_and_key_ids
                    }
                },
                long_retries=True,
            )
        except (NotRetryingDestination, RequestSendFailed) as e:
            raise_from(
                KeyLookupError("Failed to connect to remote server"), e,
            )
        except HttpResponseException as e:
            raise_from(
                KeyLookupError("Remote server returned an error"), e,
            )

        keys = {}

        responses = query_response["server_keys"]

        for response in responses:
            if (u"signatures" not in response
                    or perspective_name not in response[u"signatures"]):
                raise KeyLookupError(
                    "Key response not signed by perspective server"
                    " %r" % (perspective_name,)
                )

            verified = False
            for key_id in response[u"signatures"][perspective_name]:
                if key_id in perspective_keys:
                    verify_signed_json(
                        response,
                        perspective_name,
                        perspective_keys[key_id]
                    )
                    verified = True

            if not verified:
                logging.info(
                    "Response from perspective server %r not signed with a"
                    " known key, signed with: %r, known keys: %r",
                    perspective_name,
                    list(response[u"signatures"][perspective_name]),
                    list(perspective_keys)
                )
                raise KeyLookupError(
                    "Response not signed with a known key for perspective"
                    " server %r" % (perspective_name,)
                )

            processed_response = yield self.process_v2_response(
                perspective_name, response
            )
            server_name = response["server_name"]

            keys.setdefault(server_name, {}).update(processed_response)

        yield logcontext.make_deferred_yieldable(defer.gatherResults(
            [
                run_in_background(
                    self.store_keys,
                    server_name=server_name,
                    from_server=perspective_name,
                    verify_keys=response_keys,
                )
                for server_name, response_keys in keys.items()
            ],
            consumeErrors=True
        ).addErrback(unwrapFirstError))

        defer.returnValue(keys)
Esempio n. 29
0
    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)
Esempio n. 30
0
    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)
Esempio n. 31
0
 def test_verify_fail_not_base64(self):
     invalid = {'signatures': {'Alice': {'mock:test': 'not base64'}}}
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json(invalid, 'Alice', self.verkey)
Esempio n. 32
0
 def test_verify_fail_not_base64(self):
     invalid = {'signatures': {'Alice': {'mock:test': 'not base64'}}}
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json(invalid, 'Alice', self.verkey)
Esempio n. 33
0
 def test_verify_fail(self):
     self.signed['signatures']['Alice']['mock:test'] = encode_base64(
         b'not a signature')
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json(self.signed, 'Alice', self.verkey)
Esempio n. 34
0
    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)
Esempio n. 35
0
    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)
Esempio n. 36
0
    def get_server_verify_key_v2_indirect(self, server_names_and_key_ids,
                                          perspective_name, perspective_keys):
        # TODO(mark): Set the minimum_valid_until_ts to that needed by
        # the events being validated or the current time if validating
        # an incoming request.
        query_response = yield self.client.post_json(
            destination=perspective_name,
            path=b"/_matrix/key/v2/query",
            data={
                u"server_keys": {
                    server_name: {
                        key_id: {
                            u"minimum_valid_until_ts": 0
                        }
                        for key_id in key_ids
                    }
                    for server_name, key_ids in server_names_and_key_ids
                }
            },
            long_retries=True,
        )

        keys = {}

        responses = query_response["server_keys"]

        for response in responses:
            if (u"signatures" not in response
                    or perspective_name not in response[u"signatures"]):
                raise ValueError(
                    "Key response not signed by perspective server"
                    " %r" % (perspective_name, ))

            verified = False
            for key_id in response[u"signatures"][perspective_name]:
                if key_id in perspective_keys:
                    verify_signed_json(response, perspective_name,
                                       perspective_keys[key_id])
                    verified = True

            if not verified:
                logging.info(
                    "Response from perspective server %r not signed with a"
                    " known key, signed with: %r, known keys: %r",
                    perspective_name,
                    list(response[u"signatures"][perspective_name]),
                    list(perspective_keys))
                raise ValueError(
                    "Response not signed with a known key for perspective"
                    " server %r" % (perspective_name, ))

            processed_response = yield self.process_v2_response(
                perspective_name, response)

            for server_name, response_keys in processed_response.items():
                keys.setdefault(server_name, {}).update(response_keys)

        yield defer.gatherResults(
            [
                self.store_keys(
                    server_name=server_name,
                    from_server=perspective_name,
                    verify_keys=response_keys,
                ) for server_name, response_keys in keys.items()
            ],
            consumeErrors=True).addErrback(unwrapFirstError)

        defer.returnValue(keys)
Esempio n. 37
0
    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 = nacl.signing.VerifyKey(
                        pubKeyObj["public_key"],
                        encoder=nacl.encoding.HexEncoder)
                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)
Esempio n. 38
0
header = { 'alg': 'ES256' }
payload = { 'claim': 'JSON is the raddest.', 'iss': 'brianb' }
sig = jws.sign(header, payload, sk256)
print(jws.verify(header, payload, sig, vk))
'''
#------

from signedjson.key import generate_signing_key, get_verify_key, encode_signing_key_base64, decode_signing_key_base64
from signedjson.sign import (sign_json, verify_signed_json,
                             SignatureVerifyException)

signing_key = generate_signing_key('zxcvb')
signed_json = sign_json({'my_key': 'my_data'}, 'CDF', signing_key)
print(signed_json)

#Going into function
verify_key = get_verify_key(signing_key)
print("Going into function", verify_key)

enc = encode_signing_key_base64(verify_key)
print("Base64 encoded:", enc)

print("This will go into function")
print(decode_signing_key_base64("ed25519", "zxcvb", enc))

try:
    verify_signed_json(signed_json, 'CDF', verify_key)
    print('Signature is valid')
except SignatureVerifyException:
    print('Signature is invalid')
Esempio n. 39
0
    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
Esempio n. 40
0
    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)
Esempio n. 41
0
    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)
Esempio n. 42
0
    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)
Esempio n. 43
0
    def get_server_verify_key_v2_indirect(self, server_names_and_key_ids,
                                          perspective_name,
                                          perspective_keys):
        # TODO(mark): Set the minimum_valid_until_ts to that needed by
        # the events being validated or the current time if validating
        # an incoming request.
        try:
            query_response = yield self.client.post_json(
                destination=perspective_name,
                path="/_matrix/key/v2/query",
                data={
                    u"server_keys": {
                        server_name: {
                            key_id: {
                                u"minimum_valid_until_ts": 0
                            } for key_id in key_ids
                        }
                        for server_name, key_ids in server_names_and_key_ids
                    }
                },
                long_retries=True,
            )
        except (NotRetryingDestination, RequestSendFailed) as e:
            raise_from(
                KeyLookupError("Failed to connect to remote server"), e,
            )
        except HttpResponseException as e:
            raise_from(
                KeyLookupError("Remote server returned an error"), e,
            )

        keys = {}

        responses = query_response["server_keys"]

        for response in responses:
            if (u"signatures" not in response
                    or perspective_name not in response[u"signatures"]):
                raise KeyLookupError(
                    "Key response not signed by perspective server"
                    " %r" % (perspective_name,)
                )

            verified = False
            for key_id in response[u"signatures"][perspective_name]:
                if key_id in perspective_keys:
                    verify_signed_json(
                        response,
                        perspective_name,
                        perspective_keys[key_id]
                    )
                    verified = True

            if not verified:
                logging.info(
                    "Response from perspective server %r not signed with a"
                    " known key, signed with: %r, known keys: %r",
                    perspective_name,
                    list(response[u"signatures"][perspective_name]),
                    list(perspective_keys)
                )
                raise KeyLookupError(
                    "Response not signed with a known key for perspective"
                    " server %r" % (perspective_name,)
                )

            processed_response = yield self.process_v2_response(
                perspective_name, response, only_from_server=False
            )

            for server_name, response_keys in processed_response.items():
                keys.setdefault(server_name, {}).update(response_keys)

        yield logcontext.make_deferred_yieldable(defer.gatherResults(
            [
                run_in_background(
                    self.store_keys,
                    server_name=server_name,
                    from_server=perspective_name,
                    verify_keys=response_keys,
                )
                for server_name, response_keys in keys.items()
            ],
            consumeErrors=True
        ).addErrback(unwrapFirstError))

        defer.returnValue(keys)
Esempio n. 44
0
 def test_verify_fail_no_signature_for_alice(self):
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json({'signatures': {}}, 'Alice', self.verkey)
Esempio n. 45
0
 def test_verify_fail(self):
     self.signed['signatures']['Alice']['mock:test'] = encode_base64(
         b'not a signature'
     )
     with self.assertRaises(SignatureVerifyException):
         verify_signed_json(self.signed, 'Alice', self.verkey)
Esempio n. 46
0
    def get_server_verify_key_v2_indirect(self, server_names_and_key_ids,
                                          perspective_name,
                                          perspective_keys):
        # TODO(mark): Set the minimum_valid_until_ts to that needed by
        # the events being validated or the current time if validating
        # an incoming request.
        query_response = yield self.client.post_json(
            destination=perspective_name,
            path=b"/_matrix/key/v2/query",
            data={
                u"server_keys": {
                    server_name: {
                        key_id: {
                            u"minimum_valid_until_ts": 0
                        } for key_id in key_ids
                    }
                    for server_name, key_ids in server_names_and_key_ids
                }
            },
            long_retries=True,
        )

        keys = {}

        responses = query_response["server_keys"]

        for response in responses:
            if (u"signatures" not in response
                    or perspective_name not in response[u"signatures"]):
                raise ValueError(
                    "Key response not signed by perspective server"
                    " %r" % (perspective_name,)
                )

            verified = False
            for key_id in response[u"signatures"][perspective_name]:
                if key_id in perspective_keys:
                    verify_signed_json(
                        response,
                        perspective_name,
                        perspective_keys[key_id]
                    )
                    verified = True

            if not verified:
                logging.info(
                    "Response from perspective server %r not signed with a"
                    " known key, signed with: %r, known keys: %r",
                    perspective_name,
                    list(response[u"signatures"][perspective_name]),
                    list(perspective_keys)
                )
                raise ValueError(
                    "Response not signed with a known key for perspective"
                    " server %r" % (perspective_name,)
                )

            processed_response = yield self.process_v2_response(
                perspective_name, response, only_from_server=False
            )

            for server_name, response_keys in processed_response.items():
                keys.setdefault(server_name, {}).update(response_keys)

        yield defer.gatherResults(
            [
                self.store_keys(
                    server_name=server_name,
                    from_server=perspective_name,
                    verify_keys=response_keys,
                )
                for server_name, response_keys in keys.items()
            ],
            consumeErrors=True
        ).addErrback(unwrapFirstError)

        defer.returnValue(keys)