gPass/firefox_webextension/compat.js

221 lines
5.3 KiB
JavaScript

/*
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;
// }