summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Morgan <andrew@amorgan.xyz>2019-08-21 16:25:00 +0200
committerAndrew Morgan <andrew@amorgan.xyz>2019-08-21 16:25:00 +0200
commit1954438610691f7dcfd6f5478265f6f5d7df9daa (patch)
treea49dc48f34e468af0506d423e2487c0c9ba51827
parentMerge pull request #5860 from matrix-org/erikj/update_5704_comments (diff)
downloadsynapse-1954438610691f7dcfd6f5478265f6f5d7df9daa.tar.xz
Use the v2 lookup API
-rw-r--r--synapse/handlers/identity.py12
-rw-r--r--synapse/handlers/room_member.py68
-rw-r--r--synapse/util/hash.py33
3 files changed, 102 insertions, 11 deletions
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index d199521b58..beb7cadd46 100644
--- a/synapse/handlers/identity.py
+++ b/synapse/handlers/identity.py
@@ -31,6 +31,7 @@ from synapse.api.errors import (
 )
 
 from ._base import BaseHandler
+from enum import Enum
 
 logger = logging.getLogger(__name__)
 
@@ -282,3 +283,14 @@ class IdentityHandler(BaseHandler):
         except HttpResponseException as e:
             logger.info("Proxied requestToken failed: %r", e)
             raise e.to_synapse_error()
+
+class LookupAlgorithm(Enum):
+    """
+    Supported hashing algorithms when performing a 3PID lookup.
+
+    SHA256 - Hashing an (address, medium, pepper) combo with sha256, then url-safe base64
+        encoding
+    NONE - Not performing any hashing. Simply sending an (address, medium) combo in plaintext
+    """
+    SHA256 = "sha256"
+    NONE = "none"
diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py
index 249a6d9c5d..68b280924d 100644
--- a/synapse/handlers/room_member.py
+++ b/synapse/handlers/room_member.py
@@ -32,14 +32,16 @@ from synapse.api.errors import AuthError, Codes, HttpResponseException, SynapseE
 from synapse.types import RoomID, UserID
 from synapse.util.async_helpers import Linearizer
 from synapse.util.distributor import user_joined_room, user_left_room
+from synapse.util.hash import sha256_and_url_safe_base64
 
 from ._base import BaseHandler
 
+from synapse.handlers.identity import LookupAlgorithm
+
 logger = logging.getLogger(__name__)
 
 id_server_scheme = "https://"
 
-
 class RoomMemberHandler(object):
     # TODO(paul): This handler currently contains a messy conflation of
     #   low-level API that works on UserID objects and so on, and REST-level
@@ -697,22 +699,66 @@ class RoomMemberHandler(object):
             raise SynapseError(
                 403, "Looking up third-party identifiers is denied from this server"
             )
+
+        # Check what hashing details are supported by this identity server
         try:
-            data = yield self.simple_http_client.get_json(
-                "%s%s/_matrix/identity/api/v1/lookup" % (id_server_scheme, id_server),
-                {"medium": medium, "address": address},
+            hash_details = yield self.simple_http_client.get_json(
+                "%s%s/_matrix/identity/v2/hash_details" % (id_server_scheme, id_server)
             )
+            supported_lookup_algorithms = hash_details["algorithms"]
+            lookup_pepper = hash_details["lookup_pepper"]
+        except (HttpResponseException, ValueError) as e:
+            logger.warn("Error when looking up hashing details: %s" % (e,))
+            return None
 
-            if "mxid" in data:
-                if "signatures" not in data:
-                    raise AuthError(401, "No signatures on 3pid binding")
-                yield self._verify_any_signature(data, id_server)
-                return data["mxid"]
+        # Check if none of the supported lookup algorithms are present
+        if not any(i in supported_lookup_algorithms for i in [LookupAlgorithm.SHA256,
+                                                              LookupAlgorithm.NONE]):
+            logger.warn("No supported lookup algorithms found for %s%s" %
+                        (id_server_scheme, id_server))
 
-        except IOError as e:
-            logger.warn("Error from identity server lookup: %s" % (e,))
             return None
 
+        if LookupAlgorithm.SHA256 in supported_lookup_algorithms:
+            # Perform a hashed lookup
+            lookup_algorithm = LookupAlgorithm.SHA256
+
+            # Hash address, medium and the pepper with sha256
+            to_hash = "%s %s %s" % (address, medium, lookup_pepper)
+            lookup_value = sha256_and_url_safe_base64(to_hash)
+
+        elif LookupAlgorithm.NONE in supported_lookup_algorithms:
+            # Perform a non-hashed lookup
+            lookup_algorithm = LookupAlgorithm.NONE
+
+            # Combine together plaintext address and medium
+            lookup_value = "%s %s" % (address, medium)
+
+        try:
+            lookup_results = yield self.simple_http_client.post_json_get_json(
+                "%s%s/_matrix/identity/v2/lookup" % (id_server_scheme, id_server),
+                {
+                    "addresses": [lookup_value],
+                    "algorithm": lookup_algorithm,
+                    "pepper": lookup_pepper,
+                },
+            )
+        except (HttpResponseException, ValueError) as e:
+            logger.warn("Error when performing a 3pid lookup: %s" % (e,))
+            return None
+
+        # Check for a mapping from what we looked up to an MXID
+        if (
+            "mappings" not in lookup_results
+            or not isinstance(lookup_results["mappings"], dict)
+        ):
+            logger.debug("No results from 3pid lookup")
+            return None
+
+        # Return the MXID if it's available, or None otherwise
+        return lookup_results["mappings"].get(lookup_value)
+
+
     @defer.inlineCallbacks
     def _verify_any_signature(self, data, server_hostname):
         if server_hostname not in data["signatures"]:
diff --git a/synapse/util/hash.py b/synapse/util/hash.py
new file mode 100644
index 0000000000..aa5d5ae31c
--- /dev/null
+++ b/synapse/util/hash.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 The Matrix.org Foundation C.I.C.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import hashlib
+import unpaddedbase64
+
+
+def sha256_and_url_safe_base64(input_text):
+    """SHA256 hash an input string, encode the digest as url-safe base64, and
+    return
+
+    :param input_text: string to hash
+    :type input_text: str
+
+    :returns a sha256 hashed and url-safe base64 encoded digest
+    :rtype: str
+    """
+    digest = hashlib.sha256(input_text.encode()).digest()
+    return unpaddedbase64.encode_base64(digest, urlsafe=True)
+