From 3e69761bcea465dd5a8645dd917fdc7a2a1111a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Mon, 20 Feb 2012 09:03:11 +0100 Subject: Move OATH validation code to the 'crypto' module. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Raphaƫl Barrois --- crypto.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crypto.py (limited to 'crypto.py') diff --git a/crypto.py b/crypto.py new file mode 100644 index 0000000..3f22ccc --- /dev/null +++ b/crypto.py @@ -0,0 +1,37 @@ +# coding: utf-8 + + +class OATHValidator(object): + STATUS_OK = 'OK' + STATUS_BAD = 'BAD' + STATUS_NO_AUTH = 'NO_AUTH' + STATUS_NO_CLIENT = 'NO_CLIENT' + + def __init__(self, dbread_callback, dbwrite_callback): + self.dbread_callback = dbread_callback + self.dbwrite_callback = dbwrite_callback + + def test_HOTP(self, K, C, digits=6): + counter = ('%s' % C).rjust(16, '0').decode('hex') + HS = hmac.new(K, counter, hashlib.sha1).digest() + offset = ord(HS[19]) & 0xF + bin_code = int((chr(ord(HS[offset]) & 0x7F) + HS[offset+1:offset+4]).encode('hex'), 16) + return str(bin_code)[-digits:] + + def validate_OATH(self, OATH, publicID): + if len(OATH) % 2 != 0: + return self.STATUS_BAD + + token_data = self.dbread_callback(publicID=publicID) + if token_data.rowcount != 1: + return self.STATUS_BAD + + (actualcounter, key) = token_data.fetchone() + + K = key.decode('hex') + for C in range(actualcounter + 1, actualcounter + 256): + if OATH == self.test_HOTP(K, C, len(OATH)): + self.dbwrite_callback(counter=str(C), publicID=publicID) + return self.STATUS_OK + + return self.STATUS_NO_AUTH -- cgit v1.2.3