diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index ff2dfbc..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "env": { - "webextensions": true, - "browser": true, - "es2021": true - }, - "extends": [ - "eslint:recommended" - ], - "globals": { - "process": "readonly" - }, - "ignorePatterns": ["node_modules"], - "parserOptions": { - "ecmaVersion": "latest", - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module" - }, - "plugins": ["@stylistic/js"], - "rules": { - "no-console": ["error", {"allow": ["warn", "error"]}], - "no-unused-vars": ["error", {"argsIgnorePattern": "^_"}], - "@stylistic/js/indent": ["error", "tab"], - "@stylistic/js/semi": ["error", "always"] - } -} diff --git a/.gitignore b/.gitignore index 645de9c..8234428 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,4 @@ *.zip *.png -.web-extension-id - -node_modules \ No newline at end of file +.web-extension-id \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 7715576..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock = false \ No newline at end of file diff --git a/background.js b/background.js index 8515be2..ff82fbc 100644 --- a/background.js +++ b/background.js @@ -4,75 +4,54 @@ let state = { salt: window.crypto.getRandomValues(new Uint8Array(16)), key: null, entries: [] -}; - -let popup_port = null; // communication channel with popup - -function send_state(state) -{ - popup_port.postMessage({type: 'state', data: state}); } -function show_error(error) -{ - console.error(error); - popup_port.postMessage({type: 'notification', data: error}); -} +let popup_port = null // communication channel with popup function b32decode(text) { - const up_text = text.toUpperCase(); - let result = []; - let lshift = 3; - let carry = 0; + const up_text = text.toUpperCase() + let result = [] + let lshift = 3 + let carry = 0 for(const index in up_text) { - const char = up_text[index]; + const char = up_text[index] if(char === '=') - break; - let value = char.charCodeAt(); - if(value >= 65 && value <= 90) // char in [A- Z] - value -= 65; - else if(value >= 50 && value <= 55) // char in [2-7] - value -= 24; - else - throw `Incorrect Base32 char '${text[index]}' at index ${index}`; + break + let value = char.charCodeAt() - 65 + if(value < 0) + value += 41 + if(value < 0 || value > 31) + throw `Incorrect Base32 char '${text[index]}' at index ${index}` if(lshift > 0) // next value is needed to complete the octet { - carry += value << lshift; - lshift -= 5; + carry += value << lshift + lshift -= 5 if(lshift === 0) // last value aligned with octet - carry = 0; + carry = 0 } else // getting the last octet' bits { - result.push(carry + (value >>> (-lshift))); - carry = (value << (lshift + 8)) & 248; // if lshit is 0 this will always be 0 - lshift += 3; + result.push(carry + (value >>> (-lshift))) + carry = (value << (lshift + 8)) & 248 // if lshit is 0 this will always be 0 + lshift += 3 } } if(carry !== 0) - result.push(carry); - return result; + result.push(carry) + return result } // Create encryption key from password async function setKey(password) { - const storage = await browser.storage.local.get(); + const storage = await browser.storage.local.get() if(storage.entries !== undefined && storage.iv !== undefined && storage.salt !== undefined) { - try - { - state.iv = new Uint8Array(b32decode(storage.iv)); - state.salt = new Uint8Array(b32decode(storage.salt)); - } - catch(error) - { - show_error(error); - return; - } + state.iv = new Uint8Array(b32decode(storage.iv)) + state.salt = new Uint8Array(b32decode(storage.salt)) } let encoder = new TextEncoder(); @@ -81,7 +60,7 @@ async function setKey(password) encoder.encode(password), 'PBKDF2', false, - ['deriveKey']); + ['deriveKey']) state.key = await window.crypto.subtle.deriveKey( { name: "PBKDF2", @@ -92,28 +71,17 @@ async function setKey(password) key_material, { "name": "AES-GCM", "length": 256}, true, - ["encrypt", "decrypt"]); + ["encrypt", "decrypt"]) // If entries are set in the state we automatically restore them if(storage.entries !== undefined) - restore(storage); + restore(storage) else - send_state(state); + popup_port.postMessage(state) } // Decrypt entries saved in local storage async function restore(storage) { - let decoded_secret = null; - try - { - decoded_secret = new Uint8Array(b32decode(storage.entries)); - } - catch(error) - { - show_error(error); - return; - } - try { let decrypted = await window.crypto.subtle.decrypt( @@ -121,40 +89,42 @@ async function restore(storage) name: "AES-GCM", iv: state.iv }, - state.key, decoded_secret + state.key, + new Uint8Array(b32decode(storage.entries)) ); let decoder = new TextDecoder(); - state.entries = JSON.parse(decoder.decode(decrypted)); - send_state(state); + state.entries = JSON.parse(decoder.decode(decrypted)) + popup_port.postMessage(state) } - catch(_error) + catch (error) { - state.key = null; - show_error('Cannot decrypt entries: wrong password'); + console.error('Cannot decrypt entries: wrong password') } } function connected(port) { - popup_port = port; + popup_port = port popup_port.onMessage.addListener((message) => { - if(message.command === 'init') + if(message.command === 'init') { if(state.key === null) - send_state(state); + popup_port.postMessage(state) else - browser.storage.local.get().then((storage) => { restore(storage); }); + browser.storage.local.get().then((storage) => { + restore(storage) + }) } else if(message.command === 'key') - setKey(message.password); + setKey(message.password) else if(message.command === 'logout') { - state.key = null; - state.entries = []; + state.key = null + state.entries = [] } else - show_error(`Wrong message: ${message}`); - }); -} + console.error(`Wrong message: ${message}`) + }) + } -browser.runtime.onConnect.addListener(connected); \ No newline at end of file + browser.runtime.onConnect.addListener(connected); \ No newline at end of file diff --git a/icons/export.svg b/icons/export.svg index 0e1c8dc..56951b1 100644 --- a/icons/export.svg +++ b/icons/export.svg @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/icons/import.svg b/icons/import.svg index 56951b1..0e1c8dc 100644 --- a/icons/import.svg +++ b/icons/import.svg @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/manifest.json b/manifest.json index 4f78519..d64d489 100644 --- a/manifest.json +++ b/manifest.json @@ -16,13 +16,9 @@ }, "manifest_version": 2, "name": "uOTP", - "options_ui": { - "page": "options.html", - "open_in_tab": true - }, "permissions": [ "storage", "clipboardWrite" ], - "version": "1.5.1" + "version": "1.1.1" } \ No newline at end of file diff --git a/options.css b/options.css deleted file mode 100644 index 5187838..0000000 --- a/options.css +++ /dev/null @@ -1,96 +0,0 @@ -@media (prefers-color-scheme: dark) { - :root { - --bg-color: hsl(213, 10%, 17%); - --light-bg-color: hsl(213, 10%, 23%); - --highlight-bg-color: hsl(213, 10%, 28%); - --fg-color: #ccc; - --border-color: #888; - --error-bg-color: hsl(0, 38%, 30%); - --img-filter: ; - } -} - -@media (prefers-color-scheme: light) { - :root { - --bg-color: #eee; - --light-bg-color: #ddd; - --highlight-bg-color: #ccc; - --fg-color: #666; - --border-color: #888; - --error-bg-color: hsl(0, 38%, 70%); - --img-filter: invert(1); - } -} - -* -{ - box-sizing: border-box; -} - -html, body { - margin: 0; -} - -body { - background-color: var(--bg-color); - color: var(--fg-color); - padding: 12px; - font-size: 16px; -} - -h1 { - margin: 0 0 16px 0; -} - -h1 img { - height: 1em; - margin-right: 16px; -} - -button { - background-color: var(--light-bg-color); - color: var(--fg-color); - border: 1px solid var(--border-color); - margin: 4px; - cursor: pointer; - display: flex; - align-items: center; - border-radius: 5px; - font-size: 1.2rem; -} - -#notification { - position: absolute; - bottom: 0; - z-index: 10; - display: flex; - flex-direction: row; - justify-content: space-between; - background-color: var(--error-bg-color); - padding: 4px; - transform: translateY(100%); - transition: transform 0.3s; -} - -#notification.show { - transform: translateY(0); -} - -#notification img { - height: 1em; - cursor: pointer; -} - -#notification-text { - white-space: pre-wrap; -} - -.action { - display: flex; - flex-direction: row; -} - -.action img { - height: 1.5em; - margin-right: 8px; -} \ No newline at end of file diff --git a/options.html b/options.html deleted file mode 100644 index 3618391..0000000 --- a/options.html +++ /dev/null @@ -1,24 +0,0 @@ - - -
- - - - - --
-