mirror of
https://gitlab.com/bashrc2/epicyon
synced 2026-11-04 19:52:33 +00:00
Improve support for hs2019 http signatures
This commit is contained in:
1 parent
e48e2a0fd1
commit
0ce7573104
3 files changed
+218
-117
No files matched your search
+51
-32
@@ -11,7 +11,7 @@ __module_group__ = "Security"
|
||||
# see https://tools.ietf.org/html/draft-cavage-http-signatures-06
|
||||
#
|
||||
# This might change in future
|
||||
# see https://tools.ietf.org/html/draft-ietf-httpbis-message-signatures-01
|
||||
# see https://tools.ietf.org/html/draft-ietf-httpbis-message-signatures
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
@@ -116,11 +116,11 @@ def signPostHeadersNew(dateStr: str, privateKeyPem: str,
|
||||
path: str,
|
||||
httpPrefix: str,
|
||||
messageBodyJsonStr: str,
|
||||
algorithm: str) -> (str, str):
|
||||
algorithm: str, debug: bool) -> (str, str):
|
||||
"""Returns a raw signature strings that can be plugged into a header
|
||||
as "Signature-Input" and "Signature"
|
||||
used to verify the authenticity of an HTTP transmission.
|
||||
See https://tools.ietf.org/html/draft-ietf-httpbis-message-signatures-01
|
||||
See https://tools.ietf.org/html/draft-ietf-httpbis-message-signatures
|
||||
"""
|
||||
domain = getFullDomain(domain, port)
|
||||
|
||||
@@ -137,18 +137,17 @@ def signPostHeadersNew(dateStr: str, privateKeyPem: str,
|
||||
keyID = localActorUrl(httpPrefix, nickname, domain) + '#main-key'
|
||||
if not messageBodyJsonStr:
|
||||
headers = {
|
||||
'*request-target': f'post {path}',
|
||||
'*created': str(secondsSinceEpoch),
|
||||
'@request-target': f'get {path}',
|
||||
'@created': str(secondsSinceEpoch),
|
||||
'host': toDomain,
|
||||
'date': dateStr,
|
||||
'content-type': 'application/json'
|
||||
'date': dateStr
|
||||
}
|
||||
else:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
contentLength = len(messageBodyJsonStr)
|
||||
headers = {
|
||||
'*request-target': f'post {path}',
|
||||
'*created': str(secondsSinceEpoch),
|
||||
'@request-target': f'post {path}',
|
||||
'@created': str(secondsSinceEpoch),
|
||||
'host': toDomain,
|
||||
'date': dateStr,
|
||||
'digest': f'SHA-256={bodyDigest}',
|
||||
@@ -164,6 +163,10 @@ def signPostHeadersNew(dateStr: str, privateKeyPem: str,
|
||||
signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
|
||||
signedHeaderText = signedHeaderText.strip()
|
||||
|
||||
if debug:
|
||||
print('\nsignPostHeadersNew signedHeaderText:\n' +
|
||||
signedHeaderText + '\nEND\n')
|
||||
|
||||
# Sign the digest. Potentially other signing algorithms can be added here.
|
||||
signature = ''
|
||||
if algorithm == 'rsa-sha512':
|
||||
@@ -298,8 +301,11 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
pubkey = load_pem_public_key(publicKeyPem.encode('utf-8'),
|
||||
backend=default_backend())
|
||||
# Build a dictionary of the signature values
|
||||
if headers.get('Signature-Input'):
|
||||
signatureHeader = headers['Signature-Input']
|
||||
if headers.get('Signature-Input') or headers.get('signature-input'):
|
||||
if headers.get('Signature-Input'):
|
||||
signatureHeader = headers['Signature-Input']
|
||||
else:
|
||||
signatureHeader = headers['signature-input']
|
||||
fieldSep2 = ','
|
||||
# split the signature input into separate fields
|
||||
signatureDict = {
|
||||
@@ -342,15 +348,23 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
# original Mastodon http signature
|
||||
appendStr = f'(request-target): {method.lower()} {path}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif '*request-target' in signedHeader:
|
||||
elif '@request-target' in signedHeader:
|
||||
# https://tools.ietf.org/html/
|
||||
# draft-ietf-httpbis-message-signatures-01
|
||||
appendStr = f'*request-target: {method.lower()} {path}'
|
||||
# remove ()
|
||||
# if appendStr.startswith('('):
|
||||
# appendStr = appendStr.split('(')[1]
|
||||
# if ')' in appendStr:
|
||||
# appendStr = appendStr.split(')')[0]
|
||||
# draft-ietf-httpbis-message-signatures
|
||||
appendStr = f'@request-target: {method.lower()} {path}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif '@created' in signedHeader:
|
||||
if signatureDict.get('created'):
|
||||
createdStr = str(signatureDict['created'])
|
||||
appendStr = f'@created: {createdStr}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif '@expires' in signedHeader:
|
||||
if signatureDict.get('expires'):
|
||||
expiresStr = str(signatureDict['expires'])
|
||||
appendStr = f'@expires: {expiresStr}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif '@method' in signedHeader:
|
||||
appendStr = f'@expires: {method}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif signedHeader == 'algorithm':
|
||||
if headers.get(signedHeader):
|
||||
@@ -430,14 +444,18 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
# Now we have our header data digest
|
||||
signedHeaderText = '\n'.join(signedHeaderList)
|
||||
if debug:
|
||||
print('signedHeaderText:\n' + signedHeaderText + 'END')
|
||||
print('\nverifyPostHeaders signedHeaderText:\n' +
|
||||
signedHeaderText + '\nEND\n')
|
||||
|
||||
# Get the signature, verify with public key, return result
|
||||
signature = None
|
||||
if headers.get('Signature-Input') and headers.get('Signature'):
|
||||
if (headers.get('Signature-Input') and headers.get('Signature')) or \
|
||||
(headers.get('signature-input') and headers.get('signature')):
|
||||
# https://tools.ietf.org/html/
|
||||
# draft-ietf-httpbis-message-signatures-01
|
||||
headersSig = headers['Signature']
|
||||
# draft-ietf-httpbis-message-signatures
|
||||
if headers.get('Signature'):
|
||||
headersSig = headers['Signature']
|
||||
else:
|
||||
headersSig = headers['signature']
|
||||
# remove sig1=:
|
||||
if requestTargetKey + '=:' in headersSig:
|
||||
headersSig = headersSig.split(requestTargetKey + '=:')[1]
|
||||
@@ -445,10 +463,10 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
signature = base64.b64decode(headersSig)
|
||||
else:
|
||||
# Original Mastodon signature
|
||||
signature = base64.b64decode(signatureDict['signature'])
|
||||
if debug:
|
||||
print('signature: ' + algorithm + ' ' +
|
||||
signatureDict['signature'])
|
||||
headersSig = signatureDict['signature']
|
||||
signature = base64.b64decode(headersSig)
|
||||
if debug:
|
||||
print('signature: ' + algorithm + ' ' + headersSig)
|
||||
|
||||
# log unusual signing algorithms
|
||||
if signatureDict.get('alg'):
|
||||
@@ -457,11 +475,12 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
||||
# If extra signing algorithms need to be added then do it here
|
||||
if not signatureDict.get('alg'):
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
elif signatureDict['alg'] == 'rsa-sha256':
|
||||
elif (signatureDict['alg'] == 'rsa-sha256' or
|
||||
signatureDict['alg'] == 'rsa-v1_5-sha256' or
|
||||
signatureDict['alg'] == 'hs2019'):
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
elif signatureDict['alg'] == 'hs2019':
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
elif signatureDict['alg'] == 'rsa-sha512':
|
||||
elif (signatureDict['alg'] == 'rsa-sha512' or
|
||||
signatureDict['alg'] == 'rsa-pss-sha512'):
|
||||
alg = hazutils.Prehashed(hashes.SHA512())
|
||||
else:
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
|
||||
Reference in new issue
Block a user