forked from indymedia/epicyon
Calculate message body digest from incoming bytes to avoid any json conversion issues
parent
ffde81d909
commit
1b1810ff8a
15
daemon.py
15
daemon.py
|
@ -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()
|
||||||
|
|
19
httpsig.py
19
httpsig.py
|
@ -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)
|
||||||
|
|
5
inbox.py
5
inbox.py
|
@ -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')
|
||||||
|
|
14
tests.py
14
tests.py
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue