mirror of https://gitlab.com/bashrc2/epicyon
flake8 format
parent
34abcb54c2
commit
844f898b94
111
httpsig.py
111
httpsig.py
|
@ -19,22 +19,23 @@ except ImportError:
|
|||
# from Crypto.Signature import PKCS1_v1_5
|
||||
from Crypto.Signature import pkcs1_15
|
||||
|
||||
from requests.auth import AuthBase
|
||||
import base64
|
||||
import json
|
||||
from time import gmtime, strftime
|
||||
import datetime
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
def messageContentDigest(messageBodyJsonStr: str) -> str:
|
||||
return base64.b64encode(SHA256.new(messageBodyJsonStr.encode('utf-8')).digest()).decode('utf-8')
|
||||
msg = messageBodyJsonStr.encode('utf-8')
|
||||
digestStr = SHA256.new(msg).digest()
|
||||
return base64.b64encode(digestStr).decode('utf-8')
|
||||
|
||||
def signPostHeaders(dateStr: str,privateKeyPem: str, \
|
||||
nickname: str, \
|
||||
domain: str,port: int, \
|
||||
toDomain: str,toPort: int, \
|
||||
path: str, \
|
||||
httpPrefix: str, \
|
||||
|
||||
def signPostHeaders(dateStr: str, privateKeyPem: str,
|
||||
nickname: str,
|
||||
domain: str, port: int,
|
||||
toDomain: str, toPort: int,
|
||||
path: str,
|
||||
httpPrefix: str,
|
||||
messageBodyJsonStr: str) -> str:
|
||||
"""Returns a raw signature string that can be plugged into a header and
|
||||
used to verify the authenticity of an HTTP transmission.
|
||||
|
@ -53,11 +54,23 @@ def signPostHeaders(dateStr: str,privateKeyPem: str, \
|
|||
dateStr = strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
|
||||
keyID = httpPrefix + '://' + domain + '/users/' + nickname + '#main-key'
|
||||
if not messageBodyJsonStr:
|
||||
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:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
contentLength = len(messageBodyJsonStr)
|
||||
headers={'(request-target)': f'post {path}','host': toDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/activity+json','content-length': str(contentLength)}
|
||||
headers = {
|
||||
'(request-target)': f'post {path}',
|
||||
'host': toDomain,
|
||||
'date': dateStr,
|
||||
'digest': f'SHA-256={bodyDigest}',
|
||||
'content-type': 'application/activity+json',
|
||||
'content-length': str(contentLength)
|
||||
}
|
||||
privateKeyPem = RSA.import_key(privateKeyPem)
|
||||
# headers.update({
|
||||
# '(request-target)': f'post {path}',
|
||||
|
@ -67,9 +80,7 @@ def signPostHeaders(dateStr: str,privateKeyPem: str, \
|
|||
signedHeaderText = ''
|
||||
for headerKey in signedHeaderKeys:
|
||||
signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
|
||||
#print(f'*********************signing: headerKey: {headerKey}: {headers[headerKey]}')
|
||||
signedHeaderText = signedHeaderText.strip()
|
||||
#print('******************************Send: signedHeaderText: '+signedHeaderText)
|
||||
headerDigest = SHA256.new(signedHeaderText.encode('ascii'))
|
||||
|
||||
# Sign the digest
|
||||
|
@ -87,10 +98,11 @@ def signPostHeaders(dateStr: str,privateKeyPem: str, \
|
|||
[f'{k}="{v}"' for k, v in signatureDict.items()])
|
||||
return signatureHeader
|
||||
|
||||
def createSignedHeader(privateKeyPem: str,nickname: str, \
|
||||
domain: str,port: int, \
|
||||
toDomain: str,toPort: int, \
|
||||
path: str,httpPrefix: str,withDigest: bool, \
|
||||
|
||||
def createSignedHeader(privateKeyPem: str, nickname: str,
|
||||
domain: str, port: int,
|
||||
toDomain: str, toPort: int,
|
||||
path: str, httpPrefix: str, withDigest: bool,
|
||||
messageBodyJsonStr: str) -> {}:
|
||||
"""Note that the domain is the destination, not the sender
|
||||
"""
|
||||
|
@ -105,22 +117,17 @@ def createSignedHeader(privateKeyPem: str,nickname: str, \
|
|||
dateStr = strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
|
||||
if not withDigest:
|
||||
headers = {
|
||||
'(request-target)': f'post {path}','host': headerDomain,'date': dateStr
|
||||
'(request-target)': f'post {path}',
|
||||
'host': headerDomain,
|
||||
'date': dateStr
|
||||
}
|
||||
signatureHeader = \
|
||||
signPostHeaders(dateStr,privateKeyPem,nickname, \
|
||||
domain,port,toDomain,toPort, \
|
||||
signPostHeaders(dateStr, privateKeyPem, nickname,
|
||||
domain, port, toDomain, toPort,
|
||||
path, httpPrefix, None)
|
||||
else:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
contentLength = len(messageBodyJsonStr)
|
||||
#print('***************************Send (request-target): post '+path)
|
||||
#print('***************************Send host: '+headerDomain)
|
||||
#print('***************************Send date: '+dateStr)
|
||||
#print('***************************Send digest: '+bodyDigest)
|
||||
#print('***************************Send Content-type: '+contentType)
|
||||
#print('***************************Send Content-Length: '+str(len(messageBodyJsonStr)))
|
||||
#print('***************************Send messageBodyJsonStr: '+messageBodyJsonStr)
|
||||
headers = {
|
||||
'(request-target)': f'post {path}',
|
||||
'host': headerDomain,
|
||||
|
@ -130,19 +137,21 @@ def createSignedHeader(privateKeyPem: str,nickname: str, \
|
|||
'content-type': contentType
|
||||
}
|
||||
signatureHeader = \
|
||||
signPostHeaders(dateStr,privateKeyPem,nickname, \
|
||||
domain,port, \
|
||||
toDomain,toPort, \
|
||||
signPostHeaders(dateStr, privateKeyPem, nickname,
|
||||
domain, port,
|
||||
toDomain, toPort,
|
||||
path, httpPrefix, messageBodyJsonStr)
|
||||
headers['signature'] = signatureHeader
|
||||
return headers
|
||||
|
||||
|
||||
def verifyRecentSignature(signedDateStr: str) -> bool:
|
||||
"""Checks whether the given time taken from the header is within
|
||||
12 hours of the current time
|
||||
"""
|
||||
currDate = datetime.datetime.utcnow()
|
||||
signedDate=datetime.datetime.strptime(signedDateStr,"%a, %d %b %Y %H:%M:%S %Z")
|
||||
dateFormat = "%a, %d %b %Y %H:%M:%S %Z"
|
||||
signedDate = datetime.datetime.strptime(signedDateStr, dateFormat)
|
||||
timeDiffSec = (currDate - signedDate).seconds
|
||||
# 12 hours tollerance
|
||||
if timeDiffSec > 43200:
|
||||
|
@ -155,9 +164,10 @@ def verifyRecentSignature(signedDateStr: str) -> bool:
|
|||
return False
|
||||
return True
|
||||
|
||||
def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
|
||||
path: str,GETmethod: bool, \
|
||||
messageBodyDigest: str, \
|
||||
|
||||
def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
path: str, GETmethod: bool,
|
||||
messageBodyDigest: str,
|
||||
messageBodyJsonStr: str, debug: bool) -> bool:
|
||||
"""Returns true or false depending on if the key that we plugged in here
|
||||
validates against the headers, method, and path.
|
||||
|
@ -183,8 +193,6 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
|
|||
k: v[1:-1]
|
||||
for k, v in [i.split('=', 1) for i in signatureHeader.split(',')]
|
||||
}
|
||||
#print('********************signatureHeader: '+str(signatureHeader))
|
||||
#print('********************signatureDict: '+str(signatureDict))
|
||||
|
||||
# Unpack the signed headers and set values based on current headers and
|
||||
# body (if a digest was included)
|
||||
|
@ -193,20 +201,18 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
|
|||
if debug:
|
||||
print('DEBUG: verifyPostHeaders signedHeader=' + signedHeader)
|
||||
if signedHeader == '(request-target)':
|
||||
signedHeaderList.append(
|
||||
f'(request-target): {method.lower()} {path}')
|
||||
#print('***************************Verify (request-target): '+method.lower()+' '+path)
|
||||
appendStr = f'(request-target): {method.lower()} {path}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif signedHeader == 'digest':
|
||||
if messageBodyDigest:
|
||||
bodyDigest = messageBodyDigest
|
||||
else:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
signedHeaderList.append(f'digest: SHA-256={bodyDigest}')
|
||||
#print('***************************Verify digest: SHA-256='+bodyDigest)
|
||||
#print('***************************Verify messageBodyJsonStr: '+messageBodyJsonStr)
|
||||
elif signedHeader == 'content-length':
|
||||
if headers.get(signedHeader):
|
||||
signedHeaderList.append(f'content-length: {headers[signedHeader]}')
|
||||
appendStr = f'content-length: {headers[signedHeader]}'
|
||||
signedHeaderList.append(appendStr)
|
||||
else:
|
||||
if headers.get('Content-Length'):
|
||||
contentLength = headers['Content-Length']
|
||||
|
@ -214,18 +220,21 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
|
|||
else:
|
||||
if headers.get('Content-length'):
|
||||
contentLength = headers['Content-length']
|
||||
signedHeaderList.append(f'content-length: {contentLength}')
|
||||
appendStr = f'content-length: {contentLength}'
|
||||
signedHeaderList.append(appendStr)
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders '+signedHeader+' not found in '+str(headers))
|
||||
print('DEBUG: verifyPostHeaders ' + signedHeader +
|
||||
' not found in ' + str(headers))
|
||||
else:
|
||||
if headers.get(signedHeader):
|
||||
if signedHeader == 'date':
|
||||
if not verifyRecentSignature(headers[signedHeader]):
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders date is not recent '+headers[signedHeader])
|
||||
print('DEBUG: ' +
|
||||
'verifyPostHeaders date is not recent ' +
|
||||
headers[signedHeader])
|
||||
return False
|
||||
#print('***************************Verify '+signedHeader+': '+headers[signedHeader])
|
||||
signedHeaderList.append(
|
||||
f'{signedHeader}: {headers[signedHeader]}')
|
||||
else:
|
||||
|
@ -233,20 +242,18 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
|
|||
if signedHeaderCap == 'Date':
|
||||
if not verifyRecentSignature(headers[signedHeaderCap]):
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders date is not recent '+headers[signedHeader])
|
||||
print('DEBUG: ' +
|
||||
'verifyPostHeaders date is not recent ' +
|
||||
headers[signedHeader])
|
||||
return False
|
||||
#print('***************************Verify '+signedHeaderCap+': '+headers[signedHeaderCap])
|
||||
if headers.get(signedHeaderCap):
|
||||
signedHeaderList.append(
|
||||
f'{signedHeader}: {headers[signedHeaderCap]}')
|
||||
|
||||
#print('***********************signedHeaderList: ')
|
||||
#pprint(signedHeaderList)
|
||||
if debug:
|
||||
print('DEBUG: signedHeaderList: ' + str(signedHeaderList))
|
||||
# Now we have our header data digest
|
||||
signedHeaderText = '\n'.join(signedHeaderList)
|
||||
#print('***********************Verify: signedHeaderText: '+signedHeaderText)
|
||||
headerDigest = SHA256.new(signedHeaderText.encode('ascii'))
|
||||
|
||||
# Get the signature, verify with public key, return result
|
||||
|
|
Loading…
Reference in New Issue