mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	server side encryption WIP
This commit is contained in:
		| @@ -91,8 +91,6 @@ settings.addModule((function() { | |||||||
|  |  | ||||||
|                     // encryption password changed so current encryption session is invalid and needs to be cleared |                     // encryption password changed so current encryption session is invalid and needs to be cleared | ||||||
|                     encryption.resetEncryptionSession(); |                     encryption.resetEncryptionSession(); | ||||||
|  |  | ||||||
|                     encryption.setEncryptedDataKey(result.new_encrypted_data_key); |  | ||||||
|                 } |                 } | ||||||
|                 else { |                 else { | ||||||
|                     showError(result.message); |                     showError(result.message); | ||||||
|   | |||||||
| @@ -23,10 +23,6 @@ const encryption = (function() { | |||||||
|         encryptedDataKey = settings.encrypted_data_key; |         encryptedDataKey = settings.encrypted_data_key; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     function setEncryptedDataKey(encDataKey) { |  | ||||||
|         encryptedDataKey = encDataKey; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setEncryptionSessionTimeout(encSessTimeout) { |     function setEncryptionSessionTimeout(encSessTimeout) { | ||||||
|         encryptionSessionTimeout = encSessTimeout; |         encryptionSessionTimeout = encSessTimeout; | ||||||
|     } |     } | ||||||
| @@ -34,7 +30,7 @@ const encryption = (function() { | |||||||
|     function ensureEncryptionIsAvailable(requireEncryption, modal) { |     function ensureEncryptionIsAvailable(requireEncryption, modal) { | ||||||
|         const dfd = $.Deferred(); |         const dfd = $.Deferred(); | ||||||
|  |  | ||||||
|         if (requireEncryption && dataKey === null) { |         if (requireEncryption && !isEncryptionAvailable()) { | ||||||
|             // if this is entry point then we need to show the app even before the note is loaded |             // if this is entry point then we need to show the app even before the note is loaded | ||||||
|             showAppIfHidden(); |             showAppIfHidden(); | ||||||
|  |  | ||||||
| @@ -58,54 +54,6 @@ const encryption = (function() { | |||||||
|         return dfd.promise(); |         return dfd.promise(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async function getDataKey(password) { |  | ||||||
|         const passwordDerivedKey = await computeScrypt(password, passwordDerivedKeySalt); |  | ||||||
|  |  | ||||||
|         const dataKeyAes = getDataKeyAes(passwordDerivedKey); |  | ||||||
|  |  | ||||||
|         return decrypt(dataKeyAes, encryptedDataKey); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function computeScrypt(password, salt) { |  | ||||||
|         const normalizedPassword = password.normalize('NFKC'); |  | ||||||
|         const passwordBuffer = new buffer.SlowBuffer(normalizedPassword); |  | ||||||
|         const saltBuffer = new buffer.SlowBuffer(salt); |  | ||||||
|  |  | ||||||
|         // this settings take ~500ms on my laptop |  | ||||||
|         const N = 16384, r = 8, p = 1; |  | ||||||
|         // 32 byte key - AES 256 |  | ||||||
|         const dkLen = 32; |  | ||||||
|  |  | ||||||
|         return new Promise((resolve, reject) => { |  | ||||||
|             scrypt(passwordBuffer, saltBuffer, N, r, p, dkLen, (error, progress, key) => { |  | ||||||
|                 if (error) { |  | ||||||
|                     showError(error); |  | ||||||
|  |  | ||||||
|                     reject(error); |  | ||||||
|                 } |  | ||||||
|                 else if (key) { |  | ||||||
|                     resolve(key); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function decryptTreeItems() { |  | ||||||
|         if (!isEncryptionAvailable()) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (const noteId of glob.allNoteIds) { |  | ||||||
|             const note = treeUtils.getNodeByKey(noteId); |  | ||||||
|  |  | ||||||
|             if (note.data.encryption > 0) { |  | ||||||
|                 const title = decryptString(note.data.note_title); |  | ||||||
|  |  | ||||||
|                 note.setTitle(title); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async function setupEncryptionSession() { |     async function setupEncryptionSession() { | ||||||
|         const password = encryptionPasswordEl.val(); |         const password = encryptionPasswordEl.val(); | ||||||
|         encryptionPasswordEl.val(""); |         encryptionPasswordEl.val(""); | ||||||
| @@ -122,6 +70,7 @@ const encryption = (function() { | |||||||
|  |  | ||||||
|         dialogEl.dialog("close"); |         dialogEl.dialog("close"); | ||||||
|  |  | ||||||
|  |         noteEditor.reload(); | ||||||
|         noteTree.reload(); |         noteTree.reload(); | ||||||
|  |  | ||||||
|         if (encryptionDeferred !== null) { |         if (encryptionDeferred !== null) { | ||||||
| @@ -158,7 +107,7 @@ const encryption = (function() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     function isEncryptionAvailable() { |     function isEncryptionAvailable() { | ||||||
|         return dataKey !== null; |         return protectedSessionId !== null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function getDataAes() { |     function getDataAes() { | ||||||
| @@ -167,10 +116,6 @@ const encryption = (function() { | |||||||
|         return new aesjs.ModeOfOperation.ctr(dataKey, new aesjs.Counter(5)); |         return new aesjs.ModeOfOperation.ctr(dataKey, new aesjs.Counter(5)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function getDataKeyAes(passwordDerivedKey) { |  | ||||||
|         return new aesjs.ModeOfOperation.ctr(passwordDerivedKey, new aesjs.Counter(5)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function encryptNoteIfNecessary(note) { |     function encryptNoteIfNecessary(note) { | ||||||
|         if (note.detail.encryption === 0) { |         if (note.detail.encryption === 0) { | ||||||
|             return note; |             return note; | ||||||
| @@ -307,17 +252,6 @@ const encryption = (function() { | |||||||
|         noteEditor.setNoteBackgroundIfEncrypted(note); |         noteEditor.setNoteBackgroundIfEncrypted(note); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function decryptNoteIfNecessary(note) { |  | ||||||
|         if (note.detail.encryption > 0) { |  | ||||||
|             decryptNote(note); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function decryptNote(note) { |  | ||||||
|         note.detail.note_title = decryptString(note.detail.note_title); |  | ||||||
|         note.detail.note_text = decryptString(note.detail.note_text); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async function encryptSubTree(noteId) { |     async function encryptSubTree(noteId) { | ||||||
|         await ensureEncryptionIsAvailable(true, true); |         await ensureEncryptionIsAvailable(true, true); | ||||||
|  |  | ||||||
| @@ -433,10 +367,8 @@ const encryption = (function() { | |||||||
|     }, 5000); |     }, 5000); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         setEncryptedDataKey, |  | ||||||
|         setEncryptionSessionTimeout, |         setEncryptionSessionTimeout, | ||||||
|         ensureEncryptionIsAvailable, |         ensureEncryptionIsAvailable, | ||||||
|         decryptTreeItems, |  | ||||||
|         resetEncryptionSession, |         resetEncryptionSession, | ||||||
|         isEncryptionAvailable, |         isEncryptionAvailable, | ||||||
|         encryptNoteIfNecessary, |         encryptNoteIfNecessary, | ||||||
| @@ -444,7 +376,6 @@ const encryption = (function() { | |||||||
|         decryptString, |         decryptString, | ||||||
|         encryptNoteAndSendToServer, |         encryptNoteAndSendToServer, | ||||||
|         decryptNoteAndSendToServer, |         decryptNoteAndSendToServer, | ||||||
|         decryptNoteIfNecessary, |  | ||||||
|         encryptSubTree, |         encryptSubTree, | ||||||
|         decryptSubTree, |         decryptSubTree, | ||||||
|         getProtectedSessionId |         getProtectedSessionId | ||||||
|   | |||||||
| @@ -209,8 +209,6 @@ const noteEditor = (function() { | |||||||
|  |  | ||||||
|         encryptionPasswordEl.val(''); |         encryptionPasswordEl.val(''); | ||||||
|  |  | ||||||
|         encryption.decryptNoteIfNecessary(currentNote); |  | ||||||
|  |  | ||||||
|         noteTitleEl.val(currentNote.detail.note_title); |         noteTitleEl.val(currentNote.detail.note_title); | ||||||
|  |  | ||||||
|         noteChangeDisabled = true; |         noteChangeDisabled = true; | ||||||
| @@ -234,12 +232,6 @@ const noteEditor = (function() { | |||||||
|     async function loadNote(noteId) { |     async function loadNote(noteId) { | ||||||
|         const note = await $.get(baseApiUrl + 'notes/' + noteId); |         const note = await $.get(baseApiUrl + 'notes/' + noteId); | ||||||
|  |  | ||||||
|         if (note.detail.encryption > 0 && !encryption.isEncryptionAvailable()) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         encryption.decryptNoteIfNecessary(note); |  | ||||||
|  |  | ||||||
|         return note; |         return note; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -190,8 +190,6 @@ const noteTree = (function() { | |||||||
|  |  | ||||||
|         // this will also reload the note content |         // this will also reload the note content | ||||||
|         await treeEl.fancytree('getTree').reload(treeResp.notes); |         await treeEl.fancytree('getTree').reload(treeResp.notes); | ||||||
|  |  | ||||||
|         encryption.decryptTreeItems(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function loadTree() { |     function loadTree() { | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ const sql = require('../../services/sql'); | |||||||
| const options = require('../../services/options'); | const options = require('../../services/options'); | ||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
| const notes = require('../../services/notes'); | const notes = require('../../services/notes'); | ||||||
|  | const protected_session = require('../../services/protected_session'); | ||||||
|  | const data_encryption = require('../../services/data_encryption'); | ||||||
|  |  | ||||||
| router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||||
|     let noteId = req.params.noteId; |     let noteId = req.params.noteId; | ||||||
| @@ -20,6 +22,13 @@ router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | |||||||
|         detail = sql.getSingleResult("select * from notes where note_id = ?", [noteId]); |         detail = sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (detail.encryption > 0) { | ||||||
|  |         const dataKey = protected_session.getDataKey(req); | ||||||
|  |  | ||||||
|  |         detail.note_title = data_encryption.decrypt(dataKey, detail.note_title); | ||||||
|  |         detail.note_text = data_encryption.decrypt(dataKey, detail.note_text); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     res.send({ |     res.send({ | ||||||
|         detail: detail, |         detail: detail, | ||||||
|         images: await sql.getResults("select * from images where note_id = ? order by note_offset", [noteId]), |         images: await sql.getResults("select * from images where note_id = ? order by note_offset", [noteId]), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user