From 74fff39c3f318444a72ed05a2fa4f254f601c85b Mon Sep 17 00:00:00 2001 From: azivner Date: Wed, 24 Jan 2018 22:13:41 -0500 Subject: [PATCH] refactoring / unification of note encryption / decryption --- routes/api/note_history.js | 11 +---- routes/api/notes.js | 7 +-- routes/api/script.js | 20 +++------ routes/api/tree.js | 9 +--- services/notes.js | 36 +++++++-------- services/protected_session.js | 83 +++++++++++++++++++++++++++++++++-- services/tree.js | 8 +--- 7 files changed, 105 insertions(+), 69 deletions(-) diff --git a/routes/api/note_history.js b/routes/api/note_history.js index 3a5feb2ae..25776b650 100644 --- a/routes/api/note_history.js +++ b/routes/api/note_history.js @@ -4,7 +4,6 @@ const express = require('express'); const router = express.Router(); const sql = require('../../services/sql'); const auth = require('../../services/auth'); -const data_encryption = require('../../services/data_encryption'); const protected_session = require('../../services/protected_session'); const sync_table = require('../../services/sync_table'); const wrap = require('express-promise-wrap').wrap; @@ -12,15 +11,7 @@ const wrap = require('express-promise-wrap').wrap; router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { const noteId = req.params.noteId; const history = await sql.getAll("SELECT * FROM notes_history WHERE note_id = ? order by date_modified_to desc", [noteId]); - - const dataKey = protected_session.getDataKey(req); - - for (const hist of history) { - if (hist.is_protected) { - hist.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(hist.note_history_id), hist.note_title); - hist.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(hist.note_history_id), hist.note_text); - } - } + protected_session.decryptNoteHistoryRows(req, history); res.send(history); })); diff --git a/routes/api/notes.js b/routes/api/notes.js index cccdcccd2..1ca6aa93b 100644 --- a/routes/api/notes.js +++ b/routes/api/notes.js @@ -24,12 +24,7 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { return res.status(404).send({}); } - if (detail.is_protected) { - const dataKey = protected_session.getDataKey(req); - - detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(detail.note_id), detail.note_title); - detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(detail.note_id), detail.note_text); - } + protected_session.decryptNote(req, detail); res.send({ detail: detail diff --git a/routes/api/script.js b/routes/api/script.js index 53cb5ccb3..45d64c268 100644 --- a/routes/api/script.js +++ b/routes/api/script.js @@ -7,7 +7,6 @@ const wrap = require('express-promise-wrap').wrap; const log = require('../../services/log'); const sql = require('../../services/sql'); const protected_session = require('../../services/protected_session'); -const data_encryption = require('../../services/data_encryption'); router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => { log.info('Executing script: ' + req.body.script); @@ -22,21 +21,21 @@ router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => { router.get('/subtree/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { const noteId = req.params.noteId; - const dataKey = protected_session.getDataKey(req); - - res.send(await getSubTreeScripts(noteId, [noteId], dataKey)); + res.send(await getSubTreeScripts(noteId, [noteId], req)); })); async function getSubTreeScripts(parentId, includedNoteIds, dataKey) { - const childs = await sql.getAll(`SELECT notes.note_id, notes.note_title, notes.note_text, notes.is_protected + const children = await sql.getAll(`SELECT notes.note_id, notes.note_title, notes.note_text, notes.is_protected FROM notes JOIN notes_tree USING(note_id) WHERE notes_tree.is_deleted = 0 AND notes.is_deleted = 0 AND notes_tree.parent_note_id = ? AND notes.type = 'code' AND (notes.mime = 'application/javascript' OR notes.mime = 'text/html')`, [parentId]); + protected_session.decryptNotes(dataKey, children); + let script = "\r\n"; - for (const child of childs) { + for (const child of children) { if (includedNoteIds.includes(child.note_id)) { return; } @@ -45,15 +44,6 @@ async function getSubTreeScripts(parentId, includedNoteIds, dataKey) { script += await getSubTreeScripts(child.note_id, includedNoteIds, dataKey); - if (child.is_protected) { - if (!dataKey) { - throw new Error("Protected note is included, but script isn't running in protected session."); - } - - child.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(child.note_id), child.note_title); - child.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(child.note_id), child.note_text); - } - script += child.note_text + "\r\n"; } diff --git a/routes/api/tree.js b/routes/api/tree.js index 853a0e9a1..28ec177d1 100644 --- a/routes/api/tree.js +++ b/routes/api/tree.js @@ -7,7 +7,6 @@ const options = require('../../services/options'); const utils = require('../../services/utils'); const auth = require('../../services/auth'); const protected_session = require('../../services/protected_session'); -const data_encryption = require('../../services/data_encryption'); const sync_table = require('../../services/sync_table'); const wrap = require('express-promise-wrap').wrap; @@ -27,13 +26,7 @@ router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => { ORDER BY note_position`); - const dataKey = protected_session.getDataKey(req); - - for (const note of notes) { - if (note.is_protected) { - note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); - } - } + protected_session.decryptNotes(req, notes); res.send({ notes: notes, diff --git a/services/notes.js b/services/notes.js index 0a0f90bb0..4abd7d362 100644 --- a/services/notes.js +++ b/services/notes.js @@ -5,6 +5,7 @@ const notes = require('./notes'); const data_encryption = require('./data_encryption'); const sync_table = require('./sync_table'); const attributes = require('./attributes'); +const protected_session = require('./protected_session'); async function createNewNote(parentNoteId, note, sourceId) { const noteId = utils.newNoteId(); @@ -67,11 +68,6 @@ async function createNewNote(parentNoteId, note, sourceId) { }; } -async function encryptNote(note, dataKey) { - note.detail.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.detail.note_id), note.detail.note_title); - note.detail.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.detail.note_id), note.detail.note_text); -} - async function protectNoteRecursively(noteId, dataKey, protect, sourceId) { const note = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]); @@ -84,24 +80,20 @@ async function protectNoteRecursively(noteId, dataKey, protect, sourceId) { } } -function decryptNote(note, dataKey) { - note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); - note.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text); - note.is_protected = false; -} - async function protectNote(note, dataKey, protect, sourceId) { let changed = false; if (protect && !note.is_protected) { - note.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); - note.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text); + protected_session.encryptNote(dataKey, note); + note.is_protected = true; changed = true; } else if (!protect && note.is_protected) { - decryptNote(note, dataKey); + protected_session.decryptNote(dataKey, note); + + note.is_protected = false; changed = true; } @@ -121,13 +113,13 @@ async function protectNoteHistory(noteId, dataKey, protect, sourceId) { for (const history of historyToChange) { if (protect) { - history.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title); - history.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text); + protected_session.encryptNoteHistoryRow(dataKey, history); + history.is_protected = true; } else { - history.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title); - history.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text); + protected_session.decryptNoteHistoryRow(dataKey, history); + history.is_protected = false; } @@ -142,7 +134,9 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) { const oldNote = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]); if (oldNote.is_protected) { - decryptNote(oldNote, dataKey); + protected_session.decryptNote(dataKey, oldNote); + + note.is_protected = false; } const newNoteHistoryId = utils.newNoteHistoryId(); @@ -210,11 +204,11 @@ async function saveNoteImages(noteId, noteText, sourceId) { async function updateNote(noteId, newNote, dataKey, sourceId) { if (newNote.detail.note_text === '

 

') { - newNote.detail.note_text = ''; + newNote.detail.note_text = '' } if (newNote.detail.is_protected) { - await encryptNote(newNote, dataKey); + await protected_session.encryptNote(dataKey, newNote.detail); } const attributesMap = await attributes.getNoteAttributeMap(noteId); diff --git a/services/protected_session.js b/services/protected_session.js index 31b9e82ba..abdb089e3 100644 --- a/services/protected_session.js +++ b/services/protected_session.js @@ -1,6 +1,7 @@ "use strict"; const utils = require('./utils'); +const data_encryption = require('./data_encryption'); const session = {}; function setDataKey(req, decryptedDataKey) { @@ -14,8 +15,16 @@ function getProtectedSessionId(req) { return req.headers.protected_session_id; } -function getDataKey(req) { - const protectedSessionId = getProtectedSessionId(req); +/** + * @param obj - can be either array, in that case it's considered to be already dataKey and we just return it + * if it's not a array, we consider it a request object and try to pull dataKey based on the session id header + */ +function getDataKey(obj) { + if (!obj || obj.constructor.name === 'Array') { + return obj; + } + + const protectedSessionId = getProtectedSessionId(obj); if (protectedSessionId && session.protectedSessionId === protectedSessionId) { return session.decryptedDataKey; @@ -31,8 +40,76 @@ function isProtectedSessionAvailable(req) { return protectedSessionId && session.protectedSessionId === protectedSessionId; } +function decryptNote(dataKey, note) { + dataKey = getDataKey(dataKey); + + if (!note.is_protected) { + return; + } + + if (note.note_title) { + note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); + } + + if (note.note_text) { + note.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text); + } +} + +function decryptNotes(dataKey, notes) { + dataKey = getDataKey(dataKey); + + for (const note of notes) { + decryptNote(dataKey, note); + } +} + +function decryptNoteHistoryRow(dataKey, hist) { + dataKey = getDataKey(dataKey); + + if (!hist.is_protected) { + return; + } + + if (hist.note_title) { + hist.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(hist.note_history_id), hist.note_title); + } + + if (hist.note_text) { + hist.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(hist.note_history_id), hist.note_text); + } +} + +function decryptNoteHistoryRows(dataKey, historyRows) { + dataKey = getDataKey(dataKey); + + for (const hist of historyRows) { + decryptNoteHistoryRow(dataKey, hist); + } +} + +function encryptNote(dataKey, note) { + dataKey = getDataKey(dataKey); + + note.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); + note.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(note.note_id), note.note_text); +} + +function encryptNoteHistoryRow(dataKey, history) { + dataKey = getDataKey(dataKey); + + history.note_title = data_encryption.encrypt(dataKey, data_encryption.noteTitleIv(history.note_history_id), history.note_title); + history.note_text = data_encryption.encrypt(dataKey, data_encryption.noteTextIv(history.note_history_id), history.note_text); +} + module.exports = { setDataKey, getDataKey, - isProtectedSessionAvailable + isProtectedSessionAvailable, + decryptNote, + decryptNotes, + decryptNoteHistoryRow, + decryptNoteHistoryRows, + encryptNote, + encryptNoteHistoryRow }; \ No newline at end of file diff --git a/services/tree.js b/services/tree.js index 2a0a45623..9a6ee257b 100644 --- a/services/tree.js +++ b/services/tree.js @@ -80,17 +80,13 @@ async function loadSubTreeNoteIds(parentNoteId, subTreeNoteIds) { } } -async function sortNotesAlphabetically(parentNoteId, dataKey, sourceId) { +async function sortNotesAlphabetically(parentNoteId, req, sourceId) { await sql.doInTransaction(async () => { const notes = await sql.getAll(`SELECT note_tree_id, note_id, note_title, is_protected FROM notes JOIN notes_tree USING(note_id) WHERE notes_tree.is_deleted = 0 AND parent_note_id = ?`, [parentNoteId]); - for (const note of notes) { - if (note.is_protected) { - note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); - } - } + protected_session.decryptNotes(req, notes); notes.sort((a, b) => a.note_title.toLowerCase() < b.note_title.toLowerCase() ? -1 : 1);