/* Copyright (C) 2013-2020 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 . */ var browser = browser || chrome; var protocol_version = 4; var account_url = null; function _notification(message, data) { if (message !== data) message += data; options = { type: "basic", title : "gPass", message : message, iconUrl:browser.extension.getURL("icons/gpass_icon_64.png") }; browser.notifications.create("gPass", options, function(){}); window.setTimeout(function() {browser.notifications.clear("gPass", function(){})}, 2000); } async function generate_request(domain, login, mkey, iv) { var v = domain + ";" + login; debug("will encrypt " + v); while ((v.length % 16)) v += "\0"; hash = await digest(v); v += hash.slice(8, 24); enc = encrypt_cbc(mkey, iv, v); return enc; } async function ask_server(logins, domain, wdomain, mkey, sendResponse, options) { account_url = await get_preference("account_url"); var salt = parseURI.parseUri(account_url); salt = salt["host"] + salt["path"]; debug("salt " + salt); pbkdf2_level = await get_preference("pbkdf2_level"); global_iv = await simple_pbkdf2(salt, mkey, pbkdf2_level); global_iv = global_iv.slice(0, 16); mkey = crypto_pbkdf2(mkey, salt, pbkdf2_level); debug("global_iv " + a2hex(global_iv)); keys = ""; for(key_index=0, 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", this.responseText); ret = SERVER.FAILED; break; } } if (ret != SERVER.OK) { sendResponse({"value": ret, options:options}); return; } if (ciphered_password != "") { debug("Ciphered password : " + ciphered_password); 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); } else { debug("No password found"); ret = SERVER.FAILED; _notification("No password found in database", "") } sendResponse({"value": ret, "password":clear_password, "options":options}); }, false); gPassRequest.addEventListener("error", function(evt) { debug("error"); ret = false; _notification("Error"); }, false); debug("connect to " + account_url); gPassRequest.open("POST", account_url, true); gPassRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); gPassRequest.send(keys); return true; } function update_gpass_icon(iconId, tabId) { debug("update_gpass_icon"); icon_infos = {"tabId":tabId}; 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); 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", }; browser.browserAction.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.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.parseUri(uri); domain = domain["host"]; 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) { 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) { 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) { enabled = (key_present == null); // Do switch enabled = !enabled; update_enable(enabled, tab, true); }); } function extension_load() { browser.runtime.onMessage.addListener( function(request, sender, sendResponse) { if (request.type == "password") { var domain = parseURI.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 == "is_gpass_enabled") { browser.tabs.query({active:true, currentWindow:true}, function cb(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}, function cb(tabs) { _query_tabs_update_icon(tabs, request.icon_id); }); } else { debug("Unknown message " + request.type); } } ); if (!browser.menus && browser.contextMenus) browser.menus = browser.contextMenus; /* Settings */ browser.menus.create({ id: 'settings', title: 'gPass Settings', contexts: ['browser_action'] }); /* 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: ['browser_action'] }); /* 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: ['browser_action'] }); browser.menus.onClicked.addListener( function(info, tab) { switch (info.menuItemId) { case 'settings': browser.runtime.openOptionsPage(); break; 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({}, function cb(tabs) { for (i=0; i