def _getChallenge(self, ocrasuite, bkey, serial, ocrapin="", data=None, count=0, ttime=None): otp1 = None p = {"serial": serial, "data": "0105037311 Konto 50150850 BLZ 1752,03 Eur"} if data != None: p[data] = data response = self.app.get(genUrl(controller="ocra", action="request"), params=p) log.info("response %s\n", response) assert '"value": true' in response """ -2b- from the response get the challenge """ jresp = json.loads(response.body) challenge1 = str(jresp.get("detail").get("challenge")) transid1 = str(jresp.get("detail").get("transactionid")) now = datetime.now() if ttime != None: now = ttime stime = now.strftime("%s") itime = int(stime) param = {} param["C"] = count param["Q"] = challenge1 param["P"] = ocrapin param["S"] = "" param["T"] = itime ocra = OcraSuite(ocrasuite) data = ocra.combineData(**param) otp1 = ocra.compute(data, bkey) return (otp1, transid1)
def _setup_(self): if self.ocra != None and self.bkey != None: return key_len = 20 if self.ocrasuite.find('-SHA256'): key_len = 32 elif self.ocrasuite.find('-SHA512'): key_len = 64 self.bkey = kdf2(self.sharedsecret, self.nonce, self.activationkey, len=key_len) self.ocra = OcraSuite(self.ocrasuite) self.counter = 0 return
def signData(self, data): ''' sign the received data with the secret key :param data: arbitrary string object :type param: string :return: hexlified signature of the data ''' log.debug('[signData] %r:' % (data)) secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) signature = ocraSuite.signData(data) log.debug('[signData]: %r:' % (signature)) return signature
def _getChallenge(self, ocrasuite, bkey, serial, ocrapin='', data=None, count=0, ttime=None): otp1 = None p = { "serial": serial, "data": "0105037311 Konto 50150850 BLZ 1752,03 Eur" } if data != None: p[data] = data response = self.app.get(genUrl(controller='ocra', action='request'), params=p) log.info("response %s\n", response) assert '"value": true' in response ''' -2b- from the response get the challenge ''' jresp = json.loads(response.body) challenge1 = str(jresp.get('detail').get('challenge')) transid1 = str(jresp.get('detail').get('transactionid')) now = datetime.now() if ttime != None: now = ttime stime = now.strftime("%s") itime = int(stime) param = {} param['C'] = count param['Q'] = challenge1 param['P'] = ocrapin param['S'] = '' param['T'] = itime ocra = OcraSuite(ocrasuite) data = ocra.combineData(**param) otp1 = ocra.compute(data, bkey) return (otp1, transid1)
def verify_challenge_is_valid(self, challenge, session): ''' verify, if a challenge is valid according to the ocrasuite definition of the token ''' ret = True counter = self.getOtpCount() secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) ## set the pin onyl in the compliant hashed mode pin = '' if ocraSuite.P is not None: pinObj = self.token.getUserPin() pin = pinObj.getKey() try: param = {} param['C'] = counter param['Q'] = challenge param['P'] = pin param['S'] = session if ocraSuite.T is not None: now = datetime.datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime ''' verify that the data is compliant with the OcraSuitesuite and the client is able to calc the otp ''' c_data = ocraSuite.combineData(**param) ocraSuite.compute(c_data) except Exception as ex: log.error("[Ocra2TokenClass] challenge verification failed: " "%s,%r: " % (challenge, ex)) log.error("[Ocra2TokenClass] %r" % (traceback.format_exc())) ret = False return ret
def _getChallenge(self, ocrasuite, bkey, serial, ocrapin='', data=None, count=0, ttime=None): otp1 = None p = {"serial" : serial, "data" : "0105037311 Konto 50150850 BLZ 1752,03 Eur" } if data != None: p[data] = data response = self.app.get(genUrl(controller='ocra', action='request'), params=p) log.info("response %s\n", response) assert '"value": true' in response ''' -2b- from the response get the challenge ''' jresp = json.loads(response.body) challenge1 = str(jresp.get('detail').get('challenge')) transid1 = str(jresp.get('detail').get('transactionid')) now = datetime.now() if ttime != None: now = ttime stime = now.strftime("%s") itime = int(stime) param = {} param['C'] = count param['Q'] = challenge1 param['P'] = ocrapin param['S'] = '' param['T'] = itime ocra = OcraSuite(ocrasuite) data = ocra.combineData(**param) otp1 = ocra.compute(data, bkey) return (otp1, transid1)
def update(self, params, reset_failcount=True): ''' update: add further definition for token from param in case of init ''' log.debug('[update] %r: %r: ' % (params, reset_failcount)) if params.has_key('ocrasuite'): self.ocraSuite = params.get('ocrasuite') else: activationcode = params.get('activationcode', None) sharedSecret = params.get('sharedsecret', None) if activationcode is None and sharedSecret is None: self.ocraSuite = self.getOcraSuiteSuite() else: self.ocraSuite = self.getQROcraSuiteSuite() if params.get('activationcode', None): ## due to changes in the tokenclass parameter handling ## we have to add for compatibility a genkey parameter if params.has_key('otpkey') == False and params.has_key('genkey') == False: log.warning('[Ocra2TokenClass:update] missing parameter genkey\ to complete the rollout 2!') params['genkey'] = 1 TokenClass.update(self, params, reset_failcount=reset_failcount) self.addToTokenInfo('ocrasuite', self.ocraSuite) ocraSuite = OcraSuite(self.ocraSuite) otplen = ocraSuite.truncation self.setOtpLen(otplen) ocraPin = params.get('ocrapin', None) if ocraPin is not None: self.token.setUserPin(ocraPin) if params.has_key('otpkey'): self.setOtpKey(params.get('otpkey')) self._rollout_1(params) self._rollout_2(params) log.debug('[update]:') return
def challenge(self, data, session='', typ='raw', challenge=None): ''' the challenge method is for creating an transaction / challenge object remark: the transaction has a maximum lifetime and a reference to the OcraSuite token (serial) :param data: data, which is the base for the challenge or None :type data: string or None :param session: session support for ocratokens :type session: string :type typ: define, which kind of challenge base should be used could be raw - take the data input as is (extract chars accordind challenge definition Q) or random - will generate a random input or hased - will take the hash of the input data :return: challenge response containing the transcation id and the challenge for the ocrasuite :rtype : tuple of (transId(string), challenge(string)) ''' log.debug('[challenge] %r: %r: %r' % (data, session, challenge)) secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) if data is None or len(data) == 0: typ = 'random' if challenge is None: if typ == 'raw': challenge = ocraSuite.data2rawChallenge(data) elif typ == 'random': challenge = ocraSuite.data2randomChallenge(data) elif typ == 'hash': challenge = ocraSuite.data2hashChallenge(data) log.debug('[Ocra2TokenClass] challenge: %r ' % (challenge)) counter = self.getOtpCount() ## set the pin onyl in the compliant hashed mode pin = '' if ocraSuite.P is not None: pinObj = self.token.getUserPin() pin = pinObj.getKey() try: param = {} param['C'] = counter param['Q'] = challenge param['P'] = pin param['S'] = session if ocraSuite.T is not None: now = datetime.datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime ''' verify that the data is compliant with the OcraSuitesuite and the client is able to calc the otp ''' c_data = ocraSuite.combineData(**param) ocraSuite.compute(c_data) except Exception as ex: log.error("[Ocra2TokenClass] %r" % (traceback.format_exc())) raise Exception('[Ocra2TokenClass] Failed to create ocrasuite ' 'challenge: %r' % (ex)) ## create a non exisiting challenge try: (res, opt) = create_challenge(self, options={'messgae': data}) transid = opt.get('transactionid') challenge = opt.get('challenge') except Exception as ex: ## this might happen if we have a db problem or ## the uniqnes constrain does not fit log.error("[Ocra2TokenClass] %r" % (traceback.format_exc())) raise Exception('[Ocra2TokenClass] Failed to create ' 'challenge object: %s' % (ex)) realm = None realms = self.token.getRealms() if len(realms) > 0: realm = realms[0] url = '' if realm is not None: url = get_qrtan_url(realm.name) log.debug('[challenge]: %r: %r: %r' % (transid, challenge, url)) return (transid, challenge, True, url)
def ptest_OCRA_token_failcounterInc(self, tid=1): ''' test_OCRA_token_failcounterInc: failcounter increment description: for all ocrasuites: create and enroll token verify the first otp get some challenges 4 times: verify a wrong otp verify a wrong transaction check status and if fail counter has incremented ''' tcount = 0 for test in self.tests: ocrasuite = test['ocrasuite'] key = test['keyh'] bkey = test['key'] ocrapin = 'myocrapin' tid = tid serial = "QR_One_%r_%r_%r_%r" % (tid, tcount, int(time.time()), random.randint(0, 100)) log.info("## serial: %s" % serial) count = 0 tcount = tcount + 1 ocra = OcraSuite(ocrasuite) pinlen = ocra.truncation ''' -1- create an ocra token ''' parameters = { "serial" : serial, "user" : "root", "pin" : "pin", "description" : "first QRToken", 'type' : 'ocra', 'ocrapin' : ocrapin, 'otpkey' : key, 'ocrasuite' : ocrasuite } response = self.app.get(genUrl(controller='admin', action='init'), params=parameters) assert '"value": true' in response ## verify that the token is usable ''' -2- fetch the challenge ''' p = {"serial" : serial, "data" : "0105037311 Konto 50150850 BLZ 1752,03 Eur" } response = self.app.get(genUrl(controller='ocra', action='request'), params=p) log.info("response %s\n", response) if '"value": true' not in response: assert '"value": true' in response ''' -3.a- from the response get the challenge ''' jresp = json.loads(response.body) challenge = str(jresp.get('detail').get('challenge')) transid = str(jresp.get('detail').get('transactionid')) param = {} param['C'] = count param['Q'] = challenge param['P'] = ocrapin param['S'] = '' if ocra.T != None: ''' Default value for G is 1M, i.e., time-step size is one minute and the T represents the number of minutes since epoch time [UT]. ''' now = datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime ocra = OcraSuite(ocrasuite) data = ocra.combineData(**param) otp = ocra.compute(data, bkey) ppin = 'pin' + otp ''' -3.b- verify the correct otp value ''' parameters = {"transactionid" : transid, "pass" : ppin, } response = self.app.get(genUrl(controller='ocra', action='check_t'), params=parameters) log.info("response %s\n", response) if '"result": true' not in response: assert '"result": true' in response # verify that the failcounter increments (max is 10) fcount = 0 for count in range(1, 3): ## create more than one challenge chals = random.randint(2, 5) for cc in range(1, chals): ''' -2- fetch the challenge ''' p = {"serial" : serial, "data" : "0105037311 Konto 50150850 BLZ 1752,03 Eur" } response = self.app.get(genUrl(controller='ocra', action='request'), params=p) log.info("response %s\n", response) if '"value": true' not in response: assert '"value": true' in response ''' -3.a- from the response get the challenge ''' jresp = json.loads(response.body) challenge = str(jresp.get('detail').get('challenge')) transid = str(jresp.get('detail').get('transactionid')) ppin = 'pin' + 'a' * pinlen ''' -4- verify the wrong otp value ''' parameters = {"transactionid" : transid, "pass" : ppin, } response = self.app.get(genUrl(controller='ocra', action='check_t'), params=parameters) log.info("response %s\n", response) if '"result": false' not in response: assert '"result": false' in response fcount += 1 ppin = 'pin' + '4' * pinlen ''' -5- verify the wrong otp value ''' parameters = {"transactionid" : transid, "pass" : ppin, } response = self.app.get(genUrl(controller='ocra', action='check_t'), params=parameters) log.info("response %s\n", response) if not '"result": false' in response: assert '"result": false' in response fcount += 1 ''' -6- check if the failcounter has incremented ''' parameters = {"transactionid" : transid, } response = self.app.get(genUrl(controller='ocra', action='checkstatus'), params=parameters) log.info("response %s\n", response) assert '"status": true' in response assstring = '"failcount": %d,' % (fcount) log.info("assert %s\n", assstring) if assstring not in response: log.error(response) assert assstring in response sleep = random.uniform(0.0, 0.3) time.sleep(sleep) ''' -remove the ocra token ''' parameters = {"serial" : serial, } response = self.app.get(genUrl(controller='admin', action='remove'), params=parameters) log.info("response %s\n", response) assert '"value": 1' in response for _iii in range(0, 3): parameters = {"serial" : serial, } response = self.app.get(genUrl(controller='admin', action='remove'), params=parameters) return response
class OcraOtp(object): def __init__(self, ocrapin=None): self.ocra = None self.bkey = None self.ocrapin = ocrapin self.activationkey = None self.sharedsecret = None self.ocrasuite = None self.serial = None self.counter = 0 def init_1(self, response): ''' take the response of the first init to setup the OcraOtp''' jresp = json.loads(response.body) app_import = str(jresp.get('detail').get('app_import')) self.sharedsecret = str(jresp.get('detail').get('sharedsecret')) self.serial = str(jresp.get('detail').get('serial')) ''' now parse the appurl for the ocrasuite ''' uri = urlparse(app_import.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) ocrasuite = qdict.get('os', None) if ocrasuite != None and len(ocrasuite) > 0: ocrasuite = ocrasuite[0] self.ocrasuite = ocrasuite return (self.ocrasuite, self.sharedsecret, self.serial) def init_2(self, response, activationKey): self.activationkey = activationKey jresp = json.loads(response.body) self.nonce = str(jresp.get('detail').get('nonce')) self.transid = str(jresp.get('detail').get('transactionid')) app_import = str(jresp.get('detail').get('app_import')) ''' now parse the appurl for the ocrasuite ''' uri = urlparse(app_import.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) nonce = qdict.get('no', None) if nonce != None and len(nonce) > 0: nonce = nonce[0] challenge = qdict.get('ch', None) if challenge != None and len(challenge) > 0: challenge = challenge[0] self.challenge = challenge self.ocra = None self.bkey = None return (self.challenge, self.transid) def _setup_(self): if self.ocra != None and self.bkey != None: return key_len = 20 if self.ocrasuite.find('-SHA256'): key_len = 32 elif self.ocrasuite.find('-SHA512'): key_len = 64 self.bkey = kdf2(self.sharedsecret, self.nonce, self.activationkey, len=key_len) self.ocra = OcraSuite(self.ocrasuite) self.counter = 0 return def callcOtp(self, challenge=None, ocrapin=None, counter= -1): if self.ocra == None: self._setup_() if ocrapin == None: ocrapin = self.ocrapin if challenge == None: challenge = self.challenge if counter == -1: counter = self.counter param = {} param['C'] = counter param['Q'] = challenge param['P'] = ocrapin param['S'] = '' if self.ocra.T != None: ''' Default value for G is 1M, i.e., time-step size is one minute and the T represents the number of minutes since epoch time [UT]. ''' now = datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime data = self.ocra.combineData(**param) otp = self.ocra.compute(data, self.bkey) if counter == -1: self.counter += 1 return otp
def ptest_OCRA_token_failcounterInc(self, tid=1): """ test_OCRA_token_failcounterInc: failcounter increment description: for all ocrasuites: create and enroll token verify the first otp get some challenges 4 times: verify a wrong otp verify a wrong transaction check status and if fail counter has incremented """ tcount = 0 for test in self.tests: ocrasuite = test["ocrasuite"] key = test["keyh"] bkey = test["key"] ocrapin = "myocrapin" tid = tid serial = "QR_One_%r_%r_%r_%r" % (tid, tcount, int(time.time()), random.randint(0, 100)) log.info("## serial: %s" % serial) count = 0 tcount = tcount + 1 ocra = OcraSuite(ocrasuite) pinlen = ocra.truncation """ -1- create an ocra token """ parameters = { "serial": serial, "user": "******", "pin": "pin", "description": "first QRToken", "type": "ocra", "ocrapin": ocrapin, "otpkey": key, "ocrasuite": ocrasuite, } response = self.app.get(genUrl(controller="admin", action="init"), params=parameters) assert '"value": true' in response ## verify that the token is usable """ -2- fetch the challenge """ p = {"serial": serial, "data": "0105037311 Konto 50150850 BLZ 1752,03 Eur"} response = self.app.get(genUrl(controller="ocra", action="request"), params=p) log.info("response %s\n", response) if '"value": true' not in response: assert '"value": true' in response """ -3.a- from the response get the challenge """ jresp = json.loads(response.body) challenge = str(jresp.get("detail").get("challenge")) transid = str(jresp.get("detail").get("transactionid")) param = {} param["C"] = count param["Q"] = challenge param["P"] = ocrapin param["S"] = "" if ocra.T != None: """ Default value for G is 1M, i.e., time-step size is one minute and the T represents the number of minutes since epoch time [UT]. """ now = datetime.now() stime = now.strftime("%s") itime = int(stime) param["T"] = itime ocra = OcraSuite(ocrasuite) data = ocra.combineData(**param) otp = ocra.compute(data, bkey) ppin = "pin" + otp """ -3.b- verify the correct otp value """ parameters = {"transactionid": transid, "pass": ppin} response = self.app.get(genUrl(controller="ocra", action="check_t"), params=parameters) log.info("response %s\n", response) if '"result": true' not in response: assert '"result": true' in response # verify that the failcounter increments (max is 10) fcount = 0 for count in range(1, 3): ## create more than one challenge chals = random.randint(2, 5) for cc in range(1, chals): """ -2- fetch the challenge """ p = {"serial": serial, "data": "0105037311 Konto 50150850 BLZ 1752,03 Eur"} response = self.app.get(genUrl(controller="ocra", action="request"), params=p) log.info("response %s\n", response) if '"value": true' not in response: assert '"value": true' in response """ -3.a- from the response get the challenge """ jresp = json.loads(response.body) challenge = str(jresp.get("detail").get("challenge")) transid = str(jresp.get("detail").get("transactionid")) ppin = "pin" + "a" * pinlen """ -4- verify the wrong otp value """ parameters = {"transactionid": transid, "pass": ppin} response = self.app.get(genUrl(controller="ocra", action="check_t"), params=parameters) log.info("response %s\n", response) if '"result": false' not in response: assert '"result": false' in response fcount += 1 ppin = "pin" + "4" * pinlen """ -5- verify the wrong otp value """ parameters = {"transactionid": transid, "pass": ppin} response = self.app.get(genUrl(controller="ocra", action="check_t"), params=parameters) log.info("response %s\n", response) if not '"result": false' in response: assert '"result": false' in response fcount += 1 """ -6- check if the failcounter has incremented """ parameters = {"transactionid": transid} response = self.app.get(genUrl(controller="ocra", action="checkstatus"), params=parameters) log.info("response %s\n", response) assert '"status": true' in response assstring = '"failcount": %d,' % (fcount) log.info("assert %s\n", assstring) if assstring not in response: log.error(response) assert assstring in response sleep = random.uniform(0.0, 0.3) time.sleep(sleep) """ -remove the ocra token """ parameters = {"serial": serial} response = self.app.get(genUrl(controller="admin", action="remove"), params=parameters) log.info("response %s\n", response) assert '"value": 1' in response for _iii in range(0, 3): parameters = {"serial": serial} response = self.app.get(genUrl(controller="admin", action="remove"), params=parameters) return response
def calculateOtp(self): ''' ''' from linotp.lib.crypt import kdf2 from linotp.lib.ocra import OcraSuite from datetime import datetime from urlparse import urlparse from urlparse import parse_qs res = {} #description = 'ocra/calculateOtp: calculate the first otp from the given init2 response ' try: params = getLowerParams(request.params) log.debug("[calculateOtp]: %r" % params) checkPolicyPre('ocra', "calcOTP") sharedsecret = params.get('sharedsecret') activationcode = params.get('activationcode') nonce = params.get('nonce') ocrasuite = params.get('ocrasuite') challenge = params.get('challenge') counter = params.get('counter') ocrapin = params.get('ocrapin') nonce3 = params.get('no') ocrasuite3 = params.get('os') #serial3 = params.get('se') challenge = params.get('challenge') counter = params.get('counter') ocrapin = params.get('ocrapin') init1 = params.get('init1') init2 = params.get('init2') ## parse init1 ''' if init1 is not None: ## now parse the appurl for the ocrasuite ''' uri = urlparse(init1.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) ocrasuite2 = qdict.get('os', None) if ocrasuite2 is not None and len(ocrasuite2) > 0: ocrasuite2 = ocrasuite2[0] if ocrasuite is None: ocrasuite = ocrasuite2 sharedsecret2 = qdict.get('sh', None) if sharedsecret2 is not None and len(sharedsecret2) > 0: sharedsecret2 = sharedsecret2[0] if sharedsecret is None: sharedsecret = sharedsecret2 ## parse init1 if init2 is not None: ## now parse the appurl for the ocrasuite uri = urlparse(init2.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) challenge2 = qdict.get('ch', None) if challenge2 is not None and len(challenge2) > 0: challenge2 = challenge2[0] if challenge is None: challenge = challenge2 nonce2 = qdict.get('no', None) if nonce2 is not None and len(nonce2) > 0: nonce2 = nonce2[0] if nonce is None: nonce = nonce2 if ocrapin is None: ocrapin = '' if counter is None: counter = 0 if nonce3 is not None: nonce = unicode(nonce3) if ocrasuite3 is not None: ocrasuite = unicode(ocrasuite3) ## now we have all in place for the key derivation to create the new key ## sharedsecret, activationcode and nonce key_len = 20 if ocrasuite.find('-SHA256'): key_len = 32 elif ocrasuite.find('-SHA512'): key_len = 64 if sharedsecret is not None: sharedsecret = unicode(sharedsecret) if nonce is not None: nonce = unicode(nonce) if activationcode is not None: activationcode = unicode(activationcode) newkey = kdf2(sharedsecret, nonce, activationcode, len=key_len) ## hnewkey = binascii.hexlify(newkey) ocra = OcraSuite(ocrasuite) param = {} param['C'] = int(counter) param['Q'] = unicode(challenge) param['P'] = unicode(ocrapin) param['S'] = '' if ocra.T is not None: ## Default value for G is 1M, i.e., time-step size is one minute and the ## T represents the number of minutes since epoch time [UT]. now = datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime data = ocra.combineData(**param) otp = ocra.compute(data, newkey) res = {'otp':otp} Session.commit() return sendResult(response, res, 1) except PolicyException as pe: log.exception("[ocra/calculateOtp] policy failed: %r" % pe) Session.rollback() return sendError(response, pe) except Exception as e: log.exception("[ocra/calculateOtp] failed: %r" % e) Session.rollback() return sendError(response, unicode(e), 0) finally: Session.close() log.debug('[ocra/calculateOtp] done')
def calculateOtp(self): ''' ''' from linotp.lib.crypt import kdf2 from linotp.lib.ocra import OcraSuite from datetime import datetime from urlparse import urlparse from urlparse import parse_qs res = {} #description = 'ocra/calculateOtp: calculate the first otp from the given init2 response ' try: params = getLowerParams(request.params) log.debug("[calculateOtp]: %r" % params) checkPolicyPre('ocra', "calcOTP") sharedsecret = params.get('sharedsecret') activationcode = params.get('activationcode') nonce = params.get('nonce') ocrasuite = params.get('ocrasuite') challenge = params.get('challenge') counter = params.get('counter') ocrapin = params.get('ocrapin') nonce3 = params.get('no') ocrasuite3 = params.get('os') #serial3 = params.get('se') challenge = params.get('challenge') counter = params.get('counter') ocrapin = params.get('ocrapin') init1 = params.get('init1') init2 = params.get('init2') ## parse init1 ''' if init1 is not None: ## now parse the appurl for the ocrasuite ''' uri = urlparse(init1.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) ocrasuite2 = qdict.get('os', None) if ocrasuite2 is not None and len(ocrasuite2) > 0: ocrasuite2 = ocrasuite2[0] if ocrasuite is None: ocrasuite = ocrasuite2 sharedsecret2 = qdict.get('sh', None) if sharedsecret2 is not None and len(sharedsecret2) > 0: sharedsecret2 = sharedsecret2[0] if sharedsecret is None: sharedsecret = sharedsecret2 ## parse init1 if init2 is not None: ## now parse the appurl for the ocrasuite uri = urlparse(init2.replace('lseqr://', 'http://')) qs = uri.query qdict = parse_qs(qs) challenge2 = qdict.get('ch', None) if challenge2 is not None and len(challenge2) > 0: challenge2 = challenge2[0] if challenge is None: challenge = challenge2 nonce2 = qdict.get('no', None) if nonce2 is not None and len(nonce2) > 0: nonce2 = nonce2[0] if nonce is None: nonce = nonce2 if ocrapin is None: ocrapin = '' if counter is None: counter = 0 if nonce3 is not None: nonce = unicode(nonce3) if ocrasuite3 is not None: ocrasuite = unicode(ocrasuite3) ## now we have all in place for the key derivation to create the new key ## sharedsecret, activationcode and nonce key_len = 20 if ocrasuite.find('-SHA256'): key_len = 32 elif ocrasuite.find('-SHA512'): key_len = 64 if sharedsecret is not None: sharedsecret = unicode(sharedsecret) if nonce is not None: nonce = unicode(nonce) if activationcode is not None: activationcode = unicode(activationcode) newkey = kdf2(sharedsecret, nonce, activationcode, len=key_len) ## hnewkey = binascii.hexlify(newkey) ocra = OcraSuite(ocrasuite) param = {} param['C'] = int(counter) param['Q'] = unicode(challenge) param['P'] = unicode(ocrapin) param['S'] = '' if ocra.T is not None: ## Default value for G is 1M, i.e., time-step size is one minute and the ## T represents the number of minutes since epoch time [UT]. now = datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime data = ocra.combineData(**param) otp = ocra.compute(data, newkey) res = {'otp': otp} Session.commit() return sendResult(response, res, 1) except PolicyException as pe: log.exception("[ocra/calculateOtp] policy failed: %r" % pe) Session.rollback() return sendError(response, pe) except Exception as e: log.exception("[ocra/calculateOtp] failed: %r" % e) Session.rollback() return sendError(response, unicode(e), 0) finally: Session.close() log.debug('[ocra/calculateOtp] done')
class OcraOtp(object): def __init__(self, ocrapin=None): self.ocra = None self.bkey = None self.ocrapin = ocrapin self.activationkey = None self.sharedsecret = None self.ocrasuite = None self.serial = None self.counter = 0 def init_1(self, response): """ take the response of the first init to setup the OcraOtp""" jresp = json.loads(response.body) app_import = str(jresp.get("detail").get("app_import")) self.sharedsecret = str(jresp.get("detail").get("sharedsecret")) self.serial = str(jresp.get("detail").get("serial")) """ now parse the appurl for the ocrasuite """ uri = urlparse(app_import.replace("lseqr://", "http://")) qs = uri.query qdict = parse_qs(qs) ocrasuite = qdict.get("os", None) if ocrasuite != None and len(ocrasuite) > 0: ocrasuite = ocrasuite[0] self.ocrasuite = ocrasuite return (self.ocrasuite, self.sharedsecret, self.serial) def init_2(self, response, activationKey): self.activationkey = activationKey jresp = json.loads(response.body) self.nonce = str(jresp.get("detail").get("nonce")) self.transid = str(jresp.get("detail").get("transactionid")) app_import = str(jresp.get("detail").get("app_import")) """ now parse the appurl for the ocrasuite """ uri = urlparse(app_import.replace("lseqr://", "http://")) qs = uri.query qdict = parse_qs(qs) nonce = qdict.get("no", None) if nonce != None and len(nonce) > 0: nonce = nonce[0] challenge = qdict.get("ch", None) if challenge != None and len(challenge) > 0: challenge = challenge[0] self.challenge = challenge self.ocra = None self.bkey = None return (self.challenge, self.transid) def _setup_(self): if self.ocra != None and self.bkey != None: return key_len = 20 if self.ocrasuite.find("-SHA256"): key_len = 32 elif self.ocrasuite.find("-SHA512"): key_len = 64 self.bkey = kdf2(self.sharedsecret, self.nonce, self.activationkey, len=key_len) self.ocra = OcraSuite(self.ocrasuite) self.counter = 0 return def callcOtp(self, challenge=None, ocrapin=None, counter=-1): if self.ocra == None: self._setup_() if ocrapin == None: ocrapin = self.ocrapin if challenge == None: challenge = self.challenge if counter == -1: counter = self.counter param = {} param["C"] = counter param["Q"] = challenge param["P"] = ocrapin param["S"] = "" if self.ocra.T != None: """ Default value for G is 1M, i.e., time-step size is one minute and the T represents the number of minutes since epoch time [UT]. """ now = datetime.now() stime = now.strftime("%s") itime = int(stime) param["T"] = itime data = self.ocra.combineData(**param) otp = self.ocra.compute(data, self.bkey) if counter == -1: self.counter += 1 return otp
def createChallenge(self, state, options=None): ''' standard API to create an ocra challenge ''' res = True ## which kind of challenge gen should be used typ = 'raw' input = None challenge = None session = None message = "" if options is not None: input = options.get('challenge', None) if input is None: input = options.get('message', None) if input is None: input = options.get('data', None) typ = options.get('challenge_type', 'raw') ## ocra token could contain a session attribute session = options.get('ocra_session', None) if input is None or len(input) == 0: typ = 'random' secretHOtp = self.token.getHOtpKey() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secretHOtp) if typ == 'raw': challenge = ocraSuite.data2rawChallenge(input) elif typ == 'random': challenge = ocraSuite.data2randomChallenge(input) elif typ == 'hash': challenge = ocraSuite.data2hashChallenge(input) log.debug('[Ocra2TokenClass] challenge: %r ' % (challenge)) store_data = { 'challenge' : "%s" % (challenge), 'serial' : self.token.getSerial(), 'input' : '', 'url' : '', } if input is not None: store_data['input'] = input if session is not None: store_data["session"] = session res = self.verify_challenge_is_valid(challenge, session) ## add Info: so depending on the Info, the rendering could be done ## as a callback into the token via ## token.getQRImageData(opt=details) realms = self.token.getRealms() if len(realms) > 0: store_data["url"] = get_qrtan_url(realms[0].name) ## we will return a dict of all attributes = self.prepare_message(store_data, state) attributes['challenge'] = challenge if attributes != None and "data" in attributes: message = attributes.get("data") del attributes['data'] return (res, message, store_data, attributes)
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
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