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
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
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
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
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