/* Copyright (C) 2013-2022 Grégory Soutadé This file is part of gPass. gPass is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. gPass is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gPass. If not, see . */ import {parseUri} from "./lib/parseuri.js"; import {SERVER, GPASS_ICON, wildcard_domain, simple_pbkdf2, crypto_pbkdf2, encrypt_ecb, encrypt_cbc, decrypt_cbc, digest, a2hex, hex2a, debug} from "./lib/misc.js"; import {get_preference, set_preference, delete_preference} from "./compat.js"; var browser = browser || chrome; var protocol_version = 4; var account_url = null; function _notification(message, data) { if (message !== data) message += data; var options = { type: "basic", title : "gPass", message : message, iconUrl:"icons/gpass_icon_64.png" }; browser.notifications.create("gPass", options, function(){}); } async function generate_request(domain, login, mkey, iv) { var v = domain + ";" + login; debug("will encrypt " + v); while ((v.length % 16)) v += "\0"; var hash = await digest(crypto, v); v += hash.slice(8, 24); var enc = encrypt_cbc(mkey, iv, v); return enc; } async function ask_server(logins, domain, wdomain, mkey, sendResponse, options) { var account_url = await get_preference("account_url"); var salt = parseUri(account_url); salt = salt["host"] + salt["path"]; debug("salt " + salt); var pbkdf2_level = await get_preference("pbkdf2_level"); var global_iv = await simple_pbkdf2(crypto, salt, mkey, pbkdf2_level); global_iv = global_iv.slice(0, 16); var mkey = crypto_pbkdf2(crypto, mkey, salt, pbkdf2_level); debug("global_iv " + a2hex(global_iv)); var keys = ""; var key_index; var a; for(key_index=0, a=0; a { debug(e); _notification("Network error : check your server address", ""); }); if (response === undefined) { ret = SERVER.ERROR; sendResponse({"value": ret, options:options}); return; } const responseText = await response.text(); var ciphered_password = ""; var server_pbkdf2_level = 0; var server_version = 0; var matched_key = 0; var r = responseText.split("\n"); debug("resp " + r); for(var a=0; a protocol_version) { _notification("Protocol version not supported, please upgrade your addon", ""); ret = SERVER.FAILED; } else { switch (server_protocol_version) { case 2: server_pbkdf2_level = 1000; break; case 3: // Version 3 : nothing special to do case 4: // Version 4 : nothing special to do break; } } break; case "matched_key": matched_key = params[1]; case "pass": ciphered_password = params[1]; break; case "pkdbf2_level": case "pbkdf2_level": server_pbkdf2_level = parseInt(params[1].match(/\d+/)[0], 10); if (server_pbkdf2_level != NaN && server_pbkdf2_level != pbkdf2_level && server_pbkdf2_level >= 1000) // Minimum level for PBKDF2 ! { debug("New pbkdf2 level " + server_pbkdf2_level); pbkdf2_level = server_pbkdf2_level; set_preference("pbkdf2_level", pbkdf2_level, null); ret = SERVER.RESTART_REQUEST; } break; case "": break; default: debug("Unknown command " + params[0]); _notification("Error : It seems that it's not a gPass server", responseText); ret = SERVER.FAILED; break; } } if (ret != SERVER.OK) { sendResponse({"value": ret, options:options}); return; } if (ciphered_password != "") { debug("Ciphered password : " + ciphered_password); var clear_password = await decrypt_cbc(mkey, global_iv, hex2a(ciphered_password)); clear_password = clear_password.replace(/\0*$/, ""); clear_password = clear_password.substr(3, clear_password.length); debug("Clear password " + clear_password); sendResponse({"value": ret, "password":clear_password, "options":options}); } else { debug("No password found"); ret = SERVER.FAILED; _notification("No password found in database", "") sendResponse({"value": ret, "options":options}); } return; } function update_gpass_icon(iconId, tabId) { debug("update_gpass_icon"); var icon_infos = {"tabId":tabId}; var icon_name = ""; switch (iconId) { case GPASS_ICON.NORMAL: break; case GPASS_ICON.DISABLED: icon_name = "_disabled"; break; case GPASS_ICON.ACTIVATED: icon_name = "_activated"; break; default: } debug(icon_name); var icon_infos = {}; icon_infos["path"] = { 16:"icons/gpass" + icon_name + "_icon_16.png", 32:"icons/gpass" + icon_name + "_icon_32.png", 64:"icons/gpass" + icon_name + "_icon_64.png", 128:"icons/gpass" + icon_name + "_icon_128.png", }; if (browser.browserAction) browser.browserAction.setIcon(icon_infos); else browser.action.setIcon(icon_infos); } async function is_gpass_enabled(uri) { return await get_preference("always_disabled").then( function(always_disabled) { if (always_disabled) { debug("Always disabled"); return new Promise(function(resolve, reject) { resolve(1); // null -> enabled, 1 -> disabled }); } else { debug("Check for enable"); var domain = parseUri(uri); domain = domain["host"]; debug("Is gpass enabled for " + domain + " ?"); return get_preference("disable-" + domain); } } ); } function save_gpass_enable_config(uri, enable) { var domain = parseUri(uri); domain = domain["host"]; var key = "disable-" + domain; if (enable) { debug("Enable gpass for " + domain); delete_preference(key); } else { debug("Disable gpass for " + domain); set_preference(key, true, null); } } function _query_tabs_is_gpass_enabled(tabs, sendResponse) { if (tabs.length) { is_gpass_enabled(tabs[0].url).then( function (key_present) { var enabled = (key_present == null); update_gpass_icon((enabled)?GPASS_ICON.NORMAL:GPASS_ICON.DISABLED, tabs[0].id); sendResponse({"enabled":enabled}); } ); } else { debug("No cur tab"); sendResponse({"enabled":true}); } return true; } function _query_tabs_update_icon(tabs, iconId) { if (tabs.length) { update_gpass_icon(iconId, tabs[0].id); } } function update_enable(enabled, tab, saveConfig) { var parameters; if (enabled) { parameters = {type:"blockForms"}; saveConfig = true;// Force save when enable website debug("Now enabled"); } else { parameters = {type:"unblockForms"}; debug("Now disabled"); } if (saveConfig) save_gpass_enable_config(tab.url, enabled); update_gpass_icon((enabled)?GPASS_ICON.NORMAL:GPASS_ICON.DISABLED, tab.id); browser.tabs.sendMessage(tab.id, parameters); } function gpass_switch_enable(tab) { debug("Switch enable"); is_gpass_enabled(tab.url).then( function (key_present) { var enabled = (key_present == null); // Do switch enabled = !enabled; update_enable(enabled, tab, true); }); } function createMenus(browser) { var title; debug("Create menus"); /* Not supported by Chrome */ if (browser.menus.onShown) title = 'Disable gPass for this website'; else title = 'Disable or enable gPass for this website'; /* Enable/disable */ browser.menus.create({ id: 'switch_enable', title: title, contexts: ['action'], }, () => {console.log(chrome.runtime.lastError);}); /* Not supported by Chrome */ if (browser.menus.onShown) title = 'Disable gPass for ALL websites'; else title = 'Disable or enable gPass for ALL websites'; /* Always enable/disable */ browser.menus.create({ id: 'always_disable', title: title, contexts: ['action'] }); } function extension_load() { browser.runtime.onMessage.addListener( function(request, sender, sendResponse) { if (request.type == "password") { var domain = parseUri(request.domain); domain = domain["host"]; var wdomain = wildcard_domain(domain); ask_server(request.logins, domain, wdomain, request.mkey, sendResponse, request.options); return true; } else if (request.type == "notification") { _notification(request.options.message, request.options.data); } else if (request.type == "getServerAddress") { get_preference("account_url").then( function (address) { sendResponse({"value" : address}); }); return true; } else if (request.type == "getPopupClipboard") { get_preference("popup_clipboard").then( function (value) { sendResponse({"value" : value}); }); return true; } else if (request.type == "is_gpass_enabled") { browser.tabs.query({active:true, currentWindow:true}).then( (tabs) => { _query_tabs_is_gpass_enabled(tabs, sendResponse); }); return true; } else if (request.type == "update_icon") { debug("update_icon"); browser.tabs.query({active:true, currentWindow:true}).then( (tabs) => { _query_tabs_update_icon(tabs, request.icon_id); }); } else { debug("Unknown message " + request.type); } } ); /* Chrome */ if (!browser.menus && browser.contextMenus) { browser.menus = browser.contextMenus; } browser.runtime.onInstalled.addListener(() => { createMenus(browser) }); browser.menus.onClicked.addListener( function(info, tab) { switch (info.menuItemId) { case 'always_disable': get_preference('always_disabled').then( function (always_disabled) { debug('Change always disable'); debug(always_disabled); always_disabled = !always_disabled; set_preference('always_disabled', always_disabled, function(error) { browser.tabs.query({active:true, currentWindow:true}, (tabs) => { for (var i=0; i