# 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, session text, 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 username # hash password bcrypt.hash password, saltRounds, (err, hash) -> reject 'hash' if err # try creating row in users table that.db.run "INSERT INTO users (login, pwdhash) VALUES ('#{login}', '#{hash}');", (err) -> if err if err.code == 'SQLITE_CONSTRAINT' reject 'existence' else reject 'db' else # registration successful resolve @lastID FFTCGDB::login = (login, password) -> that = @ new Promise (resolve, reject) -> # validate username # get users table row that.db.all "SELECT rowid, pwdhash FROM users WHERE login = '#{login}';", (err, rows) -> if err reject 'db' else if rows.length == 0 # hashing the password for timing attack reasons bcrypt.hash password, saltRounds, (err, hash) -> reject 'existence' else row = rows[0] bcrypt.compare password, row.pwdhash, (err, res) -> reject 'hash' if err if res == true resolve row.rowid else reject 'login' module.exports = FFTCGDB