2020-06-10 18:09:40 +00:00
|
|
|
import re
|
|
|
|
import logging
|
2020-06-10 20:50:16 +00:00
|
|
|
import hashlib
|
2020-06-10 22:00:16 +00:00
|
|
|
import hmac
|
2020-06-10 20:50:16 +00:00
|
|
|
from datetime import datetime
|
2020-06-10 18:09:40 +00:00
|
|
|
from owrx.config import Config
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
keyRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{64})$")
|
|
|
|
keyChallengeRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{32})$")
|
|
|
|
headerRegex = re.compile("^ReceiverId (.*)$")
|
|
|
|
|
|
|
|
|
|
|
|
class KeyException(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Key(object):
|
|
|
|
def __init__(self, keyString):
|
|
|
|
matches = keyRegex.match(keyString)
|
|
|
|
if not matches:
|
|
|
|
raise KeyException("invalid key format")
|
|
|
|
self.source = matches.group(1)
|
|
|
|
self.id = matches.group(2)
|
|
|
|
self.secret = matches.group(3)
|
|
|
|
|
|
|
|
|
|
|
|
class KeyChallenge(object):
|
|
|
|
def __init__(self, challengeString):
|
|
|
|
matches = keyChallengeRegex.match(challengeString)
|
|
|
|
if not matches:
|
|
|
|
raise KeyException("invalid key challenge format")
|
|
|
|
self.source = matches.group(1)
|
|
|
|
self.id = matches.group(2)
|
|
|
|
self.challenge = matches.group(3)
|
|
|
|
|
|
|
|
|
|
|
|
class KeyResponse(object):
|
|
|
|
def __str__(self):
|
|
|
|
return "TODO"
|
|
|
|
|
|
|
|
|
|
|
|
class ReceiverId(object):
|
|
|
|
@staticmethod
|
|
|
|
def getResponseHeader(requestHeader):
|
|
|
|
matches = headerRegex.match(requestHeader)
|
|
|
|
if not matches:
|
|
|
|
raise KeyException("invalid authorization header")
|
|
|
|
challenge = KeyChallenge(matches.group(1))
|
|
|
|
key = ReceiverId.findKey(challenge)
|
2020-06-10 22:00:16 +00:00
|
|
|
if key is None:
|
|
|
|
return {}
|
2020-06-10 20:50:16 +00:00
|
|
|
time, signature = ReceiverId.signChallenge(challenge, key)
|
|
|
|
return {
|
|
|
|
"Signature": signature,
|
|
|
|
"Time": time,
|
|
|
|
}
|
2020-06-10 18:09:40 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def findKey(challenge):
|
|
|
|
def parseKey(keyString):
|
|
|
|
try:
|
|
|
|
return Key(keyString)
|
2020-06-10 22:00:16 +00:00
|
|
|
except KeyException as e:
|
2020-06-10 18:09:40 +00:00
|
|
|
logger.error(e)
|
2020-06-10 22:00:16 +00:00
|
|
|
keys = [parseKey(keyString) for keyString in Config.get()['receiver_keys']]
|
|
|
|
keys = [key for key in keys if key is not None]
|
2020-06-10 18:09:40 +00:00
|
|
|
matching_keys = [key for key in keys if key.source == challenge.source and key.id == challenge.id]
|
|
|
|
if matching_keys:
|
|
|
|
return matching_keys[0]
|
|
|
|
return None
|
2020-06-10 20:50:16 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def signChallenge(challenge, key):
|
|
|
|
now = datetime.utcnow().isoformat()
|
|
|
|
signString = "{challenge}:{time}".format(challenge=challenge.challenge, time=now)
|
2020-06-10 22:00:16 +00:00
|
|
|
m = hmac.new(bytes.fromhex(key.secret), msg=signString.encode('utf8'), digestmod=hashlib.sha256)
|
2020-06-10 20:50:16 +00:00
|
|
|
return now, m.hexdigest()
|