def setDatabases(self, settingsDatabase): self.__settingsTable = RMUserSettingsTable(settingsDatabase) self.__tokensTable = RMAuthTokensTable(settingsDatabase)
class RMAuth: def __init__(self): self.__password = self.__encryptPassword("") self.__tokens = OrderedDict() self.__isLoaded = False self.__tokenLongExpirationTimeout = 5 * 365 * 24 * 3600 # ~5 years # TODO: OpenWRT time_t is 32bit Y2038 problem self.__tokenShortExpirationTimeout = 1 * 24 * 3600 # 1 day self.__settingsTable = None self.__tokensTable = None def setDatabases(self, settingsDatabase): self.__settingsTable = RMUserSettingsTable(settingsDatabase) self.__tokensTable = RMAuthTokensTable(settingsDatabase) def load(self): if not self.__isLoaded: password = self.__settingsTable.getPassword() if password: self.__password = password self.__tokensTable.deleteExpiredRecords(rmCurrentTimestamp()) self.__tokens = self.__tokensTable.getAllRecords(True) self.__isLoaded = True def factoryReset(self): self.__password = self.__encryptPassword("") self.__tokens.clear() self.__tokensTable.deleteAllRecords() self.__settingsTable.savePassword(self.__password) def doPasswordsMatch (self, password): if self.__password == self.__encryptPassword(password): return True return False def authenticateByPassword(self, password, remember, oldAccessToken): self.__deleteExpiredTokens() if self.doPasswordsMatch(password): self.__deleteAccessToken(oldAccessToken) return self.__generateToken(remember), self.__manglePassword(self.__password) return None, None def authenticateByToken(self, token): self.__deleteExpiredTokens() return self.__tokens.get(token, None) def authenticateByTOTP(self, totp): if self.validateTOTP(totp, length=6, interval=14400, drift=2): return self.__generateToken(longLiveToken=False) return None def changePassword(self, oldPassword, newPassword, oldAccessToken, requireOldPassword = True): if requireOldPassword and self.__encryptPassword(oldPassword) != self.__password: return None, None if not newPassword or len(newPassword) < 3: return None, None token = self.__tokens.get(oldAccessToken, None) self.__password = self.__encryptPassword(newPassword) self.__tokens.clear() self.__settingsTable.savePassword(self.__password) self.__tokensTable.deleteAllRecords() if token is None: token = self.__generateToken() else: self.__tokens[token.token] = token self.__tokensTable.addRecord(token.token, token.expiration) return token, self.__manglePassword(self.__password) def changePasswordEx(self, mangleddPassword, oldAccessToken): if not mangleddPassword or len(mangleddPassword) < len(self.__password): return None, None token = self.__tokens.get(oldAccessToken, None) encryptedPassword = self.__unmanglePassword(mangleddPassword) self.__password = encryptedPassword self.__tokens.clear() self.__settingsTable.savePassword(self.__password) self.__tokensTable.deleteAllRecords() if token is None: token = self.__generateToken() else: self.__tokens[token.token] = token self.__tokensTable.addRecord(token.token, token.expiration) return token, self.__manglePassword(self.__password) # Functions to generate/validate a TOTP # follows http://jacob.jkrall.net/totp/ def generateTOTP(self, length=6, interval=30, asString=True, clock=None): if clock is None: clock = time.time() secret = self.__password counter = int(clock) // interval message = struct.pack(">Q", counter) hmacDigest = hmac.new(secret, message, hashlib.sha1).digest() # HMAC-SHA-1(SECRET, time()/30) lastByte = ord(hmacDigest[-1]) offset = lastByte & 0xF # last nibble (dbc1, ) = struct.unpack(">I", hmacDigest[offset:offset + 4]) # Dynamic Binary Code #1 dbc2 = dbc1 & 0x7fffffff # 31 bit number with top bit cleared # Dynamic Binary Code #2 if asString: digits = str(dbc2)[-length:] return digits return dbc2 def validateTOTP(self, totp, length=6, interval=30, drift=0): if totp is None: return False clock = time.time() secret = self.__password for d in range(-drift, drift + 1): candidate = self.generateTOTP(length, interval, asString=True, clock=int(clock) + (d * interval)) #log.debug("Validate %s against candidate %s" % (totp, candidate)) try: if totp == candidate: return True except Exception,e: continue return False
class RMAuth: def __init__(self): self.__password = self.__encryptPassword("") self.__tokens = OrderedDict() self.__isLoaded = False self.__tokenLongExpirationTimeout = 5 * 365 * 24 * 3600 # ~5 years # TODO: OpenWRT time_t is 32bit Y2038 problem self.__tokenShortExpirationTimeout = 1 * 24 * 3600 # 1 day self.__settingsTable = None self.__tokensTable = None def setDatabases(self, settingsDatabase): self.__settingsTable = RMUserSettingsTable(settingsDatabase) self.__tokensTable = RMAuthTokensTable(settingsDatabase) def load(self): if not self.__isLoaded: password = self.__settingsTable.getPassword() if password: self.__password = password self.__tokensTable.deleteExpiredRecords(rmCurrentTimestamp()) self.__tokens = self.__tokensTable.getAllRecords(True) self.__isLoaded = True def factoryReset(self): self.__password = self.__encryptPassword("") self.__tokens.clear() self.__tokensTable.deleteAllRecords() self.__settingsTable.savePassword(self.__password) def doPasswordsMatch(self, password): if self.__password == self.__encryptPassword(password): return True return False def authenticateByPassword(self, password, remember, oldAccessToken): self.__deleteExpiredTokens() if self.doPasswordsMatch(password): self.__deleteAccessToken(oldAccessToken) return self.__generateToken(remember), self.__manglePassword( self.__password) return None, None def authenticateByToken(self, token): self.__deleteExpiredTokens() return self.__tokens.get(token, None) def authenticateByTOTP(self, totp): if self.validateTOTP(totp, length=6, interval=14400, drift=2): return self.__generateToken(longLiveToken=False) return None def changePassword(self, oldPassword, newPassword, oldAccessToken, requireOldPassword=True): if requireOldPassword and self.__encryptPassword( oldPassword) != self.__password: return None, None if not newPassword or len(newPassword) < 3: return None, None token = self.__tokens.get(oldAccessToken, None) self.__password = self.__encryptPassword(newPassword) self.__tokens.clear() self.__settingsTable.savePassword(self.__password) self.__tokensTable.deleteAllRecords() if token is None: token = self.__generateToken() else: self.__tokens[token.token] = token self.__tokensTable.addRecord(token.token, token.expiration) return token, self.__manglePassword(self.__password) def changePasswordEx(self, mangleddPassword, oldAccessToken): if not mangleddPassword or len(mangleddPassword) < len( self.__password): return None, None token = self.__tokens.get(oldAccessToken, None) encryptedPassword = self.__unmanglePassword(mangleddPassword) self.__password = encryptedPassword self.__tokens.clear() self.__settingsTable.savePassword(self.__password) self.__tokensTable.deleteAllRecords() if token is None: token = self.__generateToken() else: self.__tokens[token.token] = token self.__tokensTable.addRecord(token.token, token.expiration) return token, self.__manglePassword(self.__password) # Functions to generate/validate a TOTP # follows http://jacob.jkrall.net/totp/ def generateTOTP(self, length=6, interval=30, asString=True, clock=None): if clock is None: clock = time.time() secret = self.__password counter = int(clock) // interval message = struct.pack(">Q", counter) hmacDigest = hmac.new( secret, message, hashlib.sha1).digest() # HMAC-SHA-1(SECRET, time()/30) lastByte = ord(hmacDigest[-1]) offset = lastByte & 0xF # last nibble (dbc1, ) = struct.unpack(">I", hmacDigest[offset:offset + 4]) # Dynamic Binary Code #1 dbc2 = dbc1 & 0x7fffffff # 31 bit number with top bit cleared # Dynamic Binary Code #2 if asString: digits = str(dbc2)[-length:] return digits return dbc2 def validateTOTP(self, totp, length=6, interval=30, drift=0): if totp is None: return False clock = time.time() secret = self.__password for d in range(-drift, drift + 1): candidate = self.generateTOTP(length, interval, asString=True, clock=int(clock) + (d * interval)) #log.debug("Validate %s against candidate %s" % (totp, candidate)) try: if totp == candidate: return True except Exception, e: continue return False