multi-key signing implementation

This commit is contained in:
Jakob Ketterl 2020-07-09 21:32:57 +02:00
parent 7eb0a8cf7e
commit e53f1f60eb
3 changed files with 26 additions and 23 deletions

View File

@ -18,10 +18,6 @@ class Controller(object):
if max_age is not None: if max_age is not None:
headers["Cache-Control"] = "max-age: {0}".format(max_age) headers["Cache-Control"] = "max-age: {0}".format(max_age)
for key, value in headers.items(): for key, value in headers.items():
if isinstance(value, list):
for v in value:
self.handler.send_header(key, v)
else:
self.handler.send_header(key, value) self.handler.send_header(key, value)
self.handler.end_headers() self.handler.end_headers()
if type(content) == str: if type(content) == str:

View File

@ -6,16 +6,17 @@ from datetime import datetime
class ReceiverIdController(Controller): class ReceiverIdController(Controller):
def __init__(self, handler, request, options): def __init__(self, handler, request, options):
super().__init__(handler, request, options) super().__init__(handler, request, options)
self.authHeaders = [] self.authHeader = None
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None, headers=None): def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None, headers=None):
if self.authHeader is not None:
if headers is None: if headers is None:
headers = {} headers = {}
headers['Authorization'] = self.authHeaders headers['Authorization'] = self.authHeader
super().send_response(content, code=code, content_type=content_type, last_modified=last_modified, max_age=max_age, headers=headers) super().send_response(content, code=code, content_type=content_type, last_modified=last_modified, max_age=max_age, headers=headers)
pass pass
def handle_request(self): def handle_request(self):
headers = self.request.headers.get_all("Authorization", []) if "Authorization" in self.request.headers:
self.authHeaders = [ReceiverId.getResponseHeader(h) for h in headers] self.authHeader = ReceiverId.getResponseHeader(self.request.headers['Authorization'])
super().handle_request() super().handle_request()

View File

@ -2,7 +2,7 @@ import re
import logging import logging
import hashlib import hashlib
import hmac import hmac
from datetime import datetime from datetime import datetime, timezone
from owrx.config import Config from owrx.config import Config
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,18 +38,18 @@ class KeyChallenge(object):
class KeyResponse(object): class KeyResponse(object):
def __init__(self, source, id, time: datetime, signature): def __init__(self, source, id, time, signature):
self.source = source self.source = source
self.id = id self.id = id
self.time = time self.time = time
self.signature = signature self.signature = signature
def __str__(self): def __str__(self):
return "Time={time}, Response={source}-{id}-{signature}".format( return "{source}-{id}-{time}-{signature}".format(
source=self.source, source=self.source,
id=self.id, id=self.id,
time=self.time,
signature=self.signature, signature=self.signature,
time=self.time
) )
@ -59,12 +59,17 @@ class ReceiverId(object):
matches = headerRegex.match(requestHeader) matches = headerRegex.match(requestHeader)
if not matches: if not matches:
raise KeyException("invalid authorization header") raise KeyException("invalid authorization header")
challenge = KeyChallenge(matches.group(1)) challenges = [KeyChallenge(i) for i in matches.group(1).split(",")]
def signChallenge(challenge):
key = ReceiverId.findKey(challenge) key = ReceiverId.findKey(challenge)
if key is None: if key is None:
return {} return
return ReceiverId.signChallenge(challenge, key) return ReceiverId.signChallenge(challenge, key)
responses = [signChallenge(c) for c in challenges]
return ",".join(str(r) for r in responses if r is not None)
@staticmethod @staticmethod
def findKey(challenge): def findKey(challenge):
def parseKey(keyString): def parseKey(keyString):
@ -81,8 +86,9 @@ class ReceiverId(object):
@staticmethod @staticmethod
def signChallenge(challenge, key): def signChallenge(challenge, key):
now = datetime.utcnow().isoformat() now = datetime.utcnow().replace(microsecond=0, tzinfo=timezone.utc)
now_bytes = int(now.timestamp()).to_bytes(4, byteorder="big")
m = hmac.new(bytes.fromhex(key.secret), digestmod=hashlib.sha256) m = hmac.new(bytes.fromhex(key.secret), digestmod=hashlib.sha256)
m.update(bytes.fromhex(challenge.challenge)) m.update(bytes.fromhex(challenge.challenge))
m.update(now.encode('utf8')) m.update(now_bytes)
return KeyResponse(challenge.source, challenge.id, now, m.hexdigest()) return KeyResponse(challenge.source, challenge.id, now_bytes.hex(), m.hexdigest())