Ejemplo n.º 1
0
def generate_secret(rate_limit=(3, 30), window_size=3, scratch_tokens=5):
    good_chars = base64._b32alphabet.values()

    secret = ''
    while len(secret) < 16:
        rchar = os.urandom(1)
        if rchar in good_chars:
            secret += rchar

    gaus = totpcgi.GAUserSecret(secret)

    gaus.rate_limit = rate_limit
    gaus.window_size = window_size

    good_chars = '0123456789'
    for i in xrange(scratch_tokens):
        scratch_token = ''
        while len(scratch_token) < 8:
            rchar = os.urandom(1)
            if rchar in good_chars:
                scratch_token += rchar

        gaus.scratch_tokens.append(scratch_token)

    return gaus
Ejemplo n.º 2
0
    def get_user_secret(self, user, pincode=None):
        cur = self.conn.cursor()

        logger.debug('Querying DB for user %s' % user)

        cur.execute(
            '''
            SELECT s.secret, 
                   s.rate_limit_times, 
                   s.rate_limit_seconds, 
                   s.window_size
              FROM secrets AS s 
              JOIN users AS u USING (userid)
             WHERE u.username = %s''', (user, ))
        row = cur.fetchone()

        if not row:
            raise totpcgi.UserNotFound('no secrets record for %s' % user)

        (secret, rate_limit_times, rate_limit_seconds, window_size) = row

        using_encrypted_secret = False
        if secret.find('aes256+hmac256') == 0 and pincode is not None:
            secret = totpcgi.utils.decrypt_secret(secret, pincode)
            using_encrypted_secret = True

        gaus = totpcgi.GAUserSecret(secret)
        if rate_limit_times is not None and rate_limit_seconds is not None:
            gaus.rate_limit = (rate_limit_times, rate_limit_seconds)

        if window_size is not None:
            gaus.window_size = window_size

        # Not loading scratch tokens if using encrypted secret
        if using_encrypted_secret:
            return gaus

        logger.debug('Querying DB for scratch tokens for %s' % user)

        cur.execute(
            '''
            SELECT st.token
              FROM scratch_tokens AS st
              JOIN users AS u USING (userid)
             WHERE u.username = %s''', (user, ))

        for (token, ) in cur.fetchall():
            gaus.scratch_tokens.append(token)

        return gaus
Ejemplo n.º 3
0
def generate_secret(rate_limit=(3, 30), window_size=3, scratch_tokens=5,
                    bs=80):
    # os.urandom expects bytes, so we divide by 8
    secret = base64.b32encode(os.urandom(int(bs / 8))).decode('utf-8')

    gaus = totpcgi.GAUserSecret(secret)

    gaus.rate_limit = rate_limit
    gaus.window_size = window_size

    for i in range(scratch_tokens):
        token = str(struct.unpack('I', os.urandom(4))[0]).zfill(8)[-8:]
        gaus.scratch_tokens.append(token)

    return gaus
Ejemplo n.º 4
0
    def get_user_secret(self, user, pincode=None):

        totp_file = os.path.join(self.secrets_dir, user) + '.totp'
        logger.debug('Examining user secret file: %s' % totp_file)

        if not os.access(totp_file, os.R_OK):
            raise totpcgi.UserNotFound('%s.totp does not exist or is not readable' % user)

        fh = open(totp_file, 'r')
        flock(fh, LOCK_SH)

        # secret is always the first entry
        secret = fh.readline()
        secret = secret.strip()

        using_encrypted_secret = False
        if secret.find('aes256+hmac256') == 0 and pincode is not None:
            secret = totpcgi.utils.decrypt_secret(secret, pincode)
            using_encrypted_secret = True

        gaus = totpcgi.GAUserSecret(secret)

        while True:
            line = fh.readline()

            if line == '':
                break

            line = line.strip()

            if len(line) and line[0] == '"':
                if line[2:12] == 'RATE_LIMIT':
                    (tries, seconds) = line[13:].split(' ')
                    gaus.rate_limit = (int(tries), int(seconds))
                    logger.debug('rate_limit=%s' % str(gaus.rate_limit))

                elif line[2:13] == 'WINDOW_SIZE':
                    window_size = int(line[14:])
                    if window_size > 0 and window_size < 3:
                        window_size = 3
                    gaus.window_size = window_size

                    logger.debug('window_size=%s' % window_size)

            # Scratch code tokens are 8-digit
            # We ignore scratch tokens if we're using encrypted secret
            elif len(line) == 8 and not using_encrypted_secret:
                try:
                    gaus.scratch_tokens.append(int(line))
                    logger.debug('Found a scratch-code token, adding it')
                except ValueError:
                    logger.debug('Non-numeric scratch token found')
                    # don't fail, just pretend we didn't see it
                    continue

        flock(fh, LOCK_UN)
        fh.close()

        # Make sure that we have a window_size defined
        # The topt configuration many not have had one, if not we need
        # to make sure we set it to the default of 3
        if not hasattr(gaus, 'window_size'):
                gaus.window_size = 3

        return gaus
Ejemplo n.º 5
0
    def get_user_secret(self, user, pincode=None):

        totp_file = os.path.join(self.secrets_dir, user) + '.totp'
        logger.debug('Examining user secret file: %s', totp_file)

        if not os.access(totp_file, os.R_OK):
            raise totpcgi.UserNotFound(
                '%s.totp does not exist or is not readable' % user)

        with open(totp_file, 'r') as fh:
            lockf(fh, LOCK_SH)

            # secret is always the first entry
            secret = fh.readline()
            secret = secret.strip()

            using_encrypted_secret = False
            if secret.find('aes256+hmac256') == 0:
                using_encrypted_secret = True
                if pincode is not None:
                    secret = totpcgi.utils.decrypt_secret(secret, pincode)
                else:
                    raise totpcgi.UserSecretError(
                        'Secret is encrypted, but no pincode provided')

            gaus = totpcgi.GAUserSecret(secret)

            while True:
                line = fh.readline()

                if line == '':
                    break

                line = line.strip()

                if len(line) and line[0] == '"':
                    if line[2:12] == 'RATE_LIMIT':
                        (tries, seconds) = line[13:].split(' ')
                        gaus.rate_limit = (int(tries), int(seconds))
                        logger.debug('rate_limit=%s', str(gaus.rate_limit))

                    elif line[2:13] == 'WINDOW_SIZE':
                        window_size = int(line[14:])
                        if 0 < window_size < 3:
                            window_size = 3
                        gaus.window_size = window_size
                        logger.debug('window_size=%s', window_size)

                    elif line[2:14] == 'HOTP_COUNTER':
                        # This will most likely be overriden by user state, but load it up anyway,
                        # as this will trigger HOTP mode.
                        try:
                            gaus.set_hotp(int(line[15:]))
                        except ValueError:
                            gaus.set_hotp(0)

                        logger.debug('hotp_counter=%s', gaus.counter)

                # Scratch code tokens are 8-digit
                # We ignore scratch tokens if we're using encrypted secret
                elif len(line) == 8 and not using_encrypted_secret:
                    try:
                        gaus.scratch_tokens.append(line)
                        logger.debug('Found a scratch-code token, adding it')
                    except ValueError:
                        logger.debug('Non-numeric scratch token found')
                        # don't fail, just pretend we didn't see it
                        continue

            lockf(fh, LOCK_UN)

        # Make sure that we have a window_size defined
        # The topt configuration many not have had one, if not we need
        # to make sure we set it to the default of 3
        if not hasattr(gaus, 'window_size'):
            gaus.window_size = 3

        return gaus