mirror of https://gitlab.com/bashrc2/epicyon
				
				
				
			http signature fixes
							parent
							
								
									ed050d48f5
								
							
						
					
					
						commit
						977160eecf
					
				| 
						 | 
					@ -400,6 +400,7 @@ class PubServer(BaseHTTPRequestHandler):
 | 
				
			||||||
                                 messageJson,
 | 
					                                 messageJson,
 | 
				
			||||||
                                 self.headers['host'],
 | 
					                                 self.headers['host'],
 | 
				
			||||||
                                 self.headers['signature'],
 | 
					                                 self.headers['signature'],
 | 
				
			||||||
 | 
					                                 self.headers,
 | 
				
			||||||
                                 '/'+self.path.split('/')[-1],
 | 
					                                 '/'+self.path.split('/')[-1],
 | 
				
			||||||
                                 self.server.debug)
 | 
					                                 self.server.debug)
 | 
				
			||||||
        if queueFilename:
 | 
					        if queueFilename:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										39
									
								
								httpsig.py
								
								
								
								
							
							
						
						
									
										39
									
								
								httpsig.py
								
								
								
								
							| 
						 | 
					@ -28,20 +28,21 @@ def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \
 | 
				
			||||||
    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
					    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
				
			||||||
    keyID = httpPrefix+'://'+domain+'/users/'+nickname+'#main-key'
 | 
					    keyID = httpPrefix+'://'+domain+'/users/'+nickname+'#main-key'
 | 
				
			||||||
    if not messageBodyJson:
 | 
					    if not messageBodyJson:
 | 
				
			||||||
        headers = {'host': domain}
 | 
					        headers = {'(request-target)': f'post {path}','host': domain,'date': dateStr,'content-type': 'application/json'}
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        bodyDigest = \
 | 
					        bodyDigest = \
 | 
				
			||||||
            base64.b64encode(SHA256.new(messageBodyJson.encode()).digest())
 | 
					            base64.b64encode(SHA256.new(messageBodyJson.encode()).digest())
 | 
				
			||||||
        headers = {'host': domain,'digest': f'SHA-256={bodyDigest}'}
 | 
					        headers = {'(request-target)': f'post {path}','host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/json'}
 | 
				
			||||||
    privateKeyPem = RSA.import_key(privateKeyPem)
 | 
					    privateKeyPem = RSA.import_key(privateKeyPem)
 | 
				
			||||||
    headers.update({
 | 
					    #headers.update({
 | 
				
			||||||
        '(request-target)': f'post {path}',
 | 
					    #    '(request-target)': f'post {path}',
 | 
				
			||||||
    })
 | 
					    #})
 | 
				
			||||||
    # build a digest for signing
 | 
					    # build a digest for signing
 | 
				
			||||||
    signedHeaderKeys = headers.keys()
 | 
					    signedHeaderKeys = headers.keys()
 | 
				
			||||||
    signedHeaderText = ''
 | 
					    signedHeaderText = ''
 | 
				
			||||||
    for headerKey in signedHeaderKeys:
 | 
					    for headerKey in signedHeaderKeys:
 | 
				
			||||||
        signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
 | 
					        signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
 | 
				
			||||||
 | 
					        #print(f'headerKey: {headerKey}: {headers[headerKey]}')
 | 
				
			||||||
    signedHeaderText = signedHeaderText.strip()
 | 
					    signedHeaderText = signedHeaderText.strip()
 | 
				
			||||||
    headerDigest = SHA256.new(signedHeaderText.encode('ascii'))
 | 
					    headerDigest = SHA256.new(signedHeaderText.encode('ascii'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,13 +54,11 @@ def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \
 | 
				
			||||||
    signatureDict = {
 | 
					    signatureDict = {
 | 
				
			||||||
        'keyId': keyID,
 | 
					        'keyId': keyID,
 | 
				
			||||||
        'algorithm': 'rsa-sha256',
 | 
					        'algorithm': 'rsa-sha256',
 | 
				
			||||||
#        'date': dateStr,
 | 
					 | 
				
			||||||
        'headers': ' '.join(signedHeaderKeys),
 | 
					        'headers': ' '.join(signedHeaderKeys),
 | 
				
			||||||
        'signature': signature
 | 
					        'signature': signature
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    signatureHeader = ','.join(
 | 
					    signatureHeader = ','.join(
 | 
				
			||||||
        [f'{k}="{v}"' for k, v in signatureDict.items()])
 | 
					        [f'{k}="{v}"' for k, v in signatureDict.items()])
 | 
				
			||||||
#    print('signatureHeader: '+str(signatureHeader))
 | 
					 | 
				
			||||||
    return signatureHeader
 | 
					    return signatureHeader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \
 | 
					def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \
 | 
				
			||||||
| 
						 | 
					@ -67,23 +66,23 @@ def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \
 | 
				
			||||||
                       messageBodyJson: {}) -> {}:
 | 
					                       messageBodyJson: {}) -> {}:
 | 
				
			||||||
    headerDomain=domain
 | 
					    headerDomain=domain
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if port:
 | 
				
			||||||
        if port!=80 and port!=443:
 | 
					        if port!=80 and port!=443:
 | 
				
			||||||
            headerDomain=headerDomain+':'+str(port)
 | 
					            headerDomain=headerDomain+':'+str(port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
					    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
				
			||||||
 | 
					    path='/inbox'
 | 
				
			||||||
    if not withDigest:
 | 
					    if not withDigest:
 | 
				
			||||||
        headers = {'host': headerDomain}
 | 
					        headers = {'(request-target)': f'post {path}','host': headerDomain,'date': dateStr}
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        messageBodyJsonStr=json.dumps(messageBodyJson)
 | 
					        messageBodyJsonStr=json.dumps(messageBodyJson)
 | 
				
			||||||
        bodyDigest = \
 | 
					        bodyDigest = \
 | 
				
			||||||
            base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
					            base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
				
			||||||
        headers = {'host': headerDomain, 'digest': f'SHA-256={bodyDigest}'}        
 | 
					        headers = {'(request-target)': f'post {path}','host': headerDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/json'}
 | 
				
			||||||
    path='/inbox'
 | 
					 | 
				
			||||||
    signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, \
 | 
					    signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, \
 | 
				
			||||||
                                      path, httpPrefix, None)
 | 
					                                      path, httpPrefix, None)
 | 
				
			||||||
    headers['date'] = dateStr
 | 
					 | 
				
			||||||
    headers['signature'] = signatureHeader
 | 
					    headers['signature'] = signatureHeader
 | 
				
			||||||
    headers['Content-type'] = 'application/json'
 | 
					    #print('******************************************************http headers: '+str(headers))
 | 
				
			||||||
    return headers
 | 
					    return headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
 | 
					def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
 | 
				
			||||||
| 
						 | 
					@ -109,6 +108,8 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict, \
 | 
				
			||||||
        k: v[1:-1]
 | 
					        k: v[1:-1]
 | 
				
			||||||
        for k, v in [i.split('=', 1) for i in signatureHeader.split(',')]
 | 
					        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
 | 
					    # Unpack the signed headers and set values based on current headers and
 | 
				
			||||||
    # body (if a digest was included)
 | 
					    # body (if a digest was included)
 | 
				
			||||||
| 
						 | 
					@ -117,21 +118,19 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict, \
 | 
				
			||||||
        if signedHeader == '(request-target)':
 | 
					        if signedHeader == '(request-target)':
 | 
				
			||||||
            signedHeaderList.append(
 | 
					            signedHeaderList.append(
 | 
				
			||||||
                f'(request-target): {method.lower()} {path}')
 | 
					                f'(request-target): {method.lower()} {path}')
 | 
				
			||||||
        elif signedHeader.lower() == 'content-type':
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        elif signedHeader == 'date':
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        elif signedHeader == 'digest':
 | 
					        elif signedHeader == 'digest':
 | 
				
			||||||
            bodyDigest = \
 | 
					            bodyDigest = \
 | 
				
			||||||
                base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
					                base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
				
			||||||
            signedHeaderList.append(f'digest: SHA-256={bodyDigest}')
 | 
					            signedHeaderList.append(f'digest: SHA-256={bodyDigest}')
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            try:
 | 
					            if headers.get(signedHeader):
 | 
				
			||||||
                signedHeaderList.append(
 | 
					                signedHeaderList.append(
 | 
				
			||||||
                    f'{signedHeader}: {headers[signedHeader]}')
 | 
					                    f'{signedHeader}: {headers[signedHeader]}')
 | 
				
			||||||
            except Exception as e:
 | 
					            else:
 | 
				
			||||||
                print('http signature check failure')
 | 
					                signedHeaderCap=signedHeader.capitalize()
 | 
				
			||||||
                print(e)
 | 
					                if headers.get(signedHeaderCap):
 | 
				
			||||||
 | 
					                    signedHeaderList.append(
 | 
				
			||||||
 | 
					                        f'{signedHeader}: {headers[signedHeaderCap]}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Now we have our header data digest
 | 
					    # Now we have our header data digest
 | 
				
			||||||
    signedHeaderText = '\n'.join(signedHeaderList)
 | 
					    signedHeaderText = '\n'.join(signedHeaderList)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								inbox.py
								
								
								
								
							
							
						
						
									
										11
									
								
								inbox.py
								
								
								
								
							| 
						 | 
					@ -164,7 +164,7 @@ def validPublishedDate(published) -> bool:
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
    return True
 | 
					    return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJsonObject: {},host: str,headers: str,postPath: str,debug: bool) -> str:
 | 
					def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJsonObject: {},host: str,headers: str,httpHeaders: str,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
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -225,6 +225,7 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str
 | 
				
			||||||
        'published': published,
 | 
					        'published': published,
 | 
				
			||||||
        'host': host,
 | 
					        'host': host,
 | 
				
			||||||
        'headers': headers,
 | 
					        'headers': headers,
 | 
				
			||||||
 | 
					        'httpHeaders': httpHeaders,
 | 
				
			||||||
        'path': postPath,
 | 
					        'path': postPath,
 | 
				
			||||||
        'post': postJsonObject,
 | 
					        'post': postJsonObject,
 | 
				
			||||||
        'filename': filename,
 | 
					        'filename': filename,
 | 
				
			||||||
| 
						 | 
					@ -1135,15 +1136,11 @@ def runInboxQueue(projectVersion: str, \
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # check the signature
 | 
					            # check the signature
 | 
				
			||||||
            verifyHeaders={
 | 
					 | 
				
			||||||
                'host': queueJson['host'],
 | 
					 | 
				
			||||||
                'signature': queueJson['headers']
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if debug:
 | 
					            if debug:
 | 
				
			||||||
                print('DEBUG: checking http headers')
 | 
					                print('DEBUG: checking http headers')
 | 
				
			||||||
                pprint(verifyHeaders)
 | 
					                pprint(queueJson['headers'])
 | 
				
			||||||
            if not verifyPostHeaders(httpPrefix, \
 | 
					            if not verifyPostHeaders(httpPrefix, \
 | 
				
			||||||
                                     pubKey,verifyHeaders, \
 | 
					                                     pubKey,queueJson['headers'], \
 | 
				
			||||||
                                     queueJson['path'],False, \
 | 
					                                     queueJson['path'],False, \
 | 
				
			||||||
                                     json.dumps(queueJson['post'])):
 | 
					                                     json.dumps(queueJson['post'])):
 | 
				
			||||||
                if debug:
 | 
					                if debug:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,7 @@ def getJson(session,url: str,headers: {},params: {}, \
 | 
				
			||||||
        print('WARN: no session specified for getJson')
 | 
					        print('WARN: no session specified for getJson')
 | 
				
			||||||
    session.cookies.clear()
 | 
					    session.cookies.clear()
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        result=session.get(url, headers=sessionHeaders, params=sessionParams, allow_redirects=True)
 | 
					        result=session.get(url, headers=sessionHeaders, params=sessionParams)
 | 
				
			||||||
        return result.json()
 | 
					        return result.json()
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        print('ERROR: getJson failed')
 | 
					        print('ERROR: getJson failed')
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,7 @@ def postJson(session,postJsonObject: {},federationList: [],inboxUrl: str,headers
 | 
				
			||||||
            print('postJson: '+inboxUrl+' not permitted')
 | 
					            print('postJson: '+inboxUrl+' not permitted')
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers, allow_redirects=True)
 | 
					    postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers)
 | 
				
			||||||
    return postResult.text
 | 
					    return postResult.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def postImage(session,attachImageFilename: str,federationList: [],inboxUrl: str,headers: {},capability: str) -> str:
 | 
					def postImage(session,attachImageFilename: str,federationList: [],inboxUrl: str,headers: {},capability: str) -> str:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										26
									
								
								tests.py
								
								
								
								
							
							
						
						
									
										26
									
								
								tests.py
								
								
								
								
							| 
						 | 
					@ -82,7 +82,7 @@ def testHttpsigBase(withDigest):
 | 
				
			||||||
    privateKeyPem,publicKeyPem,person,wfEndpoint= \
 | 
					    privateKeyPem,publicKeyPem,person,wfEndpoint= \
 | 
				
			||||||
        createPerson(path,nickname,domain,port,httpPrefix,False,password)
 | 
					        createPerson(path,nickname,domain,port,httpPrefix,False,password)
 | 
				
			||||||
    assert privateKeyPem
 | 
					    assert privateKeyPem
 | 
				
			||||||
    messageBodyJsonStr = '{"a key": "a value", "another key": "A string","yet another key": "A string"}'
 | 
					    messageBodyJsonStr = '{"a key": "a value", "another key": "A string","yet another key": "Another string"}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    headersDomain=domain
 | 
					    headersDomain=domain
 | 
				
			||||||
    if port!=80 and port !=443:
 | 
					    if port!=80 and port !=443:
 | 
				
			||||||
| 
						 | 
					@ -90,39 +90,43 @@ def testHttpsigBase(withDigest):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
					    dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
 | 
				
			||||||
    if not withDigest:
 | 
					    if not withDigest:
 | 
				
			||||||
        headers = {'host': headersDomain}
 | 
					        headers = {'host': headersDomain,'date': dateStr,'content-type': 'application/json'}
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        bodyDigest = \
 | 
					        bodyDigest = \
 | 
				
			||||||
            base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
					            base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
				
			||||||
        headers = {'host': headersDomain, 'date': dateStr, 'digest': f'SHA-256={bodyDigest}'}
 | 
					        headers = {'host': headersDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/json'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    boxpath='/inbox'
 | 
					    boxpath='/inbox'
 | 
				
			||||||
    signatureHeader = \
 | 
					    signatureHeader = \
 | 
				
			||||||
        signPostHeaders(privateKeyPem, nickname, domain, port, boxpath, httpPrefix, None)
 | 
					        signPostHeaders(privateKeyPem, nickname, domain, port, boxpath, httpPrefix, None)
 | 
				
			||||||
    headers['signature'] = signatureHeader
 | 
					    headers['signature'] = signatureHeader
 | 
				
			||||||
    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
					    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
				
			||||||
                             '/inbox' ,False, messageBodyJsonStr)
 | 
					                             '/inbox' ,False, \
 | 
				
			||||||
 | 
					                             messageBodyJsonStr)
 | 
				
			||||||
    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
					    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
				
			||||||
                             '/parambulator/inbox', False , messageBodyJsonStr) == False
 | 
					                             '/parambulator/inbox',False, \
 | 
				
			||||||
 | 
					                             messageBodyJsonStr) == False
 | 
				
			||||||
    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
					    assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
 | 
				
			||||||
                             '/inbox', True, messageBodyJsonStr) == False
 | 
					                             '/inbox',True, \
 | 
				
			||||||
 | 
					                             messageBodyJsonStr) == False
 | 
				
			||||||
    if not withDigest:
 | 
					    if not withDigest:
 | 
				
			||||||
        # fake domain
 | 
					        # fake domain
 | 
				
			||||||
        headers = {'host': 'bogon.domain'}
 | 
					        headers = {'host': 'bogon.domain','date': dateStr,'content-type': 'application/json'}
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        # correct domain but fake message
 | 
					        # correct domain but fake message
 | 
				
			||||||
        messageBodyJsonStr = '{"a key": "a value", "another key": "Fake GNUs", "yet another key": "Fake GNUs"}'
 | 
					        messageBodyJsonStr = '{"a key": "a value", "another key": "Fake GNUs", "yet another key": "More Fake GNUs"}'
 | 
				
			||||||
        bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
					        bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
 | 
				
			||||||
        headers = {'host': domain, 'date': dateStr, 'digest': f'SHA-256={bodyDigest}'}        
 | 
					        headers = {'host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': 'application/json'}
 | 
				
			||||||
    headers['signature'] = signatureHeader
 | 
					    headers['signature'] = signatureHeader
 | 
				
			||||||
    assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
 | 
					    assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
 | 
				
			||||||
                             '/inbox', True, messageBodyJsonStr) == False
 | 
					                             '/inbox',True, \
 | 
				
			||||||
 | 
					                             messageBodyJsonStr) == False
 | 
				
			||||||
    os.chdir(baseDir)
 | 
					    os.chdir(baseDir)
 | 
				
			||||||
    shutil.rmtree(path)
 | 
					    shutil.rmtree(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def testHttpsig():
 | 
					def testHttpsig():
 | 
				
			||||||
    testHttpsigBase(False)
 | 
					 | 
				
			||||||
    testHttpsigBase(True)
 | 
					    testHttpsigBase(True)
 | 
				
			||||||
 | 
					    testHttpsigBase(False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def testCache():
 | 
					def testCache():
 | 
				
			||||||
    print('testCache')
 | 
					    print('testCache')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue