Calculate message body digest from incoming bytes to avoid any json conversion issues

master
Bob Mottram 2019-08-16 18:19:23 +01:00
parent ffde81d909
commit 1b1810ff8a
4 changed files with 31 additions and 22 deletions

View File

@ -383,7 +383,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.projectVersion) self.server.projectVersion)
return True return True
def _updateInboxQueue(self,nickname: str,messageJson: {}) -> int: def _updateInboxQueue(self,nickname: str,messageJson: {},messageBytes: str) -> int:
"""Update the inbox queue """Update the inbox queue
""" """
# Check if the queue is full # Check if the queue is full
@ -410,11 +410,12 @@ class PubServer(BaseHTTPRequestHandler):
# save the json for later queue processing # save the json for later queue processing
queueFilename = \ queueFilename = \
savePostToInboxQueue(self.server.baseDir, \ savePostToInboxQueue(self.server.baseDir,
self.server.httpPrefix, \ self.server.httpPrefix,
nickname, \ nickname,
self.server.domainFull, \ self.server.domainFull,
messageJson, messageJson,
messageBytes.decode('utf-8'),
headersDict, headersDict,
self.path, self.path,
self.server.debug) self.server.debug)
@ -2750,7 +2751,7 @@ class PubServer(BaseHTTPRequestHandler):
else: else:
self.postToNickname=pathUsersSection.split('/')[0] self.postToNickname=pathUsersSection.split('/')[0]
if self.postToNickname: if self.postToNickname:
queueStatus=self._updateInboxQueue(self.postToNickname,messageJson) queueStatus=self._updateInboxQueue(self.postToNickname,messageJson,messageBytes)
if queueStatus==0: if queueStatus==0:
self.send_response(200) self.send_response(200)
self.end_headers() self.end_headers()
@ -2771,7 +2772,7 @@ class PubServer(BaseHTTPRequestHandler):
else: else:
if self.path == '/sharedInbox' or self.path == '/inbox': if self.path == '/sharedInbox' or self.path == '/inbox':
print('DEBUG: POST to shared inbox') print('DEBUG: POST to shared inbox')
queueStatus=self._updateInboxQueue('inbox',messageJson) queueStatus=self._updateInboxQueue('inbox',messageJson,messageBytes)
if queueStatus==0: if queueStatus==0:
self.send_response(200) self.send_response(200)
self.end_headers() self.end_headers()

View File

@ -19,12 +19,16 @@ import json
from time import gmtime, strftime from time import gmtime, strftime
from pprint import pprint from pprint import pprint
def messageContentDigest(messageBodyJsonStr: str) -> str:
return base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()).decode('utf-8')
def signPostHeaders(dateStr: str,privateKeyPem: str, \ def signPostHeaders(dateStr: str,privateKeyPem: str, \
nickname: str, \ nickname: str, \
domain: str,port: int, \ domain: str,port: int, \
toDomain: str,toPort: int, \ toDomain: str,toPort: int, \
path: str, \ path: str, \
httpPrefix: str, messageBodyJson: {}) -> str: httpPrefix: str, \
messageBodyJson: {}) -> str:
"""Returns a raw signature string that can be plugged into a header and """Returns a raw signature string that can be plugged into a header and
used to verify the authenticity of an HTTP transmission. used to verify the authenticity of an HTTP transmission.
""" """
@ -45,8 +49,7 @@ def signPostHeaders(dateStr: str,privateKeyPem: str, \
headers = {'(request-target)': f'post {path}','host': toDomain,'date': dateStr,'content-type': 'application/json'} headers = {'(request-target)': f'post {path}','host': toDomain,'date': dateStr,'content-type': 'application/json'}
else: else:
messageBodyJsonStr=json.dumps(messageBodyJson) messageBodyJsonStr=json.dumps(messageBodyJson)
bodyDigest = \ bodyDigest=messageContentDigest(messageBodyJsonStr)
base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()).decode('utf-8')
headers = {'(request-target)': f'post {path}','host': toDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/activity+json'} headers = {'(request-target)': f'post {path}','host': toDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/activity+json'}
privateKeyPem = RSA.import_key(privateKeyPem) privateKeyPem = RSA.import_key(privateKeyPem)
#headers.update({ #headers.update({
@ -101,8 +104,7 @@ def createSignedHeader(privateKeyPem: str,nickname: str, \
path,httpPrefix,None) path,httpPrefix,None)
else: else:
messageBodyJsonStr=json.dumps(messageBodyJson) messageBodyJsonStr=json.dumps(messageBodyJson)
bodyDigest = \ bodyDigest=messageContentDigest(messageBodyJsonStr)
base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()).decode('utf-8')
print('***************************Send (request-target): post '+path) print('***************************Send (request-target): post '+path)
print('***************************Send host: '+headerDomain) print('***************************Send host: '+headerDomain)
print('***************************Send date: '+dateStr) print('***************************Send date: '+dateStr)
@ -120,6 +122,7 @@ def createSignedHeader(privateKeyPem: str,nickname: str, \
def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
path: str,GETmethod: bool, \ path: str,GETmethod: bool, \
messageBodyDigest: str, \
messageBodyJsonStr: str) -> bool: messageBodyJsonStr: str) -> bool:
"""Returns true or false depending on if the key that we plugged in here """Returns true or false depending on if the key that we plugged in here
validates against the headers, method, and path. validates against the headers, method, and path.
@ -153,8 +156,10 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
f'(request-target): {method.lower()} {path}') f'(request-target): {method.lower()} {path}')
print('***************************Verify (request-target): '+method.lower()+' '+path) print('***************************Verify (request-target): '+method.lower()+' '+path)
elif signedHeader == 'digest': elif signedHeader == 'digest':
bodyDigest = \ if messageBodyDigest:
base64.b64encode(SHA256.new(messageBodyJsonStr.strip().encode()).digest()).decode('utf-8') bodyDigest=messageBodyDigest
else:
bodyDigest = messageContentDigest(messageBodyJsonStr)
signedHeaderList.append(f'digest: SHA-256={bodyDigest}') signedHeaderList.append(f'digest: SHA-256={bodyDigest}')
print('***************************Verify digest: SHA-256='+bodyDigest) print('***************************Verify digest: SHA-256='+bodyDigest)
print('***************************Verify messageBodyJsonStr: '+messageBodyJsonStr) print('***************************Verify messageBodyJsonStr: '+messageBodyJsonStr)

View File

@ -41,6 +41,7 @@ from like import undoLikesCollectionEntry
from blocking import isBlocked from blocking import isBlocked
from filters import isFiltered from filters import isFiltered
from announce import updateAnnounceCollection from announce import updateAnnounceCollection
from httpsig import messageContentDigest
def validInbox(baseDir: str,nickname: str,domain: str) -> bool: def validInbox(baseDir: str,nickname: str,domain: str) -> bool:
"""Checks whether files were correctly saved to the inbox """Checks whether files were correctly saved to the inbox
@ -165,7 +166,7 @@ def validPublishedDate(published) -> bool:
return False return False
return True return True
def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJsonObject: {},httpHeaders: {},postPath: str,debug: bool) -> str: def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJsonObject: {},messageBytes: str,httpHeaders: {},postPath: str,debug: bool) -> str:
"""Saves the give json to the inbox queue for the person """Saves the give json to the inbox queue for the person
keyId specifies the actor sending the post keyId specifies the actor sending the post
""" """
@ -241,6 +242,7 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str
'httpHeaders': httpHeaders, 'httpHeaders': httpHeaders,
'path': postPath, 'path': postPath,
'post': postJsonObject, 'post': postJsonObject,
'digest': messageContentDigest(messageBytes),
'filename': filename, 'filename': filename,
'destination': destination 'destination': destination
} }
@ -1156,6 +1158,7 @@ def runInboxQueue(projectVersion: str, \
pubKey, \ pubKey, \
queueJson['httpHeaders'], \ queueJson['httpHeaders'], \
queueJson['path'],False, \ queueJson['path'],False, \
queueJson['digest'], \
json.dumps(queueJson['post'])): json.dumps(queueJson['post'])):
if debug: if debug:
print('DEBUG: Header signature check failed') print('DEBUG: Header signature check failed')

View File

@ -111,14 +111,14 @@ def testHttpsigBase(withDigest):
boxpath, httpPrefix, messageBodyJson) boxpath, httpPrefix, messageBodyJson)
headers['signature'] = signatureHeader headers['signature'] = signatureHeader
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \ assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
boxpath,False, \ boxpath,False,None, \
messageBodyJsonStr) messageBodyJsonStr)
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \ assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
'/parambulator'+boxpath,False, \ '/parambulator'+boxpath,False,None, \
messageBodyJsonStr) == False messageBodyJsonStr) == False
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \ assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
boxpath,True, \ boxpath,True,None, \
messageBodyJsonStr) == False messageBodyJsonStr) == False
if not withDigest: if not withDigest:
# fake domain # fake domain
@ -130,7 +130,7 @@ def testHttpsigBase(withDigest):
headers = {'host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType} headers = {'host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType}
headers['signature'] = signatureHeader headers['signature'] = signatureHeader
assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \ assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
boxpath,True, \ boxpath,True,None, \
messageBodyJsonStr) == False messageBodyJsonStr) == False
os.chdir(baseDir) os.chdir(baseDir)
shutil.rmtree(path) shutil.rmtree(path)