summary refs log tree commit diff
diff options
context:
space:
mode:
authorMark Haines <mark.haines@matrix.org>2014-11-14 16:45:39 +0000
committerMark Haines <mark.haines@matrix.org>2014-11-14 19:11:04 +0000
commitde1ec90133031cf6a043c47adae515ae1690c6d8 (patch)
treeb334a5dcb60556d68a136bc6d1a3b672b35e322c
parentAdd event-stream-service unit tests. (diff)
downloadsynapse-de1ec90133031cf6a043c47adae515ae1690c6d8.tar.xz
Validate signatures on incoming events
-rw-r--r--synapse/crypto/event_signing.py18
-rw-r--r--synapse/handlers/federation.py37
-rw-r--r--tests/handlers/test_federation.py4
3 files changed, 52 insertions, 7 deletions
diff --git a/synapse/crypto/event_signing.py b/synapse/crypto/event_signing.py
index baa93b0ee4..c7e6bec8f5 100644
--- a/synapse/crypto/event_signing.py
+++ b/synapse/crypto/event_signing.py
@@ -19,6 +19,7 @@ from synapse.api.events.utils import prune_event
 from syutil.jsonutil import encode_canonical_json
 from syutil.base64util import encode_base64, decode_base64
 from syutil.crypto.jsonsign import sign_json
+from synapse.api.errors import SynapseError, Codes
 
 import hashlib
 import logging
@@ -29,15 +30,24 @@ logger = logging.getLogger(__name__)
 def check_event_content_hash(event, hash_algorithm=hashlib.sha256):
     """Check whether the hash for this PDU matches the contents"""
     computed_hash = _compute_content_hash(event, hash_algorithm)
+    logging.debug("Expecting hash: %s", encode_base64(computed_hash.digest()))
     if computed_hash.name not in event.hashes:
-        raise Exception("Algorithm %s not in hashes %s" % (
-            computed_hash.name, list(event.hashes)
-        ))
+        raise SynapseError(
+            400,
+            "Algorithm %s not in hashes %s" % (
+                computed_hash.name, list(event.hashes),
+            ),
+            Codes.UNAUTHORIZED,
+        )
     message_hash_base64 = event.hashes[computed_hash.name]
     try:
         message_hash_bytes = decode_base64(message_hash_base64)
     except:
-        raise Exception("Invalid base64: %s" % (message_hash_base64,))
+        raise SynapseError(
+            400,
+            "Invalid base64: %s" % (message_hash_base64,),
+            Codes.UNAUTHORIZED,
+        )
     return message_hash_bytes == computed_hash.digest()
 
 
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index 5e096f4652..fce935b444 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -17,13 +17,17 @@
 
 from ._base import BaseHandler
 
-from synapse.api.errors import AuthError, FederationError
+from synapse.api.events.utils import prune_event
+from synapse.api.errors import AuthError, FederationError, SynapseError
 from synapse.api.events.room import RoomMemberEvent
 from synapse.api.constants import Membership
 from synapse.util.logutils import log_function
 from synapse.federation.pdu_codec import PduCodec
 from synapse.util.async import run_on_reactor
-from synapse.crypto.event_signing import compute_event_signature
+from synapse.crypto.event_signing import (
+    compute_event_signature, check_event_content_hash
+)
+from syutil.jsonutil import encode_canonical_json
 
 from twisted.internet import defer
 
@@ -59,6 +63,7 @@ class FederationHandler(BaseHandler):
         self.state_handler = hs.get_state_handler()
         # self.auth_handler = gs.get_auth_handler()
         self.server_name = hs.hostname
+        self.keyring = hs.get_keyring()
 
         self.lock_manager = hs.get_room_lock_manager()
 
@@ -112,6 +117,34 @@ class FederationHandler(BaseHandler):
 
         logger.debug("Processing event: %s", event.event_id)
 
+        redacted_event = prune_event(event)
+        redacted_event.origin = pdu.origin
+        redacted_event.origin_server_ts = pdu.origin_server_ts
+        redacted_pdu = self.pdu_codec.pdu_from_event(redacted_event)
+
+        redacted_pdu_json = redacted_pdu.get_dict()
+        try:
+            yield self.keyring.verify_json_for_server(
+                event.origin, redacted_pdu_json
+            )
+        except SynapseError as e:
+            logger.warn("Signature check failed for %s redacted to %s",
+                encode_canonical_json(pdu.get_dict()),
+                encode_canonical_json(redacted_pdu_json),
+            )
+            raise FederationError(
+                "ERROR",
+                e.code,
+                e.msg,
+                affected=event.event_id,
+            )
+
+        if not check_event_content_hash(pdu):
+            logger.warn(
+                "Event content has been tampered, redacting %s", event.event_id
+            )
+            event = redacted_event
+
         if state:
             state = [self.pdu_codec.event_from_pdu(p) for p in state]
 
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index e386cddb38..3f17ca8fb0 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -23,7 +23,7 @@ from synapse.handlers.federation import FederationHandler
 from synapse.server import HomeServer
 from synapse.federation.units import Pdu
 
-from mock import NonCallableMock, ANY
+from mock import NonCallableMock, ANY, Mock
 
 from ..utils import MockKey
 
@@ -62,6 +62,7 @@ class FederationTestCase(unittest.TestCase):
             config=self.mock_config,
             auth=self.auth,
             state_handler=self.state_handler,
+            keyring=Mock(),
         )
 
         self.datastore = hs.get_datastore()
@@ -80,6 +81,7 @@ class FederationTestCase(unittest.TestCase):
             origin_server_ts=0,
             event_id="$a:b",
             origin="b",
+            hashes={"sha256":"PvbCLWrTBxnBsSO7/cJ76072ySTCgI/XGadESRAe02M"},
         )
 
         self.datastore.persist_event.return_value = defer.succeed(None)