Check all signatures are signed by the right actor.
This commit is contained in:
parent
10efe4d1b4
commit
2db4da4061
|
@ -1,6 +1,8 @@
|
||||||
''' handles all of the activity coming in to the server '''
|
''' handles all of the activity coming in to the server '''
|
||||||
import json
|
import json
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
from urllib.parse import urldefrag
|
||||||
|
|
||||||
from Crypto.Hash import SHA256
|
from Crypto.Hash import SHA256
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Signature import pkcs1_15
|
from Crypto.Signature import pkcs1_15
|
||||||
|
@ -46,7 +48,7 @@ def shared_inbox(request):
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verify_signature(request)
|
verify_signature(activity.get('actor'), request)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return HttpResponse(status=401)
|
return HttpResponse(status=401)
|
||||||
|
|
||||||
|
@ -82,24 +84,24 @@ def shared_inbox(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
|
|
||||||
def get_public_key(key_id):
|
def get_public_key(key_actor):
|
||||||
# TODO Use the anchor - actors can have multiple keys?
|
|
||||||
key_actor = key_id.split('#', 1)[0]
|
|
||||||
try:
|
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:
|
except models.User.DoesNotExist:
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
key_id,
|
key_actor,
|
||||||
headers={'Accept': 'application/activity+json'}
|
headers={'Accept': 'application/activity+json'}
|
||||||
)
|
)
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
raise ValueError('Could not load public key')
|
raise ValueError('Could not load public key')
|
||||||
actor = response.json()
|
user_data = response.json()
|
||||||
public_key = actor['publicKey']['publicKeyPem']
|
public_key = user_data['publicKey']['publicKeyPem']
|
||||||
|
|
||||||
return RSA.import_key(public_key)
|
return RSA.import_key(public_key)
|
||||||
|
|
||||||
def verify_signature(request):
|
def verify_signature(required_actor, request):
|
||||||
''' verify rsa signature '''
|
''' verify rsa signature '''
|
||||||
signature_dict = {}
|
signature_dict = {}
|
||||||
for pair in request.headers['Signature'].split(','):
|
for pair in request.headers['Signature'].split(','):
|
||||||
|
@ -114,7 +116,13 @@ def verify_signature(request):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError('Invalid auth header')
|
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 = []
|
comparison_string = []
|
||||||
for signed_header_name in headers.split(' '):
|
for signed_header_name in headers.split(' '):
|
||||||
|
@ -134,8 +142,6 @@ def verify_signature(request):
|
||||||
# raises a ValueError if it fails
|
# raises a ValueError if it fails
|
||||||
signer.verify(digest, signature)
|
signer.verify(digest, signature)
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_follow(activity):
|
def handle_follow(activity):
|
||||||
|
|
|
@ -14,23 +14,14 @@ class Signature(TestCase):
|
||||||
self.rat = User.objects.create_user('rat', 'rat@example.com', '')
|
self.rat = User.objects.create_user('rat', 'rat@example.com', '')
|
||||||
self.cat = User.objects.create_user('cat', 'cat@example.com', '')
|
self.cat = User.objects.create_user('cat', 'cat@example.com', '')
|
||||||
|
|
||||||
def test_wrong_signature(self):
|
def send_follow(self, signature, now):
|
||||||
''' 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)
|
|
||||||
|
|
||||||
c = Client()
|
c = Client()
|
||||||
response = c.post(
|
return c.post(
|
||||||
urlsplit(self.rat.inbox).path,
|
urlsplit(self.rat.inbox).path,
|
||||||
data=activity,
|
data=get_follow_request(
|
||||||
|
self.mouse,
|
||||||
|
self.rat,
|
||||||
|
),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
**{
|
**{
|
||||||
'HTTP_DATE': now,
|
'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
|
||||||
|
|
Loading…
Reference in New Issue