/* Copyright (C) 2013-2017 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 DEBUG = false; var protocol_version = 4; var account_url = null; var crypto_v2_logins_size=0; SERVER = {OK : 0, FAILED : 1, RESTART_REQUEST : 2}; // http://stackoverflow.com/questions/3745666/how-to-convert-from-hex-to-ascii-in-javascript function hex2a(hex) { var str = ''; for (var i = 0; i < hex.length; i += 2) str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); return str; } function a2hex(_str_) { var hex = ''; for (var i = 0; i < _str_.length; i++) { var c = _str_.charCodeAt(i).toString(16); if (c.length == 1) c = "0" + c; hex += c; } return hex; } function debug(s) { if (DEBUG) console.log(s); } 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(form, field, logins, domain, wdomain, mkey, submit) { account_url = await getPref("account_url"); var salt = parseURI.parseUri(account_url); salt = salt["host"] + salt["path"]; debug("salt " + salt); pbkdf2_level = await getPref("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) { notify("Protocol version not supported, please upgrade your addon", "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; setPref("pbkdf2_level", pbkdf2_level); ret = SERVER.RESTART_REQUEST; } break; case "": break; default: debug("Unknown command " + params[0]); notify("Error : It seems that it's not a gPass server", this.responseText); ret = SERVER.FAILED; break; } } if (ret != SERVER.OK) { return; } if (ciphered_password != "") { debug("Ciphered password : " + ciphered_password); if (matched_key >= crypto_v2_logins_size || server_protocol_version < 4) // 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); field.value = clear_password; // Remove gPass event listener and submit again with clear password if (submit) { form.removeEventListener("submit", on_sumbit, true); // Propagate change change_cb = field.onchange; if (change_cb) change_cb(); // Try to type "enter" var evt = new KeyboardEvent("keydown"); delete evt.which; evt.which = 13; field.dispatchEvent(evt); // Submit form form.submit(); } else { notify("Password successfully replaced", "Password successfully replaced"); } } else { debug("No password found"); ret = SERVER.FAILED; notify("No password found in database", "No password found in database"); } }, false); gPassRequest.addEventListener("error", function(evt) { debug("error"); ret = false; notify("Error", "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 ret; } function wildcard_domain(domain) { var parts = domain.split("."); if (parts.length >= 3) { // Seems to be a two level root domain (ie zzz.xxx.co.uk ...) if (parts[parts.length-2].lenght <= 3) { if (parts.length > 3) return "*" + "." + parts[parts.length-3] + "." + parts[parts.length-2] + "." + parts[parts.length-1]; } // Standard root domain (zzz.xxx.com) else return "*" + "." + parts[parts.length-2] + "." + parts[parts.length-1]; } // Simple xxx.com else if (parts.length == 2) return "*" + "." + parts[0] + "." + parts[1]; return ""; } function _add_name(logins, name) { for(var i=0; i && type_filters.push("text"); type_filters.push("email"); logins = try_get_name(fields, type_filters, true); // Get all other fields except text, email and password if (!logins.length) { type_filters.push("password"); logins = try_get_name(fields, type_filters, false); } // Look for for (var i=0; i"); } document_loaded(document); 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();