From 2db4da4061da0e06d18636afffc133850948c5a4 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 13 May 2020 11:40:57 +0100 Subject: [PATCH] Check all signatures are signed by the right actor. --- fedireads/incoming.py | 30 +++++++++++++++++------------ fedireads/tests/test_signing.py | 34 +++++++++++++++++---------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/fedireads/incoming.py b/fedireads/incoming.py index bbe68048..6827e8ee 100644 --- a/fedireads/incoming.py +++ b/fedireads/incoming.py @@ -1,6 +1,8 @@ ''' handles all of the activity coming in to the server ''' import json from base64 import b64decode +from urllib.parse import urldefrag + from Crypto.Hash import SHA256 from Crypto.PublicKey import RSA from Crypto.Signature import pkcs1_15 @@ -46,7 +48,7 @@ def shared_inbox(request): return HttpResponseBadRequest() try: - verify_signature(request) + verify_signature(activity.get('actor'), request) except ValueError: return HttpResponse(status=401) @@ -82,24 +84,24 @@ def shared_inbox(request): return HttpResponse() -def get_public_key(key_id): - # TODO Use the anchor - actors can have multiple keys? - key_actor = key_id.split('#', 1)[0] +def get_public_key(key_actor): try: - public_key = models.User.objects.get(actor=key_actor).public_key + user = models.User.objects.get(actor=key_actor) + public_key = user.public_key + actor = user.actor except models.User.DoesNotExist: response = requests.get( - key_id, + key_actor, headers={'Accept': 'application/activity+json'} ) if not response.ok: raise ValueError('Could not load public key') - actor = response.json() - public_key = actor['publicKey']['publicKeyPem'] + user_data = response.json() + public_key = user_data['publicKey']['publicKeyPem'] return RSA.import_key(public_key) -def verify_signature(request): +def verify_signature(required_actor, request): ''' verify rsa signature ''' signature_dict = {} for pair in request.headers['Signature'].split(','): @@ -114,7 +116,13 @@ def verify_signature(request): except KeyError: raise ValueError('Invalid auth header') - key = get_public_key(key_id) + # TODO Use the fragment - actors can have multiple keys? + key_actor = urldefrag(key_id).url + + if key_actor != required_actor: + raise ValueError("Wrong actor created signature.") + + key = get_public_key(key_actor) comparison_string = [] for signed_header_name in headers.split(' '): @@ -134,8 +142,6 @@ def verify_signature(request): # raises a ValueError if it fails signer.verify(digest, signature) - return True - @app.task def handle_follow(activity): diff --git a/fedireads/tests/test_signing.py b/fedireads/tests/test_signing.py index 9650f552..f4da6033 100644 --- a/fedireads/tests/test_signing.py +++ b/fedireads/tests/test_signing.py @@ -14,23 +14,14 @@ class Signature(TestCase): self.rat = User.objects.create_user('rat', 'rat@example.com', '') self.cat = User.objects.create_user('cat', 'cat@example.com', '') - def test_wrong_signature(self): - ''' All messages must be signed by the right actor. - - (cat cannot sign messages on behalf of mouse) - ''' - activity = get_follow_request( - self.mouse, - self.rat, - ) - - now = http_date() - signature = make_signature(self.cat, self.rat.inbox, now) - + def send_follow(self, signature, now): c = Client() - response = c.post( + return c.post( urlsplit(self.rat.inbox).path, - data=activity, + data=get_follow_request( + self.mouse, + self.rat, + ), content_type='application/json', **{ 'HTTP_DATE': now, @@ -40,4 +31,15 @@ class Signature(TestCase): } ) - assert response.status_code == 401 + def test_correct_signature(self): + now = http_date() + signature = make_signature(self.mouse, self.rat.inbox, now) + return self.send_follow(signature, now).status_code == 200 + + def test_wrong_signature(self): + ''' Messages must be signed by the right actor. + (cat cannot sign messages on behalf of mouse) + ''' + now = http_date() + signature = make_signature(self.cat, self.rat.inbox, now) + assert self.send_follow(signature, now).status_code == 401