def checkOtp(self, passw , counter, window, options=None): ''' checkOtp - standard callback of linotp to verify the token :param passw: the passw / otp, which has to be checked :type passw: string :param counter: the start counter :type counter: int :param window: the window, in which the token is valid :type window: int :param options: options contains the transaction id, eg. if check_t checks one transaction this will support assynchreonous otp checks (when check_t is used) :type options: dict :return: verification counter or -1 :rtype: int (-1) ''' log.debug('[checkOtp] %r: %r: %r' % (passw, counter, window)) ret = -1 challenges = [] serial = self.getSerial() if options is None: options = {} maxRequests = int(getFromConfig("Ocra2MaxChallengeRequests", '3')) if 'transactionid' in options: transid = options.get('transactionid', None) challs = get_challenges(serial=serial, transid=transid) for chall in challs: (rec_tan, rec_valid) = chall.getTanStatus() if rec_tan == False: challenges.append(chall) elif rec_valid == False: ## add all touched but failed challenges if chall.getTanCount() <= maxRequests: challenges.append(chall) if 'challenge' in options: ## direct challenge - there might be addtionalget info like ## session data in the options challenges.append(options) if len(challenges) == 0: challs = get_challenges(serial=serial) for chall in challs: (rec_tan, rec_valid) = chall.getTanStatus() if rec_tan == False: ## add all untouched challenges challenges.append(chall) elif rec_valid == False: ## add all touched but failed challenges if chall.getTanCount() <= maxRequests: challenges.append(chall) if len(challenges) == 0: err = 'No open transaction found for token %s' % serial log.error(err) ##TODO should log and fail!! raise Exception(err) ## prepare the challenge check - do the ocra setup secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) ## set the ocra token pin ocraPin = '' if ocraSuite.P is not None: ocraPinObj = self.token.getUserPin() ocraPin = ocraPinObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: defTimeWindow = int(getFromConfig("ocra.timeWindow", 180)) window = int(self.getFromTokenInfo('timeWindow', defTimeWindow)) / ocraSuite.T defTimeShift = int(getFromConfig("ocra.timeShift", 0)) timeShift = int(self.getFromTokenInfo("timeShift", defTimeShift)) default_retry_window = int(getFromConfig("ocra2.max_check_challenge_retry", 0)) retry_window = int(self.getFromTokenInfo("max_check_challenge_retry", default_retry_window)) ## now check the otp for each challenge for ch in challenges: challenge = {} ## preserve transaction context, so we could use this in the status callback self.transId = ch.get('transid', None) challenge['transid'] = self.transId challenge['session'] = ch.get('session', None) ## we saved the 'real' challenge in the data data = ch.get('data', None) if data is not None: challenge['challenge'] = data.get('challenge') elif 'challenge' in ch: ## handle explicit challenge requests challenge['challenge'] = ch.get('challenge') if challenge.get('challenge') is None: raise Exception('could not checkOtp due to missing challenge' ' in request: %r' % ch) ret = ocraSuite.checkOtp(passw, counter, window, challenge, pin=ocraPin , options=options, timeshift=timeShift) log.debug('[checkOtp]: %r' % (ret)) ## due to the assynchronous challenge verification of the checkOtp ## it might happen, that the found counter is lower than the given ## one. Thus we fix this here to deny assynchronous verification # we do not support retry checks anymore: # which means, that ret might be smaller than the actual counter if ocraSuite.T is None: if ret + retry_window < counter: ret = -1 if ret != -1: break if -1 == ret: ## autosync: test if two consecutive challenges + it's counter match ret = self.autosync(ocraSuite, passw, challenge) return ret
def resync(self, otp1, otp2, options=None): ''' - for the resync to work, we take the last two transactions and their challenges - for each challenge, we search forward the sync window length ''' log.debug('[resync] %r : %r' % (otp1, otp2)) ret = False challenges = [] ## the challenges are orderd, the first one is the newest challenges = get_challenges(self.getSerial()) ## check if there are enough challenges around if len(challenges) < 2: return False challenge1 = {} challenge2 = {} if options is None: ## the newer one ch1 = challenges[0] challenge1['challenge'] = ch1.get('data').get('challenge') challenge1['transid'] = ch1.get('transid') challenge1['session'] = ch1.get('session') challenge1['id'] = ch1.get('id') ## the elder one ch2 = challenges[0] challenge2['challenge'] = ch2.get('data').get('challenge') challenge2['transid'] = ch2.get('transid') challenge2['session'] = ch2.get('session') challenge2['id'] = ch2.get('id') else: if options.has_key('challenge1'): challenge1['challenge'] = options.get('challenge1') if options.has_key('challenge2'): challenge2['challenge'] = options.get('challenge2') if len(challenge1) == 0 or len(challenge2) == 0: error = "No challeges found!" log.error('[Ocra2TokenClass:resync] %s' % (error)) raise Exception('[Ocra2TokenClass:resync] %s' % (error)) secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) syncWindow = self.token.getSyncWindow() if ocraSuite.T is not None: syncWindow = syncWindow / 10 counter = self.token.getOtpCounter() ## set the ocra token pin ocraPin = '' if ocraSuite.P is not None: ocraPinObj = self.token.getUserPin() ocraPin = ocraPinObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: timeShift = int(self.getFromTokenInfo("timeShift", 0)) try: count_1 = ocraSuite.checkOtp(otp1, counter, syncWindow, challenge1, pin=ocraPin, timeshift=timeShift) if count_1 == -1: log.info('[resync] lookup for first otp value failed!') ret = False else: count_2 = ocraSuite.checkOtp(otp2, counter, syncWindow, challenge2, pin=ocraPin, timeshift=timeShift) if count_2 == -1: log.info('[resync] lookup for second otp value failed!') ret = False else: if ocraSuite.C is not None: if count_1 + 1 == count_2: self.setOtpCount(count_2) ret = True if ocraSuite.T is not None: if count_1 - count_2 <= ocraSuite.T * 2: ## callculate the timeshift date = datetime.datetime.fromtimestamp(count_2) log.info('[resync] syncing token to new timestamp: %r' % (date)) now = datetime.datetime.now() stime = now.strftime("%s") timeShift = count_2 - int(stime) self.addToTokenInfo('timeShift', timeShift) ret = True except Exception as ex: log.error('[Ocra2TokenClass:resync] unknown error: %r' % (ex)) raise Exception('[Ocra2TokenClass:resync] unknown error: %s' % (ex)) log.debug('[resync]: %r ' % (ret)) return ret