# libraries bcrypt = (require 'bcrypt') sqlite3 = (require 'sqlite3').verbose() # bruteforce countermeasure saltRounds = 13 FFTCGDB = (filename) -> @filename = filename @db = new sqlite3.Database @filename, (err) -> if err console.error err.message @db.run """ CREATE TABLE IF NOT EXISTS users ( login text NOT NULL COLLATE NOCASE, pwdhash text NOT NULL, UNIQUE(login) ); """ console.log "[FFTCGDB] Connected to '#{@filename}'" return FFTCGDB::close = -> new Promise (resolve, reject) -> @db.close (err) -> if err resolve "[FFTCGDB] Error closing: '#{err.message}'" else reject "[FFTCGDB] Closed '#{@filename}'" FFTCGDB::register = (login, password) -> that = @ new Promise (resolve, reject) -> # validate user input if login == '' or password == '' # no user name or password given console.log "[FFTCGDB] reg: user name '#{login}' or password empty" reject 'invalid' # hash password bcrypt.hash password, saltRounds, (err, hash) -> if err console.log "[FFTCGDB] reg: hash fail for name '#{login}'" reject 'hash' # try creating row in users table stmt = that.db.prepare 'INSERT INTO users (login, pwdhash) VALUES (?, ?)' stmt.run [login, hash], (err) -> if err console.log "[FFTCGDB] reg: DB fail '#{err.code}' for name '#{login}'" stmt.finalize() # reduce attack surface, don't disclose user names reject 'db' # user already exists else console.log "[FFTCGDB] reg: OK '#{login}'" stmt.finalize() # registration successful resolve uid: @lastID login: login FFTCGDB::login = (login, password) -> that = @ new Promise (resolve, reject) -> # get users table row stmt = that.db.prepare 'SELECT rowid, login, pwdhash FROM users WHERE login = ?' stmt.get [login], (err, row) -> if err console.log "[FFTCGDB] login: DB fail '#{err.code}' for name '#{login}'" stmt.finalize() reject 'db' else if not row # hash the password for timing attack reasons bcrypt.hash password, saltRounds, (err, hash) -> console.log "[FFTCGDB] login: nonexistent '#{login}'" stmt.finalize() # reduce attack surface, don't disclose user names reject 'login' # user doesnt exist else bcrypt.compare password, row.pwdhash, (err, res) -> if err console.log "[FFTCGDB] login: hash fail for name '#{login}'" reject 'hash' if res == true console.log "[FFTCGDB] login: OK '#{row.login}'" stmt.finalize() # login successful resolve uid: row.rowid login: row.login else console.log "[FFTCGDB] login: wrong password for '#{login}'" stmt.finalize() # login failed reject 'login' module.exports = FFTCGDB