Source code for linotp.lib.HMAC
# -*- coding: utf-8 -*-
#
# LinOTP - the open source solution for two factor authentication
# Copyright (C) 2010 - 2014 LSE Leading Security Experts GmbH
#
# This file is part of LinOTP server.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public
# License, version 3, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the
# GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
# E-mail: linotp@lsexperts.de
# Contact: www.linotp.org
# Support: www.lsexperts.de
#
"""
HMAC-OTP (RFC 4226)
"""
import hmac
import logging
import binascii
import struct
from hashlib import sha1
from linotp.lib.crypt import zerome
import sys
(ma, mi, _, _, _,) = sys.version_info
pver = float(int(ma) + int(mi) * 0.1)
log = logging.getLogger(__name__)
[docs]class HmacOtp():
def __init__(self, secObj=None, counter=0, digits=6, hashfunc=sha1):
self.secretObj = secObj
self.counter = counter
self.digits = digits
self.hashfunc = hashfunc
[docs] def hmac(self, counter=None, key=None):
#log.error("hmacSecret()")
counter = counter or self.counter
data_input = struct.pack(">Q", counter)
if key == None:
dig = str(self.secretObj.hmac_digest(data_input, self.hashfunc))
else:
if pver > 2.6:
dig = hmac.new(key, data_input, self.hashfunc).digest()
else:
dig = hmac.new(key, str(data_input), self.hashfunc).digest()
return dig
[docs] def truncate(self, digest):
offset = ord(digest[-1:]) & 0x0f
binary = (ord(digest[offset + 0]) & 0x7f) << 24
binary |= (ord(digest[offset + 1]) & 0xff) << 16
binary |= (ord(digest[offset + 2]) & 0xff) << 8
binary |= (ord(digest[offset + 3]) & 0xff)
return binary % (10 ** self.digits)
[docs] def generate(self, counter=None, inc_counter=True, key=None):
counter = counter or self.counter
otp = str(self.truncate(self.hmac(counter=counter, key=key)))
""" fill in the leading zeros """
sotp = (self.digits - len(otp)) * "0" + otp
#log.debug("[generate] %s %s %s" % (str(counter), str(otp), str(sotp) ) )
if inc_counter:
self.counter = counter + 1
return sotp
[docs] def checkOtp(self, anOtpVal, window, symetric=False):
res = -1
start = self.counter
end = self.counter + window
if symetric == True:
# changed window/2 to window for TOTP
start = self.counter - (window)
start = 0 if (start < 0) else start
end = self.counter + (window)
log.debug("[checkOTP] OTP range counter: %r - %r" % (start, end))
for c in range(start , end):
otpval = self.generate(c)
log.debug("[checkOtp] calculating counter %r: %r %r"
% (c, anOtpVal, otpval))
#log.error("otp[%d]: %s : %s",c,otpval,anOtpVal)
if (unicode(otpval) == unicode(anOtpVal)):
# log.debug("Match Pin: %s : %d : %s",otpval,c,anOtpVal)
res = c
break
#return -1 or the counter
return res
#eof##########################################################################