replace plaintext secret transmission with time-limited hmac-based bearer tokens
[hippotat] / hippotatlib / __init__.py
index 282266c..2fa3008 100644 (file)
@@ -50,6 +50,9 @@ from functools import partial
 
 import collections
 import time
+import hmac
+import hashlib
+import base64
 import codecs
 import traceback
 
@@ -139,6 +142,7 @@ port = 80
 vroutes = ''
 ifname_client = hippo%%d
 ifname_server = shippo%%d
+max_clock_skew = 300
 
 #[server] or [<client>] overrides
 ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip,%(ifname)s %(rnets)s
@@ -367,6 +371,34 @@ def crash_on_critical(event):
   if event.get('log_level') >= LogLevel.critical:
     crash(twisted.logger.formatEvent(event))
 
+#---------- authentication tokens ----------
+
+_authtoken_digest = hashlib.sha256
+
+def _authtoken_time():
+  return int(time.time())
+
+def _authtoken_hmac(secret, hextime):
+  return hmac.new(secret, hextime, _authtoken_digest).digest()
+
+def authtoken_make(secret):
+  hextime = ('%x' % _authtoken_time()).encode('ascii')
+  mac = _authtoken_hmac(secret, hextime)
+  return hextime + b' ' + base64.b64encode(mac)
+
+def authtoken_check(secret, token, maxskew):
+  (hextime, theirmac64) = token.split(b' ')
+  now = _authtoken_time()
+  then = int(hextime, 16)
+  skew = then - now;
+  if (abs(skew) > maxskew):
+    raise ValueError('too much clock skew (client %ds ahead)' % skew)
+  theirmac = base64.b64decode(theirmac64)
+  ourmac = _authtoken_hmac(secret, hextime)
+  if not hmac.compare_digest(theirmac, ourmac):
+    raise ValueError('invalid token (wrong secret?)')
+  pass
+
 #---------- config processing ----------
 
 def _cfg_process_putatives():