/* 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; var crypto_v2_logins_size = 0; 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, old) { if (old) { var v = "@@" + domain + ";" + login; debug("will encrypt " + v); enc = encrypt_ecb(mkey, v); } else { 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); 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); if (matched_key >= crypto_v2_logins_size) // Crypto v1 { clear_password = await decrypt_ecb(mkey, hex2a(ciphered_password)); // Remove trailing \0 and salt clear_password = clear_password.replace(/\0*$/, ""); clear_password = clear_password.substr(0, clear_password.length-3); } else { 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 url_block_callback(details) { // debug(JSON.stringify(details)); if (details.requestBody) { if (details.requestBody.formData) { for (var key in details.requestBody.formData) { for(var idx in details.requestBody.formData[key]) { value = details.requestBody.formData[key][idx]; if (value.startsWith("@@") || value.startsWith("@_")) return {cancel: true}; } } } /* // Analyse POST parameters if (details.method == "POST" && details.requestBody.raw) { alert(details.requestBody.raw); var postedString = decodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(details.requestBody.raw[0].bytes))); if (postedString.indexOf("=@@") != -1 || postedString.indexOf("=@_") != -1) return {cancel: true}; } */ } return {cancel: false}; } function url_unblock_callback(details) { return {cancel: false}; } 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: } 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); } function is_gpass_enabled(uri) { 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); } } function _block_url(tabs, callback) { options = { urls:[tabs[0].url], "types":["main_frame"] }; if (tabs.length) { options["tabId"] = tabs[0].id; options["windowId"] = tabs[0].windowId; } browser.webRequest.onBeforeRequest.addListener( url_block_callback, options, ["blocking", "requestBody"]); return true; } function _query_tabs_block_url(tabs) { return _block_url(tabs, url_block_callback); } function _query_tabs_unblock_url(tabs) { return _block_url(tabs, url_unblock_callback); } 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.ENABLED: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 gpass_switch_enable(tab) { is_gpass_enabled(tab.url).then( function (key_present) { enabled = (key_present == null); // Do switch enabled = !enabled; if (enabled) { parameters = {type:"blockForms"}; debug("Now enabled"); } else { parameters = {type:"unblockForms"}; debug("Now disabled"); } save_gpass_enable_config(tab.url, enabled); update_gpass_icon((enabled)?GPASS_ICON.ENABLED:GPASS_ICON.DISABLED, tab.id); browser.tabs.sendMessage(tab.id, parameters); }); } 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 == "block_url") { browser.tabs.query({active:true, currentWindow:true}, _query_tabs_block_url); } else if (request.type == "unblock_url") { browser.tabs.query({active:true, currentWindow:true}, _query_tabs_unblock_url); } 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 == "switch_enable") { debug("Switch enable"); browser.tabs.query({active:true, currentWindow:true}, function cb(tabs) { _query_tabs_switch_enable(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; 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'; browser.menus.create({ id: 'switch_enable', title: title, contexts: ['browser_action'] }); browser.menus.onClicked.addListener( function(info, tab) { switch (info.menuItemId) { case 'settings': browser.runtime.openOptionsPage(); break; case 'switch_enable': gpass_switch_enable(tab); break; } } ); if (browser.menus.onShown) { browser.menus.onShown.addListener( function(info, tab) { is_gpass_enabled(tab.url).then( function (key_present) { enabled = (key_present == null); if (enabled) title = 'disable gPass for this website'; else title = 'enable gPass for this website'; browser.menus.update("switch_enable", { "title":title } ); browser.menus.refresh(); } ); } ); } } async function self_test() { mkey = crypto_pbkdf2("password", "salt", 4096); res = await encrypt_ecb(mkey, "DDDDDDDDDDDDDDDD"); reference = new Uint8Array([0xc4, 0x76, 0x01, 0x07, 0xa1, 0xc0, 0x2f, 0x22, 0xee, 0xbe, 0x60, 0xff, 0x65, 0x33, 0x5b, 0x9e]); if (res != ab2str(reference)) { console.log("Self test ERROR !"); } else console.log("Self test OK !"); } //self_test(); extension_load();