RSA sign and verify (working)

This commit is contained in:
Mouse Reeve 2020-01-29 20:56:18 -08:00
parent 62f8b2ac17
commit 9360f4512c
3 changed files with 56 additions and 37 deletions

View file

@ -79,9 +79,12 @@ def sign_and_send(sender, action, destination):
''' crpyto whatever and http junk ''' ''' crpyto whatever and http junk '''
inbox_fragment = sender.inbox.replace('https://%s' % DOMAIN, '') inbox_fragment = sender.inbox.replace('https://%s' % DOMAIN, '')
now = datetime.utcnow().isoformat() now = datetime.utcnow().isoformat()
message_to_sign = '''(request-target): post %s signature_headers = [
host: https://%s '(request-target): post %s' % inbox_fragment,
date: %s''' % (inbox_fragment, DOMAIN, now) 'host: https://%s' % DOMAIN,
'date: %s' % now
]
message_to_sign = '\n'.join(signature_headers)
signer = pkcs1_15.new(RSA.import_key(sender.private_key)) signer = pkcs1_15.new(RSA.import_key(sender.private_key))
signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8'))) signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8')))
@ -89,7 +92,7 @@ date: %s''' % (inbox_fragment, DOMAIN, now)
'keyId': '%s#main-key' % sender.actor, 'keyId': '%s#main-key' % sender.actor,
'algorithm': 'rsa-sha256', 'algorithm': 'rsa-sha256',
'headers': '(request-target) host date', 'headers': '(request-target) host date',
'signature': b64encode(signed_message), 'signature': b64encode(signed_message).decode('utf8'),
} }
signature = ','.join('%s="%s"' % (k, v) for (k, v) in signature.items()) signature = ','.join('%s="%s"' % (k, v) for (k, v) in signature.items())
@ -99,7 +102,7 @@ date: %s''' % (inbox_fragment, DOMAIN, now)
headers={ headers={
'Date': now, 'Date': now,
'Signature': signature, 'Signature': signature,
'Host': DOMAIN, 'Host': 'https://%s' % DOMAIN,
}, },
) )
if not response.ok: if not response.ok:

View file

@ -1,8 +1,13 @@
''' handles all of the activity coming in to the server ''' ''' handles all of the activity coming in to the server '''
from base64 import b64decode
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from django.http import HttpResponse, HttpResponseBadRequest, \ from django.http import HttpResponse, HttpResponseBadRequest, \
HttpResponseNotFound, JsonResponse HttpResponseNotFound, JsonResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
import json import json
import requests
from uuid import uuid4 from uuid import uuid4
from fedireads import models from fedireads import models
@ -32,26 +37,56 @@ def webfinger(request):
}) })
'''
def host_meta(request):
import pdb;pdb.set_trace()
'''
@csrf_exempt @csrf_exempt
def shared_inbox(request): def shared_inbox(request):
''' incoming activitypub events ''' ''' incoming activitypub events '''
# TODO: this is just a dupe of inbox but there's gotta be a reason?? # TODO: should this be functionally different from the non-shared inbox??
if request.method == 'GET': if request.method == 'GET':
return HttpResponseNotFound() return HttpResponseNotFound()
# TODO: RSA key verification
try: try:
activity = json.loads(request.body) activity = json.loads(request.body)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
return HttpResponseBadRequest return HttpResponseBadRequest
# verify rsa signature
signature_header = request.headers['Signature'].split(',')
signature_dict = {}
for pair in signature_header:
k, v = pair.split('=', 1)
v = v.replace('"', '')
signature_dict[k] = v
key_id = signature_dict['keyId']
headers = signature_dict['headers']
signature = b64decode(signature_dict['signature'])
response = requests.get(
key_id,
headers={'Accept': 'application/activity+json'}
)
if not response.ok:
response.raise_for_status()
actor = response.json()
key = RSA.import_key(actor['publicKey']['publicKeyPem'])
comparison_string = []
for signed_header_name in headers.split(' '):
if signed_header_name == '(request-target)':
comparison_string.append('(request-target): post %s' % request.path)
else:
comparison_string.append('%s: %s' % (
signed_header_name,
request.headers[signed_header_name]
))
comparison_string = '\n'.join(comparison_string)
signer = pkcs1_15.new(key)
digest = SHA256.new()
digest.update(comparison_string.encode())
signer.verify(digest, signature)
if activity['type'] == 'Add': if activity['type'] == 'Add':
return handle_incoming_shelve(activity) return handle_incoming_shelve(activity)
@ -61,37 +96,18 @@ def shared_inbox(request):
if activity['type'] == 'Create': if activity['type'] == 'Create':
return handle_incoming_create(activity) return handle_incoming_create(activity)
return HttpResponse() return HttpResponseNotFound()
@csrf_exempt @csrf_exempt
def inbox(request, username): def inbox(request, username):
''' incoming activitypub events ''' ''' incoming activitypub events '''
if request.method == 'GET':
return HttpResponseNotFound()
# TODO: RSA key verification
try:
activity = json.loads(request.body)
except json.decoder.JSONDecodeError:
return HttpResponseBadRequest
# TODO: should do some kind of checking if the user accepts # TODO: should do some kind of checking if the user accepts
# this action from the sender # this action from the sender probably? idk
# but this will just throw an error if the user doesn't exist I guess # but this will just throw an error if the user doesn't exist I guess
models.User.objects.get(localname=username) models.User.objects.get(localname=username)
if activity['type'] == 'Add': return shared_inbox(request)
return handle_incoming_shelve(activity)
if activity['type'] == 'Follow':
return handle_incoming_follow(activity)
if activity['type'] == 'Create':
return handle_incoming_create(activity)
return HttpResponse()
@csrf_exempt @csrf_exempt

View file

@ -1,4 +1,4 @@
# Generated by Django 3.0.2 on 2020-01-29 19:58 # Generated by Django 3.0.2 on 2020-01-30 02:26
from django.conf import settings from django.conf import settings
import django.contrib.auth.models import django.contrib.auth.models