Source code for linotp.lib.mOTP

# -*- 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
#
'''lib to handle mobileOTP - Implementation inspired by:
      https://github.com/neush/otpn900/blob/master/src/test_motp.c
'''

import logging
import time

from hashlib import md5

from linotp.lib.crypt import zerome



log = logging.getLogger(__name__)



[docs]class mTimeOtp(object): ''' implements the motp timebased check_otp - s. https://github.com/neush/otpn900/blob/master/src/test_motp.c ''' def __init__(self, secObj=None, secPin=None, oldtime=0, digits=6, key=None, pin=None): ''' constructor for the mOtp class :param secObj: secretObject, which covers the encrypted secret :param secPin: secretObject, which covers the encrypted pin :param oldtime: the previously detected otp counter/time :param digits: length of otp chars to be tested :param key: direct key provider (for selfTest) :param pin: direct pin provider (for selfTest) :return: nothing ''' self.secretObject = secObj self.key = key self.secPin = secPin self.pin = pin self.oldtime = oldtime ## last time access self.digits = digits
[docs] def checkOtp(self, anOtpVal, window=10, options=None): ''' check a provided otp value :param anOtpVal: the to be tested otp value :param window: the +/- window around the test time :param options: generic container for additional values \ here only used for seltest: setting the initTime :return: -1 for fail else the identified counter/time ''' log.debug("[checkOtp] begin. checking the otpvalue %s window:%r, \ options:%r" % (anOtpVal, window, options)) res = -1 window = window * 2 initTime = 0 if options is not None and type(options) == dict: initTime = int(options.get('initTime', 0)) if (initTime == 0): otime = int(time.time() / 10) else: otime = int(initTime) if self.secretObject is None: key = self.key pin = self.pin else: key = self.secretObject.getKey() pin = self.secPin.getKey() for i in range(otime - window, otime + window): otp = unicode(self.calcOtp(i, key, pin)) if unicode(anOtpVal) == otp: res = i log.debug("[checkOtp] otpvalue %r found at: %r" % (anOtpVal, res)) break if self.secretObject is not None: zerome(key) zerome(pin) del key del pin ## prevent access twice with last motp if res <= self.oldtime: log.warning("[checkOtp] otpvalue %s checked once before (%r<=%r)" % (anOtpVal, res, self.oldtime)) res = -1 if res == -1: msg = 'checking motp failed' else: msg = 'checking motp sucess' log.debug("[checkOtp] end. %s : returning result: %r, " % (msg, res)) return res
[docs] def calcOtp(self, counter, key=None, pin=None): ''' calculate an otp value from counter/time, key and pin :param counter: counter/time to be checked :param key: the secret key :param pin: the secret pin :return: the otp value :rtype: string ''' ## ref impl from https://github.com/szimszon/motpy/blob/master/motpy if pin is None: pin = self.pin if key is None: key = self.key vhash = "%d%s%s" % (counter, key, pin) motp = md5(vhash).hexdigest()[:self.digits] return motp
[docs]def motp_test(): ''' Testvector from https://github.com/neush/otpn900/blob/master/src/test_motp.c ''' key = "0123456789abcdef" epoch = [ 129612120, 129612130, 0, 4, 129612244, 129612253] pins = ["6666", "6666", "1", "1", "77777777", "77777777"] otps = ["6ed4e4", "502a59", "bd94a4", "fb596e", "7abf75", "4d4ac4"] i = 0 motp1 = mTimeOtp(key=key, pin=pins[0]) for e in epoch: pin = pins[i] otp = otps[i] sotp = motp1.calcOtp(e, key, pin) if sotp != otp: print "error" else: print "ok" i = i + 1 ### test our unit test values key = "1234567890123456" pin = "1234" timeOtp2 = mTimeOtp(key=key, pin=pin) ntime = timeOtp2.checkOtp("7215e7", 18, options={'iniTime':126753360}) #expecting true print "result: ", ntime, " should be 126753370" return
if __name__ == '__main__': motp_test()