Add Firefox WebExtension
This commit is contained in:
parent
e759c13d64
commit
c64f886188
17
firefox_webextension/background.js
Normal file
17
firefox_webextension/background.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
browser.runtime.onMessage.addListener(
|
||||||
|
function(request) {
|
||||||
|
|
||||||
|
if (request.type == "notification")
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
type: "basic",
|
||||||
|
title : "gPass",
|
||||||
|
message : request.options.message,
|
||||||
|
iconUrl:browser.extension.getURL("gpass_icon_64.png")
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.notifications.create("gPass", options);
|
||||||
|
|
||||||
|
window.setTimeout(function() {browser.notifications.clear("gPass")}, 2000);
|
||||||
|
}
|
||||||
|
});
|
220
firefox_webextension/compat.js
Normal file
220
firefox_webextension/compat.js
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013-2016 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var default_preferences = {"pbkdf2_level": 1000,
|
||||||
|
"account_url": "https://gpass-demo.soutade.fr/demo"};
|
||||||
|
|
||||||
|
function notify(text, data)
|
||||||
|
{
|
||||||
|
browser.runtime.sendMessage({type: "notification", options:{"message":text}});
|
||||||
|
}
|
||||||
|
// https://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers
|
||||||
|
function ab2str(buf) {
|
||||||
|
return String.fromCharCode.apply(null, new Uint8Array(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
|
||||||
|
function str2ab2(str) {
|
||||||
|
var chars = []
|
||||||
|
for (var i=0, strLen=str.length; i < strLen; i++) {
|
||||||
|
chars.push(str.charCodeAt(i));
|
||||||
|
}
|
||||||
|
return new Uint8Array(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
function str2ab(str) {
|
||||||
|
var buf = new ArrayBuffer(str.length); // 2 bytes for each char
|
||||||
|
// var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
|
||||||
|
var bufView = new Uint8Array(buf);
|
||||||
|
for (var i=0, strLen=str.length; i < strLen; i++) {
|
||||||
|
bufView[i] = str.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bufView;
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnResult(result) { return result;}
|
||||||
|
function onError(error) { console.error(error); }
|
||||||
|
|
||||||
|
function pbkdf2(mkey, salt, level)
|
||||||
|
{
|
||||||
|
debug("Process pbkdf2 with " + mkey);
|
||||||
|
|
||||||
|
AESCBC = {
|
||||||
|
name: "AES-CBC",
|
||||||
|
length: 256,
|
||||||
|
}
|
||||||
|
level=1000;
|
||||||
|
|
||||||
|
var key = str2ab(mkey);
|
||||||
|
return window.crypto.subtle.importKey("raw", key, {name: "PBKDF2"}, false, ["deriveBits", "deriveKey"])
|
||||||
|
.then(function(key){
|
||||||
|
//sha-256
|
||||||
|
return window.crypto.subtle.deriveKey({
|
||||||
|
name: "PBKDF2",
|
||||||
|
salt: str2ab(salt),
|
||||||
|
iterations: level,
|
||||||
|
hash: "SHA-256",
|
||||||
|
}, key, AESCBC, false, ["encrypt", "decrypt", "unwrapKey", "wrapKey"])
|
||||||
|
.then(function(key) {
|
||||||
|
return key;
|
||||||
|
})
|
||||||
|
.catch(function(err){
|
||||||
|
console.log("Error derive key " + err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
console.log("Error import key" + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _encrypt(mkey, data)
|
||||||
|
{
|
||||||
|
while ((data.length % 16))
|
||||||
|
data += "\0";
|
||||||
|
|
||||||
|
debug("Encrypt " + data);
|
||||||
|
|
||||||
|
data = str2ab(data);
|
||||||
|
|
||||||
|
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
promise = mkey.then(function(mkey){
|
||||||
|
return window.crypto.subtle.encrypt({
|
||||||
|
name: "AES-CBC",
|
||||||
|
iv: nulliv
|
||||||
|
}, mkey, data)})
|
||||||
|
.then(function(encrypted) {
|
||||||
|
return ab2str(encrypted);
|
||||||
|
})
|
||||||
|
.catch(function(encryption) {
|
||||||
|
console.log("Encryption rejected " + encryption);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _decrypt(mkey, data)
|
||||||
|
{
|
||||||
|
while ((data.length % 16))
|
||||||
|
data += "\0";
|
||||||
|
|
||||||
|
pkcs7_padding = new Uint8Array([16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]);
|
||||||
|
pkcs7_padding = await _encrypt(mkey, ab2str(pkcs7_padding));
|
||||||
|
|
||||||
|
debug("Decrypt " + data);
|
||||||
|
|
||||||
|
data = str2ab(data + pkcs7_padding);
|
||||||
|
|
||||||
|
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
promise = mkey.then(function(mkey){
|
||||||
|
return window.crypto.subtle.decrypt({
|
||||||
|
name: "AES-CBC",
|
||||||
|
iv: nulliv
|
||||||
|
}, mkey, data)})
|
||||||
|
.then(function(decrypted) {
|
||||||
|
return ab2str(decrypted);
|
||||||
|
})
|
||||||
|
.catch(function(decryption) {
|
||||||
|
console.log("Decryption rejected " + decryption);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function encrypt(mkey, data)
|
||||||
|
{
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
while (data.length > 16)
|
||||||
|
{
|
||||||
|
res = await _encrypt(mkey, data.slice(0, 16));
|
||||||
|
// Remove PKCS7 padding
|
||||||
|
result += res.slice(0, 16);
|
||||||
|
data = data.slice(16);
|
||||||
|
}
|
||||||
|
res = await _encrypt(mkey, data);
|
||||||
|
result += res.slice(0, 16);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decrypt(mkey, data)
|
||||||
|
{
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
while (data.length > 16)
|
||||||
|
{
|
||||||
|
res = await _decrypt(mkey, data.slice(0, 16));
|
||||||
|
// Remove PKCS7 padding
|
||||||
|
result += res.slice(0, 16);
|
||||||
|
data = data.slice(16);
|
||||||
|
}
|
||||||
|
res = await _decrypt(mkey, data);
|
||||||
|
result += res.slice(0, 16);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPref(key)
|
||||||
|
{
|
||||||
|
return browser.storage.local.get(key)
|
||||||
|
.then(
|
||||||
|
function (pref) {
|
||||||
|
if (!pref.hasOwnProperty(key))
|
||||||
|
return default_preferences[key];
|
||||||
|
return pref[key];
|
||||||
|
}
|
||||||
|
,
|
||||||
|
function (err) {
|
||||||
|
console.log("Error getting preference " + err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPref(key, value)
|
||||||
|
{
|
||||||
|
browser.storage.local.set({key:value});
|
||||||
|
}
|
||||||
|
|
||||||
|
// function getPref(key)
|
||||||
|
// {
|
||||||
|
// res = localStorage.getItem(key);
|
||||||
|
// if (res == null)
|
||||||
|
// return default_preferences[key];
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function setPref(key, value)
|
||||||
|
// {
|
||||||
|
// localStorage.setItem(key, value);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function load_preferences()
|
||||||
|
// {
|
||||||
|
// preferences = {};
|
||||||
|
|
||||||
|
// for (var i = 0; i < localStorage.length; i++)
|
||||||
|
// {
|
||||||
|
// k = localStorage.key(i);
|
||||||
|
// preferences[k] = getPref[k];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return preferences;
|
||||||
|
// }
|
1
firefox_webextension/gpass_icon_128.png
Symbolic link
1
firefox_webextension/gpass_icon_128.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../resources/gpass_icon_128.png
|
1
firefox_webextension/gpass_icon_16.png
Symbolic link
1
firefox_webextension/gpass_icon_16.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../resources/gpass_icon_16.png
|
1
firefox_webextension/gpass_icon_32.png
Symbolic link
1
firefox_webextension/gpass_icon_32.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../resources/gpass_icon_32.png
|
1
firefox_webextension/gpass_icon_64.png
Symbolic link
1
firefox_webextension/gpass_icon_64.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../resources/gpass_icon_64.png
|
1
firefox_webextension/lib
Symbolic link
1
firefox_webextension/lib
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../chrome_addon/lib/
|
35
firefox_webextension/manifest.json
Normal file
35
firefox_webextension/manifest.json
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
|
||||||
|
"name": "gPass",
|
||||||
|
"short_name": "gPass",
|
||||||
|
"version": "0.8",
|
||||||
|
"description": "gPass : global password manager",
|
||||||
|
"icons" : {"16":"gpass_icon_16.png", "32":"gpass_icon_32.png", "64":"gpass_icon_64.png", "128":"gpass_icon_128.png"},
|
||||||
|
"author" : "Grégory Soutadé",
|
||||||
|
"homepage_url" : "http://indefero.soutade.fr/p/gpass",
|
||||||
|
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["https://*/*", "http://*/*"],
|
||||||
|
"js": ["lib/parseuri.js", "compat.js", "lib/main.js"],
|
||||||
|
"run_at" : "document_idle",
|
||||||
|
"all_frames" : true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"persistent": false,
|
||||||
|
"scripts": ["background.js"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"options_ui": { "page":"options.html" },
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"https://*/",
|
||||||
|
"http://*/",
|
||||||
|
"notifications",
|
||||||
|
"storage",
|
||||||
|
"activeTab"
|
||||||
|
]
|
||||||
|
}
|
1
firefox_webextension/options.html
Symbolic link
1
firefox_webextension/options.html
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../chrome_addon/options.html
|
35
firefox_webextension/options.js
Normal file
35
firefox_webextension/options.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
var default_preferences = {"pbkdf2_level": 1000,
|
||||||
|
"account_url": "https://gpass-demo.soutade.fr/demo"};
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
var account_url = document.getElementById('account_url').value;
|
||||||
|
var pbkdf2 = document.getElementById('pbkdf2').value;
|
||||||
|
|
||||||
|
browser.storage.local.set({
|
||||||
|
"account_url":account_url,
|
||||||
|
"pbkdf2_level":pbkdf2
|
||||||
|
})
|
||||||
|
.then(function ok() { alert("Saved"); },
|
||||||
|
function err() { alert("Cannot save your preferences");}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreOptions()
|
||||||
|
{
|
||||||
|
document.getElementById('account_url').value = default_preferences['account_url'];
|
||||||
|
document.getElementById('pbkdf2').value = default_preferences['pbkdf2_level'];
|
||||||
|
|
||||||
|
browser.storage.local.get().then(
|
||||||
|
function(prefs)
|
||||||
|
{
|
||||||
|
if (prefs.hasOwnProperty("account_url"))
|
||||||
|
document.getElementById('account_url').value = prefs["account_url"];
|
||||||
|
|
||||||
|
if (prefs.hasOwnProperty("pbkdf2_level"))
|
||||||
|
document.getElementById('pbkdf2').value = prefs["pbkdf2_level"];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('save').addEventListener("click", save);
|
||||||
|
document.addEventListener("DOMContentLoaded", restoreOptions);
|
Loading…
Reference in New Issue
Block a user