summaryrefslogtreecommitdiffstats
path: root/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils.py')
-rw-r--r--utils.py34
1 files changed, 24 insertions, 10 deletions
diff --git a/utils.py b/utils.py
index b749444..49a69d3 100644
--- a/utils.py
+++ b/utils.py
@@ -3,12 +3,16 @@ import hmac
import ldap
import pickle
import re
+import struct
+import subprocess
+from base64 import urlsafe_b64encode, urlsafe_b64decode
from Crypto.Cipher import AES
from email.mime.text import MIMEText
from functools import wraps
from flask import current_app, flash, g, redirect, render_template, request, session, url_for
from hashlib import sha1
from random import randint
+from time import time
from werkzeug.exceptions import Forbidden
@@ -86,7 +90,7 @@ def decrypt_password(ciphertext):
return encryptor.decrypt(ciphertext[16:]).rstrip('\0')
-def create_confirmation(realm, data):
+def make_confirmation(realm, data):
"""
Create a confirmation token e.g. for confirmation mails.
@@ -94,26 +98,36 @@ def create_confirmation(realm, data):
some data (pickle-able).
"""
key = '\0'.join((current_app.config['SECRET_KEY'], realm))
- payload = pickle.dumps(data)
+ payload = ''.join((struct.pack('>i', time()), pickle.dumps(data)))
mac = hmac.new(key, payload, sha1)
- return ''.join((mac.digest(), payload)).encode('base64').strip()
+ return urlsafe_b64encode(''.join((mac.digest(), payload))).rstrip('=')
-class InvalidConfirmation(ValueError):
+class ConfirmationInvalid(ValueError):
"""Raised by `verify_confirmation` on invalid input data"""
-def verify_confirmation(realm, token):
+class ConfirmationTimeout(ValueError):
+ """Raised by `verify_confirmation` when the input data is too old"""
+
+def verify_confirmation(realm, token, timeout=None):
"""
- Verify a confirmation created by `create_confirmation` and, if it is
+ Verify a confirmation token created by `make_confirmation` and, if it is
valid, return the original data.
+ If `timeout` is given, only accept the token if it is less than `timeout`
+ seconds old.
"""
key = '\0'.join((current_app.config['SECRET_KEY'], realm))
- token = token.decode('base64')
+ token = urlsafe_b64decode(token + '=' * (4 - len(token) % 4))
mac = token[:20]
- payload = token[20:]
+ tokentime = struct.unpack('>i', token[20:24])[0]
+ payload = token[24:]
+
+ if mac != hmac.new(key, token[20:], sha1).digest():
+ raise ConfirmationInvalid('MAC does not match')
- if mac != hmac.new(key, payload, sha1).digest():
- raise InvalidConfirmation('MAC does not match')
+ print '%d+%d=%d <> %d' % (tokentime, timeout, tokentime+timeout, time())
+ if timeout is not None and time() > tokentime + timeout:
+ raise ConfirmationTimeout('Token is too old')
return pickle.loads(payload)