From 61d03b38b9667719e654ec37917cdc485b5199ad Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 10 Jun 2020 20:09:40 +0200 Subject: [PATCH] receiver receiverid challenge and find corresponding key --- owrx/controllers/status.py | 14 ++++++--- owrx/http.py | 22 +++++++------ owrx/receiverid.py | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 owrx/receiverid.py diff --git a/owrx/controllers/status.py b/owrx/controllers/status.py index beded28..05baece 100644 --- a/owrx/controllers/status.py +++ b/owrx/controllers/status.py @@ -1,11 +1,13 @@ from . import Controller -from owrx.client import ClientRegistry from owrx.version import openwebrx_version from owrx.sdr import SdrService from owrx.config import Config -import os +from owrx.receiverid import ReceiverId, KeyException import json -import pkg_resources + +import logging + +logger = logging.getLogger(__name__) class StatusController(Controller): @@ -27,7 +29,11 @@ class StatusController(Controller): def indexAction(self): pm = Config.get() - + if "Authorization" in self.request.headers: + try: + ReceiverId.getResponseHeader(self.request.headers["Authorization"]) + except KeyException: + logger.exception("error processing authorization header") status = { "receiver": { "name": pm["receiver_name"], diff --git a/owrx/http.py b/owrx/http.py index 812bec0..538bec9 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -35,19 +35,26 @@ class RequestHandler(BaseHTTPRequestHandler): logger.debug("%s - - [%s] %s", self.address_string(), self.log_date_time_string(), format % args) def do_GET(self): - self.router.route(self, "GET") + self.router.route(self, self.get_request("GET")) def do_POST(self): - self.router.route(self, "POST") + self.router.route(self, self.get_request("POST")) + + def get_request(self, method): + url = urlparse(self.path) + return Request(url, method, self.headers) class Request(object): - def __init__(self, url, method, cookies): + def __init__(self, url, method, headers): self.path = url.path self.query = parse_qs(url.query) self.matches = None self.method = method - self.cookies = cookies + self.headers = headers + self.cookies = SimpleCookie() + if "Cookie" in headers: + self.cookies.load(headers["Cookie"]) def setMatches(self, matches): self.matches = matches @@ -114,12 +121,7 @@ class Router(object): if r.matches(request): return r - def route(self, handler, method): - url = urlparse(handler.path) - cookies = SimpleCookie() - if "Cookie" in handler.headers: - cookies.load(handler.headers["Cookie"]) - request = Request(url, method, cookies) + def route(self, handler, request): route = self.find_route(request) if route is not None: controller = route.controller diff --git a/owrx/receiverid.py b/owrx/receiverid.py new file mode 100644 index 0000000..f2d9efe --- /dev/null +++ b/owrx/receiverid.py @@ -0,0 +1,63 @@ +import re +import logging +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) + # TODO sign challenge and respond + + @staticmethod + def findKey(challenge): + def parseKey(keyString): + try: + return Key(keyString) + except KeyError as e: + logger.error(e) + keys = [key for key in (parseKey(keyString) for keyString in Config.get()['receiver_keys']) if key is not None] + 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