Skip to content

Commit

Permalink
Merge pull request matrix-org#12 from matrix-org/federation_authoriza…
Browse files Browse the repository at this point in the history
…tion

Federation authorization
  • Loading branch information
NegativeMjark committed Nov 11, 2014
2 parents 83a1cce + 092979b commit a8ceeec
Show file tree
Hide file tree
Showing 71 changed files with 3,774 additions and 3,913 deletions.
2 changes: 1 addition & 1 deletion demo/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ for port in 8080 8081 8082; do
-D --pid-file "$DIR/$port.pid" \
--manhole $((port + 1000)) \
--tls-dh-params-path "demo/demo.tls.dh" \
$PARAMS
$PARAMS $SYNAPSE_PARAMS

python -m synapse.app.homeserver \
--config-path "demo/etc/$port.config" \
Expand Down
16 changes: 8 additions & 8 deletions docs/server-server/signing.rst
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
Signing JSON
============

JSON is signed by encoding the JSON object without ``signatures`` or ``meta``
JSON is signed by encoding the JSON object without ``signatures`` or ``unsigned``
keys using a canonical encoding. The JSON bytes are then signed using the
signature algorithm and the signature encoded using base64 with the padding
stripped. The resulting base64 signature is added to an object under the
*signing key identifier* which is added to the ``signatures`` object under the
name of the server signing it which is added back to the original JSON object
along with the ``meta`` object.
along with the ``unsigned`` object.

The *signing key identifier* is the concatenation of the *signing algorithm*
and a *key version*. The *signing algorithm* identifies the algorithm used to
sign the JSON. The currently support value for *signing algorithm* is
``ed25519`` as implemented by NACL (http://nacl.cr.yp.to/). The *key version*
is used to distinguish between different signing keys used by the same entity.

The ``meta`` object and the ``signatures`` object are not covered by the
signature. Therefore intermediate servers can add metadata such as time stamps
The ``unsigned`` object and the ``signatures`` object are not covered by the
signature. Therefore intermediate servers can add unsigneddata such as time stamps
and additional signatures.


Expand All @@ -27,7 +27,7 @@ and additional signatures.
"signing_keys": {
"ed25519:1": "XSl0kuyvrXNj6A+7/tkrB9sxSbRi08Of5uRhxOqZtEQ"
},
"meta": {
"unsigned": {
"retrieved_ts_ms": 922834800000
},
"signatures": {
Expand All @@ -41,7 +41,7 @@ and additional signatures.

def sign_json(json_object, signing_key, signing_name):
signatures = json_object.pop("signatures", {})
meta = json_object.pop("meta", None)
unsigned = json_object.pop("unsigned", None)

signed = signing_key.sign(encode_canonical_json(json_object))
signature_base64 = encode_base64(signed.signature)
Expand All @@ -50,8 +50,8 @@ and additional signatures.
signatures.setdefault(sigature_name, {})[key_id] = signature_base64

json_object["signatures"] = signatures
if meta is not None:
json_object["meta"] = meta
if unsigned is not None:
json_object["unsigned"] = unsigned

return json_object

Expand Down
47 changes: 47 additions & 0 deletions scripts/check_event_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from synapse.crypto.event_signing import *
from syutil.base64util import encode_base64

import argparse
import hashlib
import sys
import json


class dictobj(dict):
def __init__(self, *args, **kargs):
dict.__init__(self, *args, **kargs)
self.__dict__ = self

def get_dict(self):
return dict(self)

def get_full_dict(self):
return dict(self)


def main():
parser = argparse.ArgumentParser()
parser.add_argument("input_json", nargs="?", type=argparse.FileType('r'),
default=sys.stdin)
args = parser.parse_args()
logging.basicConfig()

event_json = dictobj(json.load(args.input_json))

algorithms = {
"sha256": hashlib.sha256,
}

for alg_name in event_json.hashes:
if check_event_content_hash(event_json, algorithms[alg_name]):
print "PASS content hash %s" % (alg_name,)
else:
print "FAIL content hash %s" % (alg_name,)

for algorithm in algorithms.values():
name, h_bytes = compute_event_reference_hash(event_json, algorithm)
print "Reference hash %s: %s" % (name, encode_base64(h_bytes))

if __name__=="__main__":
main()

73 changes: 73 additions & 0 deletions scripts/check_signature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

from syutil.crypto.jsonsign import verify_signed_json
from syutil.crypto.signing_key import (
decode_verify_key_bytes, write_signing_keys
)
from syutil.base64util import decode_base64

import urllib2
import json
import sys
import dns.resolver
import pprint
import argparse
import logging

def get_targets(server_name):
if ":" in server_name:
target, port = server_name.split(":")
yield (target, int(port))
return
try:
answers = dns.resolver.query("_matrix._tcp." + server_name, "SRV")
for srv in answers:
yield (srv.target, srv.port)
except dns.resolver.NXDOMAIN:
yield (server_name, 8480)

def get_server_keys(server_name, target, port):
url = "https://%s:%i/_matrix/key/v1" % (target, port)
keys = json.load(urllib2.urlopen(url))
verify_keys = {}
for key_id, key_base64 in keys["verify_keys"].items():
verify_key = decode_verify_key_bytes(key_id, decode_base64(key_base64))
verify_signed_json(keys, server_name, verify_key)
verify_keys[key_id] = verify_key
return verify_keys

def 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,)


if __name__ == '__main__':
main()

69 changes: 69 additions & 0 deletions scripts/hash_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from synapse.storage.pdu import PduStore
from synapse.storage.signatures import SignatureStore
from synapse.storage._base import SQLBaseStore
from synapse.federation.units import Pdu
from synapse.crypto.event_signing import (
add_event_pdu_content_hash, compute_pdu_event_reference_hash
)
from synapse.api.events.utils import prune_pdu
from syutil.base64util import encode_base64, decode_base64
from syutil.jsonutil import encode_canonical_json
import sqlite3
import sys

class Store(object):
_get_pdu_tuples = PduStore.__dict__["_get_pdu_tuples"]
_get_pdu_content_hashes_txn = SignatureStore.__dict__["_get_pdu_content_hashes_txn"]
_get_prev_pdu_hashes_txn = SignatureStore.__dict__["_get_prev_pdu_hashes_txn"]
_get_pdu_origin_signatures_txn = SignatureStore.__dict__["_get_pdu_origin_signatures_txn"]
_store_pdu_content_hash_txn = SignatureStore.__dict__["_store_pdu_content_hash_txn"]
_store_pdu_reference_hash_txn = SignatureStore.__dict__["_store_pdu_reference_hash_txn"]
_store_prev_pdu_hash_txn = SignatureStore.__dict__["_store_prev_pdu_hash_txn"]
_simple_insert_txn = SQLBaseStore.__dict__["_simple_insert_txn"]


store = Store()


def select_pdus(cursor):
cursor.execute(
"SELECT pdu_id, origin FROM pdus ORDER BY depth ASC"
)

ids = cursor.fetchall()

pdu_tuples = store._get_pdu_tuples(cursor, ids)

pdus = [Pdu.from_pdu_tuple(p) for p in pdu_tuples]

reference_hashes = {}

for pdu in pdus:
try:
if pdu.prev_pdus:
print "PROCESS", pdu.pdu_id, pdu.origin, pdu.prev_pdus
for pdu_id, origin, hashes in pdu.prev_pdus:
ref_alg, ref_hsh = reference_hashes[(pdu_id, origin)]
hashes[ref_alg] = encode_base64(ref_hsh)
store._store_prev_pdu_hash_txn(cursor, pdu.pdu_id, pdu.origin, pdu_id, origin, ref_alg, ref_hsh)
print "SUCCESS", pdu.pdu_id, pdu.origin, pdu.prev_pdus
pdu = add_event_pdu_content_hash(pdu)
ref_alg, ref_hsh = compute_pdu_event_reference_hash(pdu)
reference_hashes[(pdu.pdu_id, pdu.origin)] = (ref_alg, ref_hsh)
store._store_pdu_reference_hash_txn(cursor, pdu.pdu_id, pdu.origin, ref_alg, ref_hsh)

for alg, hsh_base64 in pdu.hashes.items():
print alg, hsh_base64
store._store_pdu_content_hash_txn(cursor, pdu.pdu_id, pdu.origin, alg, decode_base64(hsh_base64))

except:
print "FAILED_", pdu.pdu_id, pdu.origin, pdu.prev_pdus

def main():
conn = sqlite3.connect(sys.argv[1])
cursor = conn.cursor()
select_pdus(cursor)
conn.commit()

if __name__=='__main__':
main()
Loading

0 comments on commit a8ceeec

Please sign in to comment.