Exemple #1
0
    def checkPin(self, pin, options=None):
        '''
        checkPin - test is the pin is matching

        :param pin:      the pin
        :param options:  additional optional parameters, which could
                         be token specific
        :return: boolean

        '''
        res = False

        hsm = context['hsm']
        if self.token.isPinEncrypted():
            # for comparison we encrypt the pin and do the comparison
            iv, encrypted_token_pin = self.token.get_encrypted_pin()
            encrypted_pin = SecretObj.encrypt_pin(pin, iv=iv, hsm=hsm)
            if encrypted_token_pin == encrypted_pin:
                res = True
        else:
            # for hashed pins we re-do the hash and compare the hashes
            iv, hashed_token_pin = self.token.get_hashed_pin()
            iv, hashed_pin = SecretObj.hash_pin(pin or '', iv, hsm=hsm)
            if hashed_pin == hashed_token_pin:
                res = True

            # special case of empty pin, where pin has never been set
            # especially in case of lost token with the pw token
            if len(hashed_token_pin) == 0 and len(pin) == 0:
                res = True

        return res
Exemple #2
0
    def setPin(self, pin, param=None):
        '''
        set the PIN. The optional parameter "param" can hold the information,
        if the PIN is encrypted or hashed.

        :param pin: the pin value
        :param param: the additional request parameters, which could contain
                      the 'encryptpin' value, that triggers, that the token
                      secret are stored in an encrypted form
        :return: - nothing -
        '''
        if param is None:
            param = {}

        hsm = context['hsm']
        storeHashed = True
        enc = param.get("encryptpin", None)
        if enc is not None and "true" == enc.lower():
            storeHashed = False

        if storeHashed is True:
            iv, hashed_pin = SecretObj.hash_pin(pin, hsm=hsm)
            self.token.set_hashed_pin(hashed_pin, iv)
        else:
            enc_pin = SecretObj.encrypt_pin(pin, hsm=hsm)
            iv = enc_pin.split(':')[0]
            self.token.set_encrypted_pin(enc_pin, binascii.unhexlify(iv))
Exemple #3
0
    def checkPin(self, pin, options=None):
        '''
        checkPin - test is the pin is matching

        :param pin:      the pin
        :param options:  additional optional parameters, which could
                         be token specific
        :return: boolean

        '''
        res = False

        hsm = context['hsm']
        if self.token.isPinEncrypted():
            # for comparison we encrypt the pin and do the comparison
            iv, encrypted_token_pin = self.token.get_encrypted_pin()
            encrypted_pin = SecretObj.encrypt_pin(pin, iv=iv, hsm=hsm)
            if encrypted_token_pin == encrypted_pin:
                res = True
        else:
            # for hashed pins we re-do the hash and compare the hashes
            iv, hashed_token_pin = self.token.get_hashed_pin()
            iv, hashed_pin = SecretObj.hash_pin(pin or '', iv, hsm=hsm)
            if hashed_pin == hashed_token_pin:
                res = True

            # special case of empty pin, where pin has never been set
            # especially in case of lost token with the pw token
            if len(hashed_token_pin) == 0 and len(pin) == 0:
                res = True

        return res
Exemple #4
0
    def setPin(self, pin, param=None):
        """
        set the PIN. The optional parameter "param" can hold the information,
        if the PIN is encrypted or hashed.

        :param pin: the pin value
        :param param: the additional request parameters, which could contain
                      the 'encryptpin' value, that triggers, that the token
                      secret are stored in an encrypted form
        :return: - nothing -
        """
        if param is None:
            param = {}

        storeHashed = True
        enc = param.get("encryptpin", None)
        if enc is not None and "true" == enc.lower():
            storeHashed = False

        if storeHashed is True:
            iv, hashed_pin = SecretObj.hash_pin(pin)
            self.token.set_hashed_pin(hashed_pin, iv)
        else:
            enc_pin = SecretObj.encrypt_pin(pin)
            iv = enc_pin.split(":")[0]
            self.token.set_encrypted_pin(enc_pin.encode("utf-8"),
                                         binascii.unhexlify(iv))
Exemple #5
0
    def checkPin(self, pin, options=None):
        """
        checkPin - test is the pin is matching

        :param pin:      the pin
        :param options:  additional optional parameters, which could
                         be token specific
        :return: boolean

        """

        if self.token.isPinEncrypted():

            # for comparison we encrypt the pin and do the comparison
            # iv is binary, while encrypted_token_pin is hexlified
            iv, encrypted_token_pin = self.token.get_encrypted_pin()

            return SecretObj.check_encrypted_pin(pin, encrypted_token_pin, iv)

        # hashed pin comparison

        iv, hashed_token_pin = self.token.get_hashed_pin()

        # special case of empty pin, where pin has never been set
        # especially in case of lost token with the pw token
        if len(hashed_token_pin) == 0 and len(pin) == 0:
            return True

        return SecretObj.check_hashed_pin(pin or "", hashed_token_pin, iv)
Exemple #6
0
    def set_token_data(self, token_data):

        serial = token_data["Serial"]
        tokens = Session.query(model_token).\
            filter(model_token.LinOtpTokenSerialnumber == serial).all()
        token = tokens[0]

        if 'TokenPin' in token_data:

            enc_pin = token_data['TokenPin']

            token_pin = self.crypter.decrypt(enc_pin,
                                             just_mac=serial +
                                             token.LinOtpPinHash)
            # prove, we can write
            enc_pin = SecretObj.encrypt_pin(token_pin)
            iv = enc_pin.split(':')[0]
            token.set_encrypted_pin(enc_pin, binascii.unhexlify(iv))

        if 'TokenUserPin' in token_data:
            token_enc_user_pin = token_data['TokenUserPin']

            user_pin = self.crypter.decrypt(token_enc_user_pin,
                                            just_mac=serial +
                                            token.LinOtpTokenPinUser)

            # prove, we can write
            iv, enc_user_pin = SecretObj.encrypt(user_pin, hsm=self.hsm)
            token.setUserPin(enc_user_pin, iv)

        # we put the current crypted seed in the mac to check if
        # something changed in meantime
        encKey = token.LinOtpKeyEnc
        enc_seed = token_data['TokenSeed']
        token_seed = self.crypter.decrypt(enc_seed, just_mac=serial + encKey)

        # the encryption of the token seed is not part of the model anymore
        iv, enc_token_seed = SecretObj.encrypt(token_seed)

        token.set_encrypted_seed(enc_token_seed,
                                 iv,
                                 reset_failcount=False,
                                 reset_counter=False)
Exemple #7
0
    def setSoPin(self, soPin):
        """
        set the soPin of the token
            the soPin is encrypted and the encrypte value is stored in the
            Token model

        :param soPin: the special so pin
        """
        iv, enc_soPin = SecretObj.encrypt(soPin, hsm=context.get('hsm'))
        self.token.setSoPin(enc_soPin, iv)
Exemple #8
0
 def getPin(self):
     """
     :return: the value of the pin- if it is stored encrypted
     """
     pin = ''
     hsm = context['hsm']
     if self.token.isPinEncrypted():
         _iv, enc_pin = self.token.get_encrypted_pin()
         pin = SecretObj.decrypt_pin(enc_pin, hsm=hsm)
     return pin
Exemple #9
0
    def setSoPin(self, soPin):
        """
        set the soPin of the token
            the soPin is encrypted and the encrypte value is stored in the
            Token model

        :param soPin: the special so pin
        """
        iv, enc_soPin = SecretObj.encrypt(soPin, hsm=context.get("hsm"))
        self.token.setSoPin(enc_soPin, iv)
Exemple #10
0
 def getPin(self):
     """
     :return: the value of the pin- if it is stored encrypted
     """
     pin = ""
     hsm = context["hsm"]
     if self.token.isPinEncrypted():
         _iv, enc_pin = self.token.get_encrypted_pin()
         pin = SecretObj.decrypt_pin(enc_pin, hsm=hsm)
     return pin
Exemple #11
0
    def set_token_data(self, token_data):

        serial = token_data["Serial"]
        tokens = Session.query(model_token).\
            filter(model_token.LinOtpTokenSerialnumber == serial).all()
        token = tokens[0]

        if 'TokenPin' in token_data:

            enc_pin = token_data['TokenPin']

            token_pin = self.crypter.decrypt(
                                    enc_pin,
                                    just_mac=serial + token.LinOtpPinHash)
            # prove, we can write
            enc_pin = SecretObj.encrypt_pin(token_pin)
            iv = enc_pin.split(':')[0]
            token.set_encrypted_pin(enc_pin, binascii.unhexlify(iv))

        if 'TokenUserPin' in token_data:
            token_enc_user_pin = token_data['TokenUserPin']

            user_pin = self.crypter.decrypt(
                                token_enc_user_pin,
                                just_mac=serial + token.LinOtpTokenPinUser)

            # prove, we can write
            iv, enc_user_pin = SecretObj.encrypt(user_pin, hsm=self.hsm)
            token.setUserPin(enc_user_pin, iv)

        # we put the current crypted seed in the mac to check if
        # something changed in meantime
        encKey = token.LinOtpKeyEnc
        enc_seed = token_data['TokenSeed']
        token_seed = self.crypter.decrypt(enc_seed,
                                          just_mac=serial + encKey)

        # the encryption of the token seed is not part of the model anymore
        iv, enc_token_seed = SecretObj.encrypt(token_seed)

        token.set_encrypted_seed(enc_token_seed, iv,
                                 reset_failcount=False,
                                 reset_counter=False)
Exemple #12
0
    def _rollout_1(self, params):
        '''
        do the rollout 1 step

        1. https://linotpserver/admin/init?
            type=ocra&
            genkey=1&
            sharedsecret=1&
            user=BENUTZERNAME&
            session=SESSIONKEY

            =>> "serial" : SERIENNUMMER, "sharedsecret" : DATAOBJECT,
                                         "app_import" : IMPORTURL
            - genSharedSecret - vom HSM oder urandom ?
            - app_import : + linotp://
                           + ocrasuite ->> default aus dem config:
                                                           (DefaultOcraSuite)
                           + sharedsecret (Länge wie ???)
                           + seriennummer
            - seriennummer: uuid ??
            - token wird angelegt ist aber nicht aktiv!!! (counter == 0)

        '''

        sharedSecret = params.get('sharedsecret', None)
        if sharedSecret == '1':
            #  preserve the rollout state
            self.addToTokenInfo('rollout', '1')

            # preserve the current key as sharedSecret
            secObj = self._get_secret_object()
            key = secObj.getKey()
            encSharedSecret = SecretObj.encrypt_pin(key)
            self.addToTokenInfo('sharedSecret', encSharedSecret)

            info = {}
            uInfo = {}

            info['sharedsecret'] = key
            uInfo['sh'] = key

            info['ocrasuite'] = self.getOcraSuiteSuite()
            uInfo['os'] = self.getOcraSuiteSuite()

            info['serial'] = self.getSerial()
            uInfo['se'] = self.getSerial()

            info['app_import'] = 'lseqr://init?%s' % (
                urllib.parse.urlencode(uInfo))
            del info['ocrasuite']
            self.info = info

            self.token.LinOtpIsactive = False

        return
Exemple #13
0
    def test_compare_password(self):
        """
        test the new compare passwords - used in the pw and lost token
        """

        # init the SecretObject

        sec_obj = SecretObj(val=libcrypt_password('password'), iv=':1:')

        # run the comparison tests - positive test

        res = sec_obj.compare_password('password')
        self.assertTrue(res)

        # negative test

        res = sec_obj.compare_password('Password')
        self.assertFalse(res)

        return
Exemple #14
0
    def test_compare_password(self):
        """
        test the new compare passwords - used in the pw and lost token
        """

        # init the SecretObject

        sec_obj = SecretObj(val=libcrypt_password('password'), iv=':1:')

        # run the comparison tests - positive test

        res = sec_obj.compare_password('password')
        self.assertTrue(res)

        # negative test

        res = sec_obj.compare_password('Password')
        self.assertFalse(res)

        return
def test_compare_password():
    """
    test to verify the new password comparison in secret object
    used in the pw and lost token.
    """

    # init the SecretObject

    enc_password = utils.crypt_password('password').encode('utf-8')
    sec_obj = SecretObj(val=enc_password, iv=b':1:')

    # run the comparison tests - positive test

    res = sec_obj.compare_password('password')
    assert res

    # negative test

    res = sec_obj.compare_password('Password')
    assert not res
Exemple #16
0
    def setUserPin(self, userPin):
        """
        set the userPin of the token
            the userPin is encrypted and the encrypte value is stored in the
            Token model

        :param userPin: the user pin
        """

        iv, enc_user_pin = SecretObj.encrypt(userPin, hsm=context["hsm"])
        self.token.setUserPin(enc_user_pin, iv)
Exemple #17
0
    def setUserPin(self, userPin):
        """
        set the userPin of the token
            the userPin is encrypted and the encrypte value is stored in the
            Token model

        :param userPin: the user pin
        """

        iv, enc_user_pin = SecretObj.encrypt(userPin, hsm=context['hsm'])
        self.token.setUserPin(enc_user_pin, iv)
Exemple #18
0
    def setOtpKey(self, otpKey, reset_failcount=True):
        """
        set the token seed / secret
            the seed / secret is encrypted and the encrypte value is
            stored in the Token model

        :param otpKey: the token seed / secret
        :param reset_failcount: boolean, if the failcounter should be reseted
        """
        iv, enc_otp_key = SecretObj.encrypt(otpKey, hsm=context['hsm'])
        self.token.set_encrypted_seed(enc_otp_key, iv,
                                      reset_failcount=reset_failcount)
Exemple #19
0
    def _get_secret_object(self):
        """
        encapsulate the returning of the secret object

        the returning of the SecretObj to allow delayed access to the token
        seed eg. only when hmac is calculated, the secret will be decrypted

        :return: SecretObject, containing the token seed
        """
        key, iv = self.token.get_encrypted_seed()
        secObj = SecretObj(key, iv, hsm=context["hsm"])
        return secObj
Exemple #20
0
    def setOtpKey(self, otpKey, reset_failcount=True):
        """
        set the token seed / secret
            the seed / secret is encrypted and the encrypte value is
            stored in the Token model

        :param otpKey: the token seed / secret
        :param reset_failcount: boolean, if the failcounter should be reseted
        """
        iv, enc_otp_key = SecretObj.encrypt(otpKey, hsm=context['hsm'])
        self.token.set_encrypted_seed(enc_otp_key, iv,
                                      reset_failcount=reset_failcount)
Exemple #21
0
    def get_token_data(self):
        """
        get all tokens
        """
        tokens = Session.query(model_token).all()

        for token in tokens:
            token_data = {}
            serial = token.LinOtpTokenSerialnumber
            token_data['Serial'] = serial

            if token.isPinEncrypted():
                iv, enc_pin = token.get_encrypted_pin()
                pin = SecretObj.decrypt_pin(enc_pin, hsm=self.hsm)
                just_mac = serial + token.LinOtpPinHash
                enc_value = self.crypter.encrypt(input_data=pin,
                                                 just_mac=just_mac)
                token_data['TokenPin'] = enc_value

            # the userpin is used in motp and ocra/ocra2 token
            if token.LinOtpTokenPinUser:
                key, iv = token.getUserPin()
                user_pin = SecretObj.decrypt(key, iv, hsm=self.hsm)
                just_mac = serial + token.LinOtpTokenPinUser
                enc_value = self.crypter.encrypt(input_data=user_pin,
                                                 just_mac=just_mac)
                token_data['TokenUserPin'] = enc_value

            # then we retrieve as well the original value,
            # to identify changes
            encKey = token.LinOtpKeyEnc

            key, iv = token.get_encrypted_seed()
            secObj = SecretObj(key, iv, hsm=self.hsm)
            seed = secObj.getKey()
            enc_value = self.crypter.encrypt(input_data=seed,
                                             just_mac=serial + encKey)
            token_data['TokenSeed'] = enc_value
            # next we look for tokens, where the pin is encrypted
            yield token_data
Exemple #22
0
    def get_token_data(self):
        """
        get all tokens
        """
        tokens = Session.query(model_token).all()

        for token in tokens:
            token_data = {}
            serial = token.LinOtpTokenSerialnumber
            token_data['Serial'] = serial

            if token.isPinEncrypted():
                iv, enc_pin = token.get_encrypted_pin()
                pin = SecretObj.decrypt_pin(enc_pin, hsm=self.hsm)
                just_mac = serial + token.LinOtpPinHash
                enc_value = self.crypter.encrypt(input_data=pin,
                                                 just_mac=just_mac)
                token_data['TokenPin'] = enc_value

            # the userpin is used in motp and ocra/ocra2 token
            if token.LinOtpTokenPinUser:
                key, iv = token.getUserPin()
                user_pin = SecretObj.decrypt(key, iv, hsm=self.hsm)
                just_mac = serial + token.LinOtpTokenPinUser
                enc_value = self.crypter.encrypt(input_data=user_pin,
                                                 just_mac=just_mac)
                token_data['TokenUserPin'] = enc_value

            # then we retrieve as well the original value,
            # to identify changes
            encKey = token.LinOtpKeyEnc

            key, iv = token.get_encrypted_seed()
            secObj = SecretObj(key, iv, hsm=self.hsm)
            seed = secObj.getKey()
            enc_value = self.crypter.encrypt(input_data=seed,
                                             just_mac=serial + encKey)
            token_data['TokenSeed'] = enc_value
            # next we look for tokens, where the pin is encrypted
            yield token_data
Exemple #23
0
    def _transform_action(action):
        """
        transform the action, especialy the secret parameter of the url
        """
        servers = []
        name, _sep, values = action.partition("=")
        for value in values.split(" "):
            # decompose the server url to identify, if there is a secret inside
            parsed_server = urllib.parse.urlparse(value)

            # the urlparse has a bug,, where in elder versions, the
            # path is not split from the query
            if not parsed_server.query:
                path, _sep, query = parsed_server.path.partition("?")
            else:
                path = parsed_server.path
                query = parsed_server.query

            # in gereal url parsing allows mutiple entries per key
            # but we support here only one
            params = urllib.parse.parse_qs(query)
            for key, entry in list(params.items()):
                params[key] = entry[0]

            # finally we found the query parameters
            if "secret" in params:
                secret = params["secret"]
                params["encsecret"] = SecretObj.encrypt_pin(secret)
                del params["secret"]

            # build the server url with the encrypted param:
            # as the named tuple is not updateable, we have to convert this
            # into an list to make the update and then back to a tuple to
            # create an url from this
            parsed_list = list(parsed_server[:])
            parsed_list[ForwardServerPolicy.Path_index] = path.strip()
            parsed_list[ForwardServerPolicy.
                        Query_index] = urllib.parse.urlencode(params)
            server_url = urllib.parse.urlunparse(tuple(parsed_list))

            servers.append(server_url)

        ret = "=".join([name, " ".join(servers)])
        return ret
Exemple #24
0
    def checkOtp(self, anOtpVal, counter, window, options=None):
        '''
        checkOtp - validate the token otp against a given otpvalue

        :param anOtpVal: the to be verified otpvalue
        :type anOtpVal:  string

        :param counter: the counter state, that shoule be verified
        :type counter: int

        :param window: the counter +window, which should be checked
        :type window: int

        :param options: the dict, which could contain token specific info
        :type options: dict

        :return: the counter state or -1
        :rtype: int

        '''

        otplen = self.token.LinOtpOtpLen

        #otime contains the previous verification time
        # the new one must be newer than this!
        otime = self.token.LinOtpCount
        secObj = self._get_secret_object()
        window = self.token.LinOtpCountWindow
        key, iv = self.token.getUserPin()
        secPinObj = SecretObj(key, iv, hsm=context.get('hsm'))

        mtimeOtp = mTimeOtp(secObj, secPinObj, otime, otplen)
        res = mtimeOtp.checkOtp(anOtpVal, window, options=options)

        if (res != -1):
            res = res - 1  ## later on this will be incremented by 1
        if res == -1:
            msg = "verification failed"
        else:
            msg = "verifiction was successful"

        log.debug("[checkOtp] %s :res %r" % (msg, res))
        return res
Exemple #25
0
    def do_request(servers, env, user, passw, options):
        """
        make the call to the foreign server
        """
        log.debug("start request to foreign server: %r", servers)

        for server in servers.split(" "):
            parsed_server = urllib.parse.urlparse(server)

            # the urlparse has a bug,, where in elder versions, the
            # path is not split from the query
            if not parsed_server.query:
                path, _sep, query = parsed_server.path.partition("?")
            else:
                path = parsed_server.path
                query = parsed_server.query

            # finally we found the query parameters
            params = urllib.parse.parse_qs(query)
            for key, entry in list(params.items()):
                params[key] = entry[0]

            if "encsecret" in params:
                params["secret"] = SecretObj.decrypt_pin(params["encsecret"])
                del params["encsecret"]

            parsed_list = list(parsed_server[:])
            parsed_list[ForwardServerPolicy.Path_index] = path.strip()
            parsed_list[ForwardServerPolicy.
                        Query_index] = urllib.parse.urlencode(params)
            server_url = urllib.parse.urlunparse(tuple(parsed_list))

            if "radius://" in server_url:
                rad = RadiusRequest(server=server_url, env=env)
                res, opt = rad.do_request(user, passw, options)
                return res, opt
            elif "http://" in server_url or "https://" in server_url:
                http = HttpRequest(server=server_url, env=env)
                res, opt = http.do_request(user, passw, options)
                return res, opt
Exemple #26
0
            return res

        ##
        # AUTOSYNC starts here
        ##

        counter = self.token.getOtpCounter()
        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        # timeStepping    = int(ocraSuite.T)

        tinfo = self.getTokenInfo()

        # autosync does only work, if we have a token info, where the
        # last challenge and the last sync-counter is stored
Exemple #27
0
    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))

        '''

        s_data = 'None'
        s_session = 'None'
        s_challenge = 'None'
        if data is not None:
            s_data = data
        if session is not None:
            s_session = session
        if challenge is None:
            s_challenge = challenge

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        if not data:
            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)

        serial = self.getSerial()
        counter = self.getOtpCount()

        # set the pin onyl in the compliant hashed mode
        pin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            pin = secObj.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.exception("[OcraTokenClass] Failed to create ocrasuite challenge")
            raise Exception('[OcraTokenClass] Failed to create ocrasuite'
                            'challenge: %r' % (ex))

        #  save the object
        digits = '0123456789'
        transid = ''
        transactionIdLen = 12

        try:
            transactionIdLen = int(getFromConfig("OcraDefaultSuite", '12'))
        except:
            transactionIdLen = 12
            log.debug("[OcraTokenClass] Failed to set transactionId length"
                      " from config - using fallback %d" % (transactionIdLen))

        #  create a non exisiting challenge
        try:
            while True:
                for _c in range(0, transactionIdLen):
                    transid += urandom.choice(digits)

                chall = OcraTokenClass.getTransaction(transid)
                if chall is None:
                    break

            ddata = ''
            if data is not None:
                ddata = data

            chall = OcraChallenge(transid=transid,
                                  tokenserial=serial,
                                  challenge=typ + ':' + challenge,
                                  data=typ + ':' + ddata)
            chall.save()

        except Exception as ex:
            #  this might happen if we have a db problem or
            # the uniqnes constrain does not fit
            log.exception("[OcraTokenClass] Failed to create challenge")
            raise Exception('[OcraTokenClass] Failed to create challenge'
                            ' object: %s' % (ex))

        realms = []
        tokenrealms = self.token.getRealms()
        for realm in tokenrealms:
            realms.append(realm.name)

        url = get_qrtan_url(realms)

        return (transid, challenge, True, url)
Exemple #28
0
    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)

        '''

        ret = -1

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        # if we have no transactionid given through the options,
        # we have to retrieve the eldest challenge for this ocra token
        serial = self.getSerial()
        challenges = []

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.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))

        if options is None:
            challenges = OcraTokenClass.getTransactions4serial(
                                                        serial,
                                                        currentOnly=True)

        elif options is not None:
            if type(options).__name__ != 'dict':
                err = ('[chekOtp] "options" not of type dict! %r' %
                       type(options))
                log.error(err)
                raise Exception(err)

            if 'transactionid' in options:
                transid = options.get('transactionid')
                challenges.append(OcraTokenClass.getTransaction(transid))

            elif 'challenge' in options:
                challenges.append(options)

            # due to the added options in checkUserPass, we have to extend
            # the logic here:
            # if no challenges found in between but we have a serial, we catch
            # the open challenges by serial (s.o.)
            if len(challenges) == 0:
                challenges = OcraTokenClass.getTransactions4serial(
                                                        serial,
                                                        currentOnly=True)

        if len(challenges) == 0:
            #  verify that there has already been a challenge
            challenges = OcraTokenClass.getTransactions4serial(serial)
            if len(challenges) > 0:
                err = 'No current transaction found!'
                ret = -1
                return ret
            else:
                err = 'No open transaction found!'
                log.info(err)
                if type(options) == dict and 'transactionid' in options:
                    raise Exception(err)
                ret = -1
                return ret

        for ch in challenges:
            challenge = {}

            if isinstance(ch, dict):
                #  transaction less checkOtp
                self.transId = 0
                challenge.update(ch)

            elif type(ch) == OcraChallenge:
                #  preserve transaction context, so we could use this in
                # the status callback
                self.transId = ch.transid
                challenge['challenge'] = ch.challenge
                challenge['transid'] = ch.transid
                challenge['session'] = ch.session

            ret = ocraSuite.checkOtp(passw, counter, window, challenge,
                                     pin=ocraPin, options=options,
                                     timeshift=timeShift)

            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
Exemple #29
0
    def _rollout_2(self, params):
        '''
        2.

        https://linotpserver/admin/init?
            type=ocra&
            genkey=1&
            activationcode=AKTIVIERUNGSCODE&
            user=BENUTZERNAME&
            message=MESSAGE&
            session=SESSIONKEY

        =>> "serial" : SERIENNUMMER, "nonce" : DATAOBJECT,
            "transactionid" : "TRANSAKTIONSID, "app_import" : IMPORTURL

        - nonce - von HSM oder random ?
        - pkcs5 - kdf2
        - es darf zur einer Zeit nur eine QR Token inaktiv
                                        (== im Ausrollzustand) sein !!!!!
          der Token wird über den User gefunden
        - seed = pdkdf2(nonce + activcode + shared secret)
        - challenge generiern - von urandom oder HSM

        '''

        activationcode = params.get('activationcode', None)
        if activationcode is not None:

            #  genkey might have created a new key, so we have to rely on
            encSharedSecret = self.getFromTokenInfo('sharedSecret', None)
            if encSharedSecret is None:
                raise Exception('missing shared secret of initialition'
                                ' for token %r' % (self.getSerial()))

            sharedSecret = SecretObj.decrypt_pin(encSharedSecret)

            #  we generate a nonce, which in the end is a challenge
            nonce = createNonce()
            self.addToTokenInfo('nonce', nonce)

            #  create a new key from the ocrasuite
            key_len = 20
            if self.ocraSuite.find('-SHA256'):
                key_len = 32
            elif self.ocraSuite.find('-SHA512'):
                key_len = 64

            newkey = kdf2(sharedSecret, nonce, activationcode, key_len)
            self.setOtpKey(binascii.hexlify(newkey))

            #  generate challenge, which is part of the app_import
            message = params.get('message', None)
            (transid, challenge, _ret, url) = self.challenge(message)

            #  generate response
            info = {}
            uInfo = {}
            info['serial'] = self.getSerial()
            uInfo['se'] = self.getSerial()
            info['nonce'] = nonce
            uInfo['no'] = nonce
            info['transactionid'] = transid
            uInfo['tr'] = transid
            info['challenge'] = challenge
            uInfo['ch'] = challenge
            if message is not None:
                uInfo['me'] = str(message.encode("utf-8"))

            ustr = urllib.parse.urlencode({'u': str(url.encode("utf-8"))})
            uInfo['u'] = ustr[2:]
            info['url'] = str(url.encode("utf-8"))

            app_import = 'lseqr://nonce?%s' % (urllib.parse.urlencode(uInfo))

            #  add a signature of the url
            signature = {'si': self.signData(app_import)}
            info['signature'] = signature.get('si')

            info['app_import'] = "%s&%s" % (app_import,
                                            urllib.parse.urlencode(signature))
            self.info = info

            #  setup new state
            self.addToTokenInfo('rollout', '2')
            self.enable(True)

        return
Exemple #30
0
    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

        '''

        ret = False
        challenges = []

        o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial())
        for challenge in o_challenges:
            challenges.append(challenge)

        #  check if there are enough challenges around
        if len(challenges) < 2:
            return False

        challenge1 = {}
        challenge2 = {}

        if options is None:
            ch1 = challenges[0]
            challenge1['challenge'] = ch1.challenge
            challenge1['transid'] = ch1.transid
            challenge1['session'] = ch1.session

            ch2 = challenges[1]
            challenge2['challenge'] = ch2.challenge
            challenge2['transid'] = ch2.transid
            challenge2['session'] = ch2.session

        else:
            if 'challenge1' in options:
                challenge1['challenge'] = options.get('challenge1')
            if 'challenge2' in options:
                challenge2['challenge'] = options.get('challenge2')

        if len(challenge1) == 0 or len(challenge2) == 0:
            error = "No challenges found!"
            log.info('[OcraTokenClass:resync] %s' % (error))
            raise Exception('[OcraTokenClass:resync] %s' % (error))

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        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:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.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('Ocra 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('Ocra 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('Ocra 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.exception('[OcraTokenClass:resync] unknown error: %r' % (ex))
            raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex))

        return ret
Exemple #31
0
    def autosync(self, ocraSuite, passw, challenge):
        '''
        try to resync a token automaticaly, if a former and the current
        request failed

        :param  ocraSuite: the ocraSuite of the current Token
        :type  ocraSuite: ocra object
        :param  passw:
        '''
        res = -1

        autosync = False

        try:
            confsetting = getFromConfig("AutoResync")
            if confsetting is None:
                autosync = False
            elif "true" == confsetting.lower():
                autosync = True
            elif "false" == confsetting.lower():
                autosync = False
        except Exception as ex:
            log.exception('Ocra: autosync check undefined %r' % (ex))
            return res

        ' if autosync is not enabled: do nothing '
        if autosync is False:
            return res

        ##
        # AUTOSYNC starts here
        ##

        counter = self.token.getOtpCounter()
        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        # timeStepping    = int(ocraSuite.T)

        tinfo = self.getTokenInfo()

        # autosync does only work, if we have a token info, where the
        # last challenge and the last sync-counter is stored
        # if no tokeninfo, we start with a autosync request, thus start the
        # lookup in the sync window

        if 'lChallenge' not in tinfo:
            # run checkOtp, with sync window for the current challenge
            log.info('[OcraToken:autosync] initial sync')
            count_0 = -1
            try:
                otp0 = passw
                count_0 = ocraSuite.checkOtp(otp0,
                                             counter,
                                             syncWindow,
                                             challenge,
                                             pin=ocraPin,
                                             timeshift=timeShift)
            except Exception as ex:
                log.exception('Ocra: Error during autosync0: %r' % (ex))

            if count_0 != -1:
                tinfo['lChallenge'] = {'otpc': count_0}
                self.setTokenInfo(tinfo)
                log.info('[OcraToken:autosync] initial sync - success: %r' %
                         count_0)

            res = -1

        else:
            # run checkOtp, with sync window for the current challenge
            count_1 = -1
            try:
                otp1 = passw
                count_1 = ocraSuite.checkOtp(otp1,
                                             counter,
                                             syncWindow,
                                             challenge,
                                             pin=ocraPin,
                                             timeshift=timeShift)
            except Exception as ex:
                log.exception('Ocra: Error during autosync1: %r' % (ex))

            if count_1 == -1:
                del tinfo['lChallenge']
                self.setTokenInfo(tinfo)
                log.info('[OcraToken:autosync] sync failed! Not a valid pass'
                         ' in scope (%r)' % (otp1))
                res = -1
            else:
                # run checkOtp, with sync window for the old challenge
                lChallange = tinfo.get('lChallenge')
                count_0 = lChallange.get('otpc')

                if ocraSuite.C is not None:
                    #  sync the counter based ocra token
                    if count_1 - count_0 < 2:
                        self.setOtpCount(count_1)
                        res = count_1

                if ocraSuite.T is not None:
                    #  sync the timebased ocra token
                    if count_1 - count_0 < ocraSuite.T * 2:
                        # calc the new timeshift !
                        log.debug("[autosync] the counter %r matches: %r" %
                                  (count_1,
                                   datetime.datetime.fromtimestamp(count_1)))

                        currenttime = int(time.time())
                        new_shift = (count_1 - currenttime)

                        tinfo['timeShift'] = new_shift
                        self.setOtpCount(count_1)
                        res = count_1

                #  if we came here, the old challenge is not required anymore
                del tinfo['lChallenge']
                self.setTokenInfo(tinfo)

        return res
Exemple #32
0
            return res

        ##
        # AUTOSYNC starts here
        ##

        counter = self.token.getOtpCounter()
        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        # timeStepping    = int(ocraSuite.T)

        tinfo = self.getTokenInfo()

        # autosync does only work, if we have a token info, where the
        # last challenge and the last sync-counter is stored
Exemple #33
0
    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

        '''

        ret = False
        challenges = []

        o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial())
        for challenge in o_challenges:
            challenges.append(challenge)

        #  check if there are enough challenges around
        if len(challenges) < 2:
            return False

        challenge1 = {}
        challenge2 = {}

        if options is None:
            ch1 = challenges[0]
            challenge1['challenge'] = ch1.challenge
            challenge1['transid'] = ch1.transid
            challenge1['session'] = ch1.session

            ch2 = challenges[1]
            challenge2['challenge'] = ch2.challenge
            challenge2['transid'] = ch2.transid
            challenge2['session'] = ch2.session

        else:
            if 'challenge1' in options:
                challenge1['challenge'] = options.get('challenge1')
            if 'challenge2' in options:
                challenge2['challenge'] = options.get('challenge2')

        if len(challenge1) == 0 or len(challenge2) == 0:
            error = "No challenges found!"
            log.info('[OcraTokenClass:resync] %s' % (error))
            raise Exception('[OcraTokenClass:resync] %s' % (error))

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        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:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.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('Ocra 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(
                        'Ocra 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('Ocra 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.exception('[OcraTokenClass:resync] unknown error: %r' % (ex))
            raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex))

        return ret
Exemple #34
0
    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)

        '''

        ret = -1

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        # if we have no transactionid given through the options,
        # we have to retrieve the eldest challenge for this ocra token
        serial = self.getSerial()
        challenges = []

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.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))

        if options is None:
            challenges = OcraTokenClass.getTransactions4serial(
                serial, currentOnly=True)

        elif options is not None:
            if type(options).__name__ != 'dict':
                err = ('[chekOtp] "options" not of type dict! %r' %
                       type(options))
                log.error(err)
                raise Exception(err)

            if 'transactionid' in options:
                transid = options.get('transactionid')
                challenges.append(OcraTokenClass.getTransaction(transid))

            elif 'challenge' in options:
                challenges.append(options)

            # due to the added options in checkUserPass, we have to extend
            # the logic here:
            # if no challenges found in between but we have a serial, we catch
            # the open challenges by serial (s.o.)
            if len(challenges) == 0:
                challenges = OcraTokenClass.getTransactions4serial(
                    serial, currentOnly=True)

        if len(challenges) == 0:
            #  verify that there has already been a challenge
            challenges = OcraTokenClass.getTransactions4serial(serial)
            if len(challenges) > 0:
                err = 'No current transaction found!'
                ret = -1
                return ret
            else:
                err = 'No open transaction found!'
                log.info(err)
                if type(options) == dict and 'transactionid' in options:
                    raise Exception(err)
                ret = -1
                return ret

        for ch in challenges:
            challenge = {}

            if isinstance(ch, dict):
                #  transaction less checkOtp
                self.transId = 0
                challenge.update(ch)

            elif type(ch) == OcraChallenge:
                #  preserve transaction context, so we could use this in
                # the status callback
                self.transId = ch.transid
                challenge['challenge'] = ch.challenge
                challenge['transid'] = ch.transid
                challenge['session'] = ch.session

            ret = ocraSuite.checkOtp(passw,
                                     counter,
                                     window,
                                     challenge,
                                     pin=ocraPin,
                                     options=options,
                                     timeshift=timeShift)

            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
Exemple #35
0
    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))

        '''

        s_data = 'None'
        s_session = 'None'
        s_challenge = 'None'
        if data is not None:
            s_data = data
        if session is not None:
            s_session = session
        if challenge is None:
            s_challenge = challenge

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        if not data:
            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)

        serial = self.getSerial()
        counter = self.getOtpCount()

        # set the pin onyl in the compliant hashed mode
        pin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            pin = secObj.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.exception(
                "[OcraTokenClass] Failed to create ocrasuite challenge")
            raise Exception('[OcraTokenClass] Failed to create ocrasuite'
                            'challenge: %r' % (ex))

        #  save the object
        digits = '0123456789'
        transid = ''
        transactionIdLen = 12

        try:
            transactionIdLen = int(getFromConfig("OcraDefaultSuite", '12'))
        except:
            transactionIdLen = 12
            log.debug("[OcraTokenClass] Failed to set transactionId length"
                      " from config - using fallback %d" % (transactionIdLen))

        #  create a non exisiting challenge
        try:
            while True:
                for _c in range(0, transactionIdLen):
                    transid += urandom.choice(digits)

                chall = OcraTokenClass.getTransaction(transid)
                if chall is None:
                    break

            ddata = ''
            if data is not None:
                ddata = data

            chall = OcraChallenge(transid=transid,
                                  tokenserial=serial,
                                  challenge=typ + ':' + challenge,
                                  data=typ + ':' + ddata)
            chall.save()

        except Exception as ex:
            #  this might happen if we have a db problem or
            # the uniqnes constrain does not fit
            log.exception("[OcraTokenClass] Failed to create challenge")
            raise Exception('[OcraTokenClass] Failed to create challenge'
                            ' object: %s' % (ex))

        realms = []
        tokenrealms = self.token.getRealms()
        for realm in tokenrealms:
            realms.append(realm.name)

        url = get_qrtan_url(realms)

        return (transid, challenge, True, url)