예제 #1
0
파일: __init__.py 프로젝트: soitun/LinOTP
    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
예제 #2
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
예제 #3
0
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
예제 #4
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
예제 #5
0
파일: migrate.py 프로젝트: wjbailey/LinOTP
    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
예제 #6
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
예제 #7
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
예제 #8
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)
예제 #9
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
예제 #10
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