mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-26 03:21:05 +00:00
RSA sign and verify (working)
This commit is contained in:
parent
62f8b2ac17
commit
9360f4512c
3 changed files with 56 additions and 37 deletions
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue