Use native crypto functions from misc.js
This commit is contained in:
parent
241867e93c
commit
6fa296ebc4
|
@ -122,8 +122,7 @@ function a2hex(str) {
|
|||
function derive_mkey(user, mkey)
|
||||
{
|
||||
url = url_domain(document.URL) + "/" + user;
|
||||
mkey = a2hex(pkdbf2(mkey, url, pkdbf2_level, 256/8));
|
||||
return mkey;
|
||||
return pbkdf2(mkey, url, pbkdf2_level, 256/8);
|
||||
}
|
||||
|
||||
var passwords;
|
||||
|
@ -138,7 +137,7 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
this.clear_url = "";
|
||||
this.clear_login = "";
|
||||
this.clear_password = "";
|
||||
this.masterkey = "";
|
||||
this.masterkey = null;
|
||||
this.salt = salt;
|
||||
this.shadow_login = shadow_login;
|
||||
this.access_token = "";
|
||||
|
@ -149,16 +148,21 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
this.clear_url = "";
|
||||
this.clear_login = "";
|
||||
this.clear_password = "";
|
||||
this.masterkey = "";
|
||||
this.masterkey = null;
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
this.encrypt = function(masterkey)
|
||||
this.reset_master_key = function()
|
||||
{
|
||||
this.masterkey = null;
|
||||
}
|
||||
|
||||
this.encrypt = async function(masterkey)
|
||||
{
|
||||
if (masterkey == this.masterkey)
|
||||
return true;
|
||||
|
||||
if (masterkey == "" || this.clear_url == "" || this.clear_login == "")
|
||||
if (masterkey == null || this.clear_url == "" || this.clear_login == "")
|
||||
return false;
|
||||
|
||||
ciphered_login = "@@" + this.clear_url + ";" + this.clear_login;
|
||||
|
@ -166,11 +170,8 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
// Add salt
|
||||
ciphered_password = this.clear_password + generate_random(3, false);
|
||||
|
||||
aes = new AES();
|
||||
a_masterkey = aes.init(hex2a(masterkey));
|
||||
this.ciphered_login = a2hex(aes.encryptLongString(ciphered_login, a_masterkey));
|
||||
this.ciphered_password = a2hex(aes.encryptLongString(ciphered_password, a_masterkey));
|
||||
aes.finish();
|
||||
this.ciphered_login = a2hex(await encrypt(masterkey, ciphered_login));
|
||||
this.ciphered_password = a2hex(await encrypt(masterkey, ciphered_password));
|
||||
|
||||
this.unciphered = true;
|
||||
this.masterkey = masterkey;
|
||||
|
@ -179,21 +180,18 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
this.generate_access_token(masterkey);
|
||||
}
|
||||
|
||||
this.decrypt = function(masterkey)
|
||||
this.decrypt = async function(masterkey)
|
||||
{
|
||||
if (masterkey == this.masterkey && this.unciphered == true)
|
||||
return true;
|
||||
|
||||
if (masterkey == "" || this.unciphered == true)
|
||||
if (masterkey == null)
|
||||
return false;
|
||||
|
||||
aes = new AES();
|
||||
a_masterkey = aes.init(hex2a(masterkey));
|
||||
login = aes.decryptLongString(hex2a(this.ciphered_login), a_masterkey);
|
||||
if (masterkey == this.masterkey)
|
||||
return (this.unciphered == true);
|
||||
|
||||
login = await decrypt(masterkey, hex2a(this.ciphered_login));
|
||||
login = login.replace(/\0*$/, "");
|
||||
if (login.indexOf("@@") != 0)
|
||||
{
|
||||
aes.finish();
|
||||
return false;
|
||||
}
|
||||
// Remove @@
|
||||
|
@ -201,10 +199,9 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
infos = login.split(";");
|
||||
this.clear_url = infos[0];
|
||||
this.clear_login = infos[1];
|
||||
this.clear_password = aes.decryptLongString(hex2a(this.ciphered_password), a_masterkey);
|
||||
this.clear_password = await decrypt(masterkey, hex2a(this.ciphered_password));
|
||||
this.unciphered = true;
|
||||
this.masterkey = masterkey;
|
||||
aes.finish();
|
||||
|
||||
// Remove salt
|
||||
this.clear_password = this.clear_password.replace(/\0*$/, "");
|
||||
|
@ -215,7 +212,7 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
|
||||
this.isUnciphered = function(masterkey)
|
||||
{
|
||||
return (this.unciphered == true && masterkey == this.masterkey && masterkey != "")
|
||||
return (this.unciphered == true && masterkey == this.masterkey && masterkey != null)
|
||||
}
|
||||
|
||||
this.isCiphered = function(masterkey)
|
||||
|
@ -223,28 +220,24 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
return !(this.isUnciphered(masterkey));
|
||||
}
|
||||
|
||||
this.shadow_login_to_access_token = function(masterkey)
|
||||
this.shadow_login_to_access_token = async function(masterkey)
|
||||
{
|
||||
var aes = new AES();
|
||||
var key = pkdbf2(hex2a(masterkey), hex2a(this.salt), pkdbf2_level, 256/8);
|
||||
var a_key = aes.init(hex2a(key));
|
||||
this.access_token = aes.encryptLongString(hex2a(this.shadow_login), a_key);
|
||||
this.access_token = await encrypt(masterkey, hex2a(this.shadow_login));
|
||||
this.access_token = a2hex(this.access_token);
|
||||
aes.finish();
|
||||
}
|
||||
|
||||
this.generate_access_token = function(masterkey)
|
||||
this.generate_access_token = async function(masterkey)
|
||||
{
|
||||
this.salt = a2hex(generate_random(16, false));
|
||||
this.shadow_login = a2hex(generate_random(16, false));
|
||||
|
||||
return this.shadow_login_to_access_token(masterkey);
|
||||
return await this.shadow_login_to_access_token(masterkey);
|
||||
}
|
||||
}
|
||||
|
||||
function clearMasterKey()
|
||||
{
|
||||
current_mkey = "";
|
||||
current_mkey = null;
|
||||
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
|
@ -328,17 +321,17 @@ function update_stats()
|
|||
}
|
||||
|
||||
// Remove all password without credentials
|
||||
function put_ciphered_credentials(passwords, masterkey)
|
||||
async function put_ciphered_credentials(passwords, masterkey)
|
||||
{
|
||||
for(var i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].generate_access_token(masterkey);
|
||||
await passwords[i].generate_access_token(masterkey);
|
||||
remove_password_server(current_user, passwords[i].ciphered_login, '');
|
||||
add_password_server(current_user, passwords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function get_ciphered_credentials(masterkey)
|
||||
async function get_ciphered_credentials(masterkey)
|
||||
{
|
||||
access_tokens = '';
|
||||
old_passwords = new Array();
|
||||
|
@ -353,13 +346,13 @@ function get_ciphered_credentials(masterkey)
|
|||
continue;
|
||||
}
|
||||
|
||||
passwords[i].shadow_login_to_access_token(masterkey);
|
||||
await passwords[i].shadow_login_to_access_token(masterkey);
|
||||
if (access_tokens.length) access_tokens += ",";
|
||||
access_tokens += passwords[i].access_token;
|
||||
}
|
||||
|
||||
if (old_passwords.length)
|
||||
put_ciphered_credentials(old_passwords, masterkey);
|
||||
await put_ciphered_credentials(old_passwords, masterkey);
|
||||
|
||||
if (!access_tokens.length)
|
||||
return;
|
||||
|
@ -385,16 +378,16 @@ function get_ciphered_credentials(masterkey)
|
|||
req.send("get_secure_passwords=1&user=" + user + "&access_tokens=" + access_tokens);
|
||||
}
|
||||
|
||||
function change_master_key(warning_unciphered)
|
||||
async function change_master_key(warning_unciphered)
|
||||
{
|
||||
var nb_unciphered = 0;
|
||||
|
||||
if (current_mkey.length && use_shadow_logins)
|
||||
get_ciphered_credentials(current_mkey);
|
||||
if (current_mkey && use_shadow_logins)
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].decrypt(current_mkey))
|
||||
if (await passwords[i].decrypt(current_mkey))
|
||||
nb_unciphered++;
|
||||
}
|
||||
|
||||
|
@ -543,11 +536,16 @@ function update_master_key(warning_unciphered)
|
|||
|
||||
if (current_mkey != "")
|
||||
{
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].reset_master_key();
|
||||
}
|
||||
current_mkey = derive_mkey(current_user, current_mkey);
|
||||
startClearTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
current_mkey = null;
|
||||
// Disable warning on empty master key (clear passwords from others)
|
||||
warning_unciphered = false;
|
||||
stopClearTimer();
|
||||
|
@ -584,7 +582,7 @@ function add_password_server(user, pentry)
|
|||
return ok;
|
||||
}
|
||||
|
||||
function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
||||
async function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
||||
{
|
||||
var ret = null;
|
||||
|
||||
|
@ -632,7 +630,7 @@ function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
|||
pentry.clear_url = url;
|
||||
pentry.clear_login = login;
|
||||
pentry.clear_password = password;
|
||||
pentry.encrypt(mkey);
|
||||
await pentry.encrypt(mkey);
|
||||
|
||||
return pentry;
|
||||
}
|
||||
|
@ -679,28 +677,33 @@ function add_password()
|
|||
mkey = inputs[i].value;
|
||||
}
|
||||
|
||||
pentry = construct_pentry(current_user, url, password, login, mkey, true)
|
||||
construct_pentry(current_user, url, password, login, mkey, true).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return false;
|
||||
|
||||
if (pentry == null) return;
|
||||
res = add_password_server(current_user, pentry);
|
||||
|
||||
res = add_password_server(current_user, pentry);
|
||||
if (!res) return false;
|
||||
|
||||
if (!res) return false;
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].reset_master_key();
|
||||
}
|
||||
|
||||
current_mkey = pentry.masterkey;
|
||||
|
||||
passwords.push(pentry);
|
||||
passwords.push(pentry);
|
||||
|
||||
change_master_key(false);
|
||||
current_mkey = pentry.masterkey;
|
||||
change_master_key(false);
|
||||
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("type") == "text" ||
|
||||
inputs[i].getAttribute("type") == "password")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("type") == "text" ||
|
||||
inputs[i].getAttribute("type") == "password")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
|
||||
startClearTimer();
|
||||
startClearTimer();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -816,23 +819,24 @@ function update_entry(entry_number)
|
|||
if(!confirm("Are you sure want to update this entry ?"))
|
||||
return;
|
||||
|
||||
pentry = construct_pentry(current_user, url, password, login, current_mkey, false);
|
||||
construct_pentry(current_user, url, password, login, current_mkey, false).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return;
|
||||
|
||||
if (pentry == null) return;
|
||||
ok = remove_password_server(current_user, passwords[found].ciphered_login, passwords[found].access_token);
|
||||
if (!ok) return;
|
||||
|
||||
ok = remove_password_server(current_user, passwords[found].ciphered_login, passwords[found].access_token);
|
||||
if (!ok) return;
|
||||
ok = add_password_server(current_user, pentry);
|
||||
if (!ok) return;
|
||||
|
||||
ok = add_password_server(current_user, pentry);
|
||||
if (!ok) return;
|
||||
passwords[found] = pentry;
|
||||
ciphered_login.setAttribute("login", pentry.ciphered_login);
|
||||
|
||||
passwords[found] = pentry;
|
||||
ciphered_login.setAttribute("login", pentry.ciphered_login);
|
||||
|
||||
alert("Entry updated");
|
||||
alert("Entry updated");
|
||||
});
|
||||
}
|
||||
|
||||
function update_masterkey()
|
||||
async function update_masterkey()
|
||||
{
|
||||
var url = "";
|
||||
var login = "";
|
||||
|
@ -858,7 +862,7 @@ function update_masterkey()
|
|||
var found = 0;
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].decrypt(oldmkey))
|
||||
if (await passwords[i].decrypt(oldmkey))
|
||||
{
|
||||
ok = remove_password_server(current_user, passwords[i].ciphered_login, passwords[i].access_token);
|
||||
if (!ok)
|
||||
|
@ -867,7 +871,7 @@ function update_masterkey()
|
|||
break;
|
||||
}
|
||||
|
||||
passwords[i].encrypt(current_mkey);
|
||||
await passwords[i].encrypt(current_mkey);
|
||||
ok = add_password_server(current_user, passwords[i]);
|
||||
|
||||
if (!ok)
|
||||
|
|
162
server/resources/misc.js
Normal file
162
server/resources/misc.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var default_preferences = {"pbkdf2_level": 1000,
|
||||
"account_url": "https://gpass-demo.soutade.fr/demo"};
|
||||
|
||||
var browser = browser || chrome;
|
||||
var crypto = crypto || window.crypto;
|
||||
|
||||
// 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 pbkdf2(mkey, salt, level)
|
||||
{
|
||||
AESCBC = {
|
||||
name: "AES-CBC",
|
||||
length: 256,
|
||||
}
|
||||
|
||||
var key = str2ab(mkey);
|
||||
return crypto.subtle.importKey("raw", key, {name: "PBKDF2"}, false, ["deriveBits", "deriveKey"])
|
||||
.then(function(key){
|
||||
//sha-256
|
||||
return 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";
|
||||
|
||||
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 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));
|
||||
|
||||
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 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user