Fully Encrypted notes Beta
* Encrypts all notes going to the database * Creates encrypted snippets for loading note title cards * Creates an encrypted search index when note is changed * Migrates users to encrypted notes on login * Creates new encrypted master keys for newly logged in users
This commit is contained in:
1 parent
a545ced98f
commit
df073b0e4d
14 files changed
+553
-269
No files matched your search
+113
-19
@@ -1,7 +1,10 @@
|
||||
var crypto = require('crypto')
|
||||
|
||||
let db = require('@config/database')
|
||||
let Auth = require('@helpers/Auth')
|
||||
const Note = require('@models/Note')
|
||||
|
||||
const db = require('@config/database')
|
||||
const Auth = require('@helpers/Auth')
|
||||
const cs = require('@helpers/CryptoString')
|
||||
|
||||
let User = module.exports = {}
|
||||
|
||||
@@ -22,26 +25,32 @@ User.login = (username, password) => {
|
||||
//User not found, create a new account with set data
|
||||
if(rows[0].length == 0){
|
||||
User.create(lowerName, password)
|
||||
.then(loginToken => {
|
||||
resolve(loginToken)
|
||||
.then( ({token, userId}) => {
|
||||
return resolve({ token, userId })
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//hash the password and check for a match
|
||||
const salt = new Buffer(lookedUpUser.salt, 'binary')
|
||||
crypto.pbkdf2(password, salt, lookedUpUser.iterations, 512, 'sha512', function(err, delivered_key){
|
||||
if(delivered_key.toString('hex') === lookedUpUser.password){
|
||||
if(lookedUpUser && lookedUpUser.salt){
|
||||
//hash the password and check for a match
|
||||
const salt = new Buffer(lookedUpUser.salt, 'binary')
|
||||
crypto.pbkdf2(password, salt, lookedUpUser.iterations, 512, 'sha512', function(err, delivered_key){
|
||||
if(delivered_key.toString('hex') === lookedUpUser.password){
|
||||
|
||||
//Passback a json web token
|
||||
const token = Auth.createToken(lookedUpUser.id)
|
||||
resolve(token)
|
||||
User.generateMasterKey(lookedUpUser.id, password)
|
||||
.then( result => User.getMasterKey(lookedUpUser.id, password))
|
||||
.then(masterKey => {
|
||||
|
||||
} else {
|
||||
//Passback a json web token
|
||||
const token = Auth.createToken(lookedUpUser.id, masterKey)
|
||||
resolve({ token: token, userId:lookedUpUser.id })
|
||||
})
|
||||
|
||||
reject('Password does not match database')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
reject('Password does not match database')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(console.log)
|
||||
|
||||
@@ -93,9 +102,15 @@ User.create = (username, password) => {
|
||||
|
||||
if(rows[0].affectedRows == 1){
|
||||
|
||||
const newUserId = rows[0].insertId
|
||||
const loginToken = Auth.createToken(newUserId)
|
||||
resolve(loginToken)
|
||||
const userId = rows[0].insertId
|
||||
|
||||
User.generateMasterKey(userId, password)
|
||||
.then( result => User.getMasterKey(userId, password))
|
||||
.then(masterKey => {
|
||||
|
||||
const token = Auth.createToken(userId, masterKey)
|
||||
return resolve({token, userId})
|
||||
})
|
||||
|
||||
} else {
|
||||
//Emit Error to user
|
||||
@@ -166,5 +181,84 @@ User.getCounts = (userId) => {
|
||||
resolve(countTotals)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
User.generateMasterKey = (userId, password) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if(!userId || !password){
|
||||
reject('Need userId and password to generate key')
|
||||
}
|
||||
|
||||
db.promise()
|
||||
.query('SELECT count(id) as total FROM user_key WHERE user_id = ?', [userId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Entry already exists, you good.
|
||||
if(rows[0][0]['total'] > 0){
|
||||
return resolve(true)
|
||||
// throw new Error('User Encryption key already exists')
|
||||
} else {
|
||||
// Generate user key, its big and random
|
||||
const masterPassword = cs.createSmallSalt()
|
||||
console.log('Generating new key for user', userId)
|
||||
|
||||
//Generate a salt because it wants it
|
||||
const salt = cs.createSmallSalt()
|
||||
|
||||
// Encrypt master password
|
||||
const encryptedMasterPassword = cs.encrypt(password, salt, masterPassword)
|
||||
|
||||
const created = Math.round((+new Date)/1000)
|
||||
|
||||
db.promise()
|
||||
.query(
|
||||
'INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);',
|
||||
[userId, salt, encryptedMasterPassword, created]
|
||||
)
|
||||
.then((rows, fields)=>{
|
||||
return Note.encryptEveryNote(userId, masterPassword)
|
||||
})
|
||||
.then(results => {
|
||||
return new Promise((resolve, reject) => { resolve(true) })
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
return resolve(true)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Create Master Password Error')
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
User.getMasterKey = (userId, password) => {
|
||||
|
||||
if(!userId || !password){
|
||||
reject('Need userId and password to fetch key')
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
db.promise().query('SELECT * FROM user_key WHERE user_id = ? LIMIT 1', [userId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
const row = rows[0][0]
|
||||
|
||||
const masterKey = cs.decrypt(password, row['salt'], row['key'])
|
||||
|
||||
if(masterKey == null){
|
||||
return reject('Unable to decrypt key')
|
||||
}
|
||||
|
||||
return resolve(masterKey)
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user