Add Chrome addon
This commit is contained in:
parent
2757b81e64
commit
d395807c98
|
@ -0,0 +1,17 @@
|
||||||
|
chrome.runtime.onMessage.addListener(
|
||||||
|
function(request, sender, sendResponse) {
|
||||||
|
|
||||||
|
if (request.type == "notification")
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
type: "basic",
|
||||||
|
title : "gPass",
|
||||||
|
message : request.options.message,
|
||||||
|
iconUrl:chrome.extension.getURL("gpass_icon.png")
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.notifications.create("gPass", options, function(){});
|
||||||
|
|
||||||
|
window.setTimeout(function() {chrome.notifications.clear("gPass", function(){})}, 2000);
|
||||||
|
}
|
||||||
|
});
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function hmac256(key, message) {
|
||||||
|
var ipad = "";
|
||||||
|
var opad = "";
|
||||||
|
|
||||||
|
if (key.length > 512/8)
|
||||||
|
{
|
||||||
|
key = digest256(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<512/8; i++)
|
||||||
|
{
|
||||||
|
if (i >= key.length)
|
||||||
|
{
|
||||||
|
ipad += String.fromCharCode(0x36);
|
||||||
|
opad += String.fromCharCode(0x5c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ipad += String.fromCharCode(key.charCodeAt(i) ^ 0x36);
|
||||||
|
opad += String.fromCharCode(key.charCodeAt(i) ^ 0x5c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = digest256(opad + digest256(ipad + message));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,291 @@
|
||||||
|
/*
|
||||||
|
* jsaes version 0.1 - Copyright 2006 B. Poettering
|
||||||
|
*
|
||||||
|
* This program 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 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
|
* 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* http://point-at-infinity.org/jsaes/
|
||||||
|
*
|
||||||
|
* This is a javascript implementation of the AES block cipher. Key lengths
|
||||||
|
* of 128, 192 and 256 bits are supported.
|
||||||
|
*
|
||||||
|
* The well-functioning of the encryption/decryption routines has been
|
||||||
|
* verified for different key lengths with the test vectors given in
|
||||||
|
* FIPS-197, Appendix C.
|
||||||
|
*
|
||||||
|
* The following code example enciphers the plaintext block '00 11 22 .. EE FF'
|
||||||
|
* with the 256 bit key '00 01 02 .. 1E 1F'.
|
||||||
|
*
|
||||||
|
* AES_Init();
|
||||||
|
*
|
||||||
|
* var block = new Array(16);
|
||||||
|
* for(var i = 0; i < 16; i++)
|
||||||
|
* block[i] = 0x11 * i;
|
||||||
|
*
|
||||||
|
* var key = new Array(32);
|
||||||
|
* for(var i = 0; i < 32; i++)
|
||||||
|
* key[i] = i;
|
||||||
|
*
|
||||||
|
* AES_ExpandKey(key);
|
||||||
|
* AES_Encrypt(block, key);
|
||||||
|
*
|
||||||
|
* AES_Done();
|
||||||
|
*
|
||||||
|
* Report bugs to: jsaes AT point-at-infinity.org
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
AES_Init: initialize the tables needed at runtime. Call this function
|
||||||
|
before the (first) key expansion.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function AES_Init() {
|
||||||
|
AES_Sbox_Inv = new Array(256);
|
||||||
|
for(var i = 0; i < 256; i++)
|
||||||
|
AES_Sbox_Inv[AES_Sbox[i]] = i;
|
||||||
|
|
||||||
|
AES_ShiftRowTab_Inv = new Array(16);
|
||||||
|
for(var i = 0; i < 16; i++)
|
||||||
|
AES_ShiftRowTab_Inv[AES_ShiftRowTab[i]] = i;
|
||||||
|
|
||||||
|
AES_xtime = new Array(256);
|
||||||
|
for(var i = 0; i < 128; i++) {
|
||||||
|
AES_xtime[i] = i << 1;
|
||||||
|
AES_xtime[128 + i] = (i << 1) ^ 0x1b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AES_Done: release memory reserved by AES_Init. Call this function after
|
||||||
|
the last encryption/decryption operation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function AES_Done() {
|
||||||
|
delete AES_Sbox_Inv;
|
||||||
|
delete AES_ShiftRowTab_Inv;
|
||||||
|
delete AES_xtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AES_ExpandKey: expand a cipher key. Depending on the desired encryption
|
||||||
|
strength of 128, 192 or 256 bits 'key' has to be a byte array of length
|
||||||
|
16, 24 or 32, respectively. The key expansion is done "in place", meaning
|
||||||
|
that the array 'key' is modified.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function AES_ExpandKey(key) {
|
||||||
|
var kl = key.length, ks, Rcon = 1;
|
||||||
|
switch (kl) {
|
||||||
|
case 16: ks = 16 * (10 + 1); break;
|
||||||
|
case 24: ks = 16 * (12 + 1); break;
|
||||||
|
case 32: ks = 16 * (14 + 1); break;
|
||||||
|
default:
|
||||||
|
alert("AES_ExpandKey: Only key lengths of 16, 24 or 32 bytes allowed!");
|
||||||
|
}
|
||||||
|
for(var i = kl; i < ks; i += 4) {
|
||||||
|
var temp = key.slice(i - 4, i);
|
||||||
|
if (i % kl == 0) {
|
||||||
|
temp = new Array(AES_Sbox[temp[1]] ^ Rcon, AES_Sbox[temp[2]],
|
||||||
|
AES_Sbox[temp[3]], AES_Sbox[temp[0]]);
|
||||||
|
if ((Rcon <<= 1) >= 256)
|
||||||
|
Rcon ^= 0x11b;
|
||||||
|
}
|
||||||
|
else if ((kl > 24) && (i % kl == 16))
|
||||||
|
temp = new Array(AES_Sbox[temp[0]], AES_Sbox[temp[1]],
|
||||||
|
AES_Sbox[temp[2]], AES_Sbox[temp[3]]);
|
||||||
|
for(var j = 0; j < 4; j++)
|
||||||
|
key[i + j] = key[i + j - kl] ^ temp[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AES_Encrypt: encrypt the 16 byte array 'block' with the previously
|
||||||
|
expanded key 'key'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function AES_Encrypt(block, key) {
|
||||||
|
var l = key.length;
|
||||||
|
AES_AddRoundKey(block, key.slice(0, 16));
|
||||||
|
for(var i = 16; i < l - 16; i += 16) {
|
||||||
|
AES_SubBytes(block, AES_Sbox);
|
||||||
|
AES_ShiftRows(block, AES_ShiftRowTab);
|
||||||
|
AES_MixColumns(block);
|
||||||
|
AES_AddRoundKey(block, key.slice(i, i + 16));
|
||||||
|
}
|
||||||
|
AES_SubBytes(block, AES_Sbox);
|
||||||
|
AES_ShiftRows(block, AES_ShiftRowTab);
|
||||||
|
AES_AddRoundKey(block, key.slice(i, l));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AES_Decrypt: decrypt the 16 byte array 'block' with the previously
|
||||||
|
expanded key 'key'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function AES_Decrypt(block, key) {
|
||||||
|
var l = key.length;
|
||||||
|
AES_AddRoundKey(block, key.slice(l - 16, l));
|
||||||
|
AES_ShiftRows(block, AES_ShiftRowTab_Inv);
|
||||||
|
AES_SubBytes(block, AES_Sbox_Inv);
|
||||||
|
for(var i = l - 32; i >= 16; i -= 16) {
|
||||||
|
AES_AddRoundKey(block, key.slice(i, i + 16));
|
||||||
|
AES_MixColumns_Inv(block);
|
||||||
|
AES_ShiftRows(block, AES_ShiftRowTab_Inv);
|
||||||
|
AES_SubBytes(block, AES_Sbox_Inv);
|
||||||
|
}
|
||||||
|
AES_AddRoundKey(block, key.slice(0, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/* The following lookup tables and functions are for internal use only! */
|
||||||
|
|
||||||
|
AES_Sbox = new Array(99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,
|
||||||
|
118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,
|
||||||
|
147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,
|
||||||
|
7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,
|
||||||
|
47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,
|
||||||
|
251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,
|
||||||
|
188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,
|
||||||
|
100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,
|
||||||
|
50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,
|
||||||
|
78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,
|
||||||
|
116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,
|
||||||
|
158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,
|
||||||
|
137,13,191,230,66,104,65,153,45,15,176,84,187,22);
|
||||||
|
|
||||||
|
AES_ShiftRowTab = new Array(0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11);
|
||||||
|
|
||||||
|
function AES_SubBytes(state, sbox) {
|
||||||
|
for(var i = 0; i < 16; i++)
|
||||||
|
state[i] = sbox[state[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function AES_AddRoundKey(state, rkey) {
|
||||||
|
for(var i = 0; i < 16; i++)
|
||||||
|
state[i] ^= rkey[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
function AES_ShiftRows(state, shifttab) {
|
||||||
|
var h = new Array().concat(state);
|
||||||
|
for(var i = 0; i < 16; i++)
|
||||||
|
state[i] = h[shifttab[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function AES_MixColumns(state) {
|
||||||
|
for(var i = 0; i < 16; i += 4) {
|
||||||
|
var s0 = state[i + 0], s1 = state[i + 1];
|
||||||
|
var s2 = state[i + 2], s3 = state[i + 3];
|
||||||
|
var h = s0 ^ s1 ^ s2 ^ s3;
|
||||||
|
state[i + 0] ^= h ^ AES_xtime[s0 ^ s1];
|
||||||
|
state[i + 1] ^= h ^ AES_xtime[s1 ^ s2];
|
||||||
|
state[i + 2] ^= h ^ AES_xtime[s2 ^ s3];
|
||||||
|
state[i + 3] ^= h ^ AES_xtime[s3 ^ s0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AES_MixColumns_Inv(state) {
|
||||||
|
for(var i = 0; i < 16; i += 4) {
|
||||||
|
var s0 = state[i + 0], s1 = state[i + 1];
|
||||||
|
var s2 = state[i + 2], s3 = state[i + 3];
|
||||||
|
var h = s0 ^ s1 ^ s2 ^ s3;
|
||||||
|
var xh = AES_xtime[h];
|
||||||
|
var h1 = AES_xtime[AES_xtime[xh ^ s0 ^ s2]] ^ h;
|
||||||
|
var h2 = AES_xtime[AES_xtime[xh ^ s1 ^ s3]] ^ h;
|
||||||
|
state[i + 0] ^= h1 ^ AES_xtime[s0 ^ s1];
|
||||||
|
state[i + 1] ^= h2 ^ AES_xtime[s1 ^ s2];
|
||||||
|
state[i + 2] ^= h1 ^ AES_xtime[s2 ^ s3];
|
||||||
|
state[i + 3] ^= h2 ^ AES_xtime[s3 ^ s0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bin2String (array) {
|
||||||
|
var result = "";
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
result += String.fromCharCode(parseInt(array[i], 2));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function string2Bin (str) {
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
result.push(str.charCodeAt(i));
|
||||||
|
}
|
||||||
|
while ((result.length % 16))
|
||||||
|
result.push(0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bin2String (array) {
|
||||||
|
return String.fromCharCode.apply(String, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://osama-oransa.blogspot.fr/2012/03/using-aes-encrypting-in-java-script.html
|
||||||
|
aes = {
|
||||||
|
init : function (myKey){
|
||||||
|
AES_Init();
|
||||||
|
var key = string2Bin(myKey);
|
||||||
|
AES_ExpandKey(key);
|
||||||
|
return key;
|
||||||
|
},
|
||||||
|
|
||||||
|
encrypt : function ( inputStr,key ) {
|
||||||
|
var block = string2Bin(inputStr);
|
||||||
|
AES_Encrypt(block, key);
|
||||||
|
var data=bin2String(block);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
decrypt : function ( inputStr,key ) {
|
||||||
|
block = string2Bin(inputStr);
|
||||||
|
AES_Decrypt(block, key);
|
||||||
|
var data=bin2String(block);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
encryptLongString : function( myString,key ) {
|
||||||
|
if(myString.length>16){
|
||||||
|
var data='';
|
||||||
|
for(var i=0;i<myString.length;i=i+16){
|
||||||
|
data+=this.encrypt(myString.substr(i,16),key);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}else{
|
||||||
|
return this.encrypt(myString,key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
decryptLongString : function ( myString,key ) {
|
||||||
|
if(myString.length>16){
|
||||||
|
var data='';
|
||||||
|
for(var i=0;i<myString.length;i=i+16){
|
||||||
|
data+=this.decrypt(myString.substr(i,16),key);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}else{
|
||||||
|
return this.decrypt(myString,key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
finish : function(){
|
||||||
|
AES_Done();
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
* A JavaScript implementation of the SHA256 hash function.
|
||||||
|
*
|
||||||
|
* FILE: sha256.js
|
||||||
|
* VERSION: 0.8
|
||||||
|
* AUTHOR: Christoph Bichlmeier <informatik@zombiearena.de>
|
||||||
|
*
|
||||||
|
* NOTE: This version is not tested thoroughly!
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003, Christoph Bichlmeier
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* ======================================================================
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* SHA256 logical functions */
|
||||||
|
function rotateRight(n,x) {
|
||||||
|
return ((x >>> n) | (x << (32 - n)));
|
||||||
|
}
|
||||||
|
function choice(x,y,z) {
|
||||||
|
return ((x & y) ^ (~x & z));
|
||||||
|
}
|
||||||
|
function majority(x,y,z) {
|
||||||
|
return ((x & y) ^ (x & z) ^ (y & z));
|
||||||
|
}
|
||||||
|
function sha256_Sigma0(x) {
|
||||||
|
return (rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x));
|
||||||
|
}
|
||||||
|
function sha256_Sigma1(x) {
|
||||||
|
return (rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x));
|
||||||
|
}
|
||||||
|
function sha256_sigma0(x) {
|
||||||
|
return (rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3));
|
||||||
|
}
|
||||||
|
function sha256_sigma1(x) {
|
||||||
|
return (rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10));
|
||||||
|
}
|
||||||
|
function sha256_expand(W, j) {
|
||||||
|
return (W[j&0x0f] += sha256_sigma1(W[(j+14)&0x0f]) + W[(j+9)&0x0f] +
|
||||||
|
sha256_sigma0(W[(j+1)&0x0f]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash constant words K: */
|
||||||
|
var K256 = new Array(
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||||
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||||
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||||
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||||
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||||
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||||
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||||
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||||
|
);
|
||||||
|
|
||||||
|
/* global arrays */
|
||||||
|
var ihash, count, buffer;
|
||||||
|
var sha256_hex_digits = "0123456789abcdef";
|
||||||
|
|
||||||
|
/* Add 32-bit integers with 16-bit operations (bug in some JS-interpreters:
|
||||||
|
overflow) */
|
||||||
|
function safe_add(x, y)
|
||||||
|
{
|
||||||
|
var lsw = (x & 0xffff) + (y & 0xffff);
|
||||||
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||||
|
return (msw << 16) | (lsw & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise the SHA256 computation */
|
||||||
|
function sha256_init() {
|
||||||
|
ihash = new Array(8);
|
||||||
|
count = new Array(2);
|
||||||
|
buffer = new Array(64);
|
||||||
|
count[0] = count[1] = 0;
|
||||||
|
ihash[0] = 0x6a09e667;
|
||||||
|
ihash[1] = 0xbb67ae85;
|
||||||
|
ihash[2] = 0x3c6ef372;
|
||||||
|
ihash[3] = 0xa54ff53a;
|
||||||
|
ihash[4] = 0x510e527f;
|
||||||
|
ihash[5] = 0x9b05688c;
|
||||||
|
ihash[6] = 0x1f83d9ab;
|
||||||
|
ihash[7] = 0x5be0cd19;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transform a 512-bit message block */
|
||||||
|
function sha256_transform() {
|
||||||
|
var a, b, c, d, e, f, g, h, T1, T2;
|
||||||
|
var W = new Array(16);
|
||||||
|
|
||||||
|
/* Initialize registers with the previous intermediate value */
|
||||||
|
a = ihash[0];
|
||||||
|
b = ihash[1];
|
||||||
|
c = ihash[2];
|
||||||
|
d = ihash[3];
|
||||||
|
e = ihash[4];
|
||||||
|
f = ihash[5];
|
||||||
|
g = ihash[6];
|
||||||
|
h = ihash[7];
|
||||||
|
|
||||||
|
/* make 32-bit words */
|
||||||
|
for(var i=0; i<16; i++)
|
||||||
|
W[i] = ((buffer[(i<<2)+3]) | (buffer[(i<<2)+2] << 8) | (buffer[(i<<2)+1]
|
||||||
|
<< 16) | (buffer[i<<2] << 24));
|
||||||
|
|
||||||
|
for(var j=0; j<64; j++) {
|
||||||
|
T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j];
|
||||||
|
if(j < 16) T1 += W[j];
|
||||||
|
else T1 += sha256_expand(W, j);
|
||||||
|
T2 = sha256_Sigma0(a) + majority(a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = safe_add(d, T1);
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = safe_add(T1, T2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute the current intermediate hash value */
|
||||||
|
ihash[0] += a;
|
||||||
|
ihash[1] += b;
|
||||||
|
ihash[2] += c;
|
||||||
|
ihash[3] += d;
|
||||||
|
ihash[4] += e;
|
||||||
|
ihash[5] += f;
|
||||||
|
ihash[6] += g;
|
||||||
|
ihash[7] += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the next chunk of data and update the SHA256 computation */
|
||||||
|
function sha256_update(data, inputLen) {
|
||||||
|
var i, index, curpos = 0;
|
||||||
|
/* Compute number of bytes mod 64 */
|
||||||
|
index = ((count[0] >> 3) & 0x3f);
|
||||||
|
var remainder = (inputLen & 0x3f);
|
||||||
|
|
||||||
|
/* Update number of bits */
|
||||||
|
if ((count[0] += (inputLen << 3)) < (inputLen << 3)) count[1]++;
|
||||||
|
count[1] += (inputLen >> 29);
|
||||||
|
|
||||||
|
/* Transform as many times as possible */
|
||||||
|
for(i=0; i+63<inputLen; i+=64) {
|
||||||
|
for(var j=index; j<64; j++)
|
||||||
|
buffer[j] = data.charCodeAt(curpos++);
|
||||||
|
sha256_transform();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer remaining input */
|
||||||
|
for(var j=0; j<remainder; j++)
|
||||||
|
buffer[j] = data.charCodeAt(curpos++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finish the computation by operations such as padding */
|
||||||
|
function sha256_final() {
|
||||||
|
var index = ((count[0] >> 3) & 0x3f);
|
||||||
|
buffer[index++] = 0x80;
|
||||||
|
if(index <= 56) {
|
||||||
|
for(var i=index; i<56; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
} else {
|
||||||
|
for(var i=index; i<64; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
sha256_transform();
|
||||||
|
for(var i=0; i<56; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
buffer[56] = (count[1] >>> 24) & 0xff;
|
||||||
|
buffer[57] = (count[1] >>> 16) & 0xff;
|
||||||
|
buffer[58] = (count[1] >>> 8) & 0xff;
|
||||||
|
buffer[59] = count[1] & 0xff;
|
||||||
|
buffer[60] = (count[0] >>> 24) & 0xff;
|
||||||
|
buffer[61] = (count[0] >>> 16) & 0xff;
|
||||||
|
buffer[62] = (count[0] >>> 8) & 0xff;
|
||||||
|
buffer[63] = count[0] & 0xff;
|
||||||
|
sha256_transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split the internal hash values into an array of bytes */
|
||||||
|
function sha256_encode_bytes() {
|
||||||
|
var j=0;
|
||||||
|
var output = new Array(32);
|
||||||
|
for(var i=0; i<8; i++) {
|
||||||
|
output[j++] = ((ihash[i] >>> 24) & 0xff);
|
||||||
|
output[j++] = ((ihash[i] >>> 16) & 0xff);
|
||||||
|
output[j++] = ((ihash[i] >>> 8) & 0xff);
|
||||||
|
output[j++] = (ihash[i] & 0xff);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the internal hash as a hex string */
|
||||||
|
function sha256_encode_hex() {
|
||||||
|
var output = new String();
|
||||||
|
for(var i=0; i<8; i++) {
|
||||||
|
for(var j=28; j>=0; j-=4)
|
||||||
|
output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the internal hash as string */
|
||||||
|
function sha256_encode() {
|
||||||
|
var output = new String();
|
||||||
|
for(var i=0; i<8; i++) {
|
||||||
|
for(var j=3; j>=0; j--)
|
||||||
|
output += String.fromCharCode((ihash[i] >>> j*8) & 0xff);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main function: returns a hex string representing the SHA256 value of the
|
||||||
|
given data */
|
||||||
|
function digest256 (data) {
|
||||||
|
sha256_init();
|
||||||
|
sha256_update(data, data.length);
|
||||||
|
sha256_final();
|
||||||
|
return sha256_encode();
|
||||||
|
// return sha256_encode_hex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test if the JS-interpreter is working properly */
|
||||||
|
function sha256_self_test()
|
||||||
|
{
|
||||||
|
return sha256_digest("message digest") ==
|
||||||
|
"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013-2014 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 DEBUG = false;
|
||||||
|
var default_preferences = {"pkdbf2_level": 1000,
|
||||||
|
"account_url": "http://gpass-demo.soutade.fr/demo"};
|
||||||
|
var preferences = {};
|
||||||
|
var protocol_version = 3;
|
||||||
|
var pkdbf2_level;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(text, data)
|
||||||
|
{
|
||||||
|
chrome.extension.sendMessage({type: "notification", options:{"message":text}}, function(response){alert(response);});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPref(key)
|
||||||
|
{
|
||||||
|
if (key in preferences)
|
||||||
|
return preferences[key];
|
||||||
|
else
|
||||||
|
return default_preferences[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPref(key, value)
|
||||||
|
{
|
||||||
|
chrome.storage.local.set({key:value}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_request(domain, login, mkey)
|
||||||
|
{
|
||||||
|
var v = "@@" + domain + ";" + login;
|
||||||
|
debug("will encrypt " + v);
|
||||||
|
debug("with " + a2hex(mkey));
|
||||||
|
var enc = aes.encryptLongString(v, aes.init(mkey));
|
||||||
|
aes.finish();
|
||||||
|
debug("res " + a2hex(enc));
|
||||||
|
|
||||||
|
return enc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ask_server(form, field, logins, domain, wdomain, mkey, salt, submit)
|
||||||
|
{
|
||||||
|
var a, b;
|
||||||
|
|
||||||
|
mkey = pkdbf2(mkey, salt, pkdbf2_level, 256/8);
|
||||||
|
|
||||||
|
keys = "";
|
||||||
|
for(a=0, b=logins.length; a<logins.length; a++)
|
||||||
|
{
|
||||||
|
enc = generate_request(domain, logins[a], mkey);
|
||||||
|
|
||||||
|
keys += (keys.length != 0) ? "&" : "";
|
||||||
|
keys += "k" + a + "=" + a2hex(enc);
|
||||||
|
|
||||||
|
if (wdomain != "")
|
||||||
|
{
|
||||||
|
enc = generate_request(wdomain, logins[a], mkey);
|
||||||
|
|
||||||
|
keys += (keys.length != 0) ? "&" : "";
|
||||||
|
keys += "k" + (b++) + "=" + a2hex(enc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Keys " + keys);
|
||||||
|
|
||||||
|
var gPassRequest = new XMLHttpRequest();
|
||||||
|
|
||||||
|
var ret = SERVER.OK;
|
||||||
|
|
||||||
|
// gPassRequest.addEventListener("progress", function(evt) { ; }, false);
|
||||||
|
gPassRequest.addEventListener("load", function(evt) {
|
||||||
|
var ciphered_password = "";
|
||||||
|
var server_pkdbf2_level = 0;
|
||||||
|
var server_version = 0;
|
||||||
|
|
||||||
|
var r = this.responseText.split("\n");
|
||||||
|
debug("resp " + r);
|
||||||
|
|
||||||
|
for(var a=0; a<r.length; a++)
|
||||||
|
{
|
||||||
|
debug("Analyse " + r[a]);
|
||||||
|
|
||||||
|
params = r[a].split("=");
|
||||||
|
if (params.length != 2 && params[0] != "<end>")
|
||||||
|
{
|
||||||
|
notify("Error : It seems that it's not a gPass server",
|
||||||
|
this.responseText);
|
||||||
|
ret = SERVER.FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(params[0])
|
||||||
|
{
|
||||||
|
case "protocol":
|
||||||
|
debug("protocol : " + params[1]);
|
||||||
|
|
||||||
|
if (params[1].indexOf("gpass-") != 0)
|
||||||
|
{
|
||||||
|
notify("Error : It seems that it's not a gPass server",
|
||||||
|
this.responseText);
|
||||||
|
ret = SERVER.FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_protocol_version = params[1].match(/\d+/)[0];
|
||||||
|
|
||||||
|
if (server_protocol_version > 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_pkdbf2_level = 1000;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Version 3 : nothing special to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "pass":
|
||||||
|
ciphered_password = params[1];
|
||||||
|
break;
|
||||||
|
case "pkdbf2_level":
|
||||||
|
server_pkdbf2_level = parseInt(params[1].match(/\d+/)[0], 10);
|
||||||
|
if (server_pkdbf2_level != NaN &&
|
||||||
|
server_pkdbf2_level != pkdbf2_level &&
|
||||||
|
server_pkdbf2_level >= 1000) // Minimum level for PKDBF2 !
|
||||||
|
{
|
||||||
|
debug("New pkdbf2 level " + server_pkdbf2_level);
|
||||||
|
pkdbf2_level = server_pkdbf2_level;
|
||||||
|
setPref("pkdbf2_level", pkdbf2_level);
|
||||||
|
ret = SERVER.RESTART_REQUEST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "<end>":
|
||||||
|
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);
|
||||||
|
clear_password = aes.decryptLongString(hex2a(ciphered_password), aes.init(mkey));
|
||||||
|
aes.finish();
|
||||||
|
// Remove trailing \0 and salt
|
||||||
|
clear_password = clear_password.replace(/\0*$/, "");
|
||||||
|
clear_password = clear_password.substr(0, clear_password.length-3);
|
||||||
|
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);
|
||||||
|
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 " + getPref("account_url"));
|
||||||
|
gPassRequest.open("POST", getPref("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 on_sumbit(e)
|
||||||
|
{
|
||||||
|
var form = this;
|
||||||
|
var fields = form.getElementsByTagName("input");
|
||||||
|
|
||||||
|
var domain = parseURI.parseUri(form.ownerDocument.baseURI);
|
||||||
|
domain = domain["host"];
|
||||||
|
var wdomain = wildcard_domain(domain);
|
||||||
|
|
||||||
|
var salt = parseURI.parseUri(getPref("account_url"));
|
||||||
|
salt = salt["host"] + salt["path"];
|
||||||
|
|
||||||
|
debug("salt " + salt);
|
||||||
|
|
||||||
|
var user = null;
|
||||||
|
var all_logins = new Array();
|
||||||
|
|
||||||
|
// Get all <input type="text"> && <input type="email">
|
||||||
|
for (var i=0; i<fields.length; i++)
|
||||||
|
{
|
||||||
|
var field = fields[i];
|
||||||
|
if (field.getAttribute("type") == "text" || field.getAttribute("type") == "email")
|
||||||
|
{
|
||||||
|
if (field.hasAttribute("name") && field.value != "")
|
||||||
|
{
|
||||||
|
name = field.getAttribute("name");
|
||||||
|
// Subset of common user field
|
||||||
|
if (name == "user") user = field.value;
|
||||||
|
else if (name == "usr") user = field.value;
|
||||||
|
else if (name == "username") user = field.value;
|
||||||
|
else if (name == "login") user = field.value;
|
||||||
|
all_logins.push(field.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
logins = new Array(user);
|
||||||
|
else
|
||||||
|
logins = all_logins;
|
||||||
|
|
||||||
|
// Look for <input type="password" value="@@...">
|
||||||
|
for (var i=0; i<fields.length; i++)
|
||||||
|
{
|
||||||
|
var field = fields[i];
|
||||||
|
|
||||||
|
if (field.getAttribute("type") == "password")
|
||||||
|
{
|
||||||
|
debug(field.value);
|
||||||
|
password = field.value;
|
||||||
|
if (password.indexOf("@@") != 0 && password.indexOf("@_") != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mkey = password.substring(2);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var ret = ask_server(form, field, logins, domain, wdomain, mkey, salt, (password.indexOf("@@") == 0));
|
||||||
|
|
||||||
|
switch(ret)
|
||||||
|
{
|
||||||
|
case SERVER.OK:
|
||||||
|
break;
|
||||||
|
case SERVER.FAILED:
|
||||||
|
if (logins !== all_logins)
|
||||||
|
{
|
||||||
|
ret = ask_server(form, field, all_logins, domain, wdomain, mkey, salt, (password.indexOf("@@") == 0));
|
||||||
|
if (ret == SERVER.OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SERVER.RESTART_REQUEST:
|
||||||
|
i = -1; // Restart loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function document_loaded(doc)
|
||||||
|
{
|
||||||
|
// If there is a password in the form, add a "submit" listener
|
||||||
|
for(var i=0; i<doc.forms.length; i++)
|
||||||
|
{
|
||||||
|
var form = doc.forms[i];
|
||||||
|
var fields = form.getElementsByTagName("input");
|
||||||
|
for (a=0; a<fields.length; a++)
|
||||||
|
{
|
||||||
|
var field = fields[a];
|
||||||
|
if (field.getAttribute("type") == "password")
|
||||||
|
{
|
||||||
|
form.addEventListener("submit", on_sumbit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(prefs)
|
||||||
|
{
|
||||||
|
for (k in prefs)
|
||||||
|
preferences[k] = prefs[k];
|
||||||
|
pkdbf2_level = getPref("pkdbf2_level");
|
||||||
|
document_loaded(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, load preferences
|
||||||
|
chrome.storage.local.get(null, init);
|
||||||
|
|
||||||
|
function self_test()
|
||||||
|
{
|
||||||
|
if((res = a2hex(pkdbf2("password", "salt", 4096, 256/8))) !=
|
||||||
|
"c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a")
|
||||||
|
console.log("PKDBF2 failed " + res);
|
||||||
|
else
|
||||||
|
console.log("All is OK ! ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// self_test();
|
|
@ -0,0 +1,32 @@
|
||||||
|
// parseUri 1.2.2
|
||||||
|
// (c) Steven Levithan <stevenlevithan.com>
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
parseURI = {
|
||||||
|
|
||||||
|
parseUri : function (str) {
|
||||||
|
var o = {
|
||||||
|
strictMode: false,
|
||||||
|
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
|
||||||
|
q: {
|
||||||
|
name: "queryKey",
|
||||||
|
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
||||||
|
},
|
||||||
|
parser: {
|
||||||
|
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
||||||
|
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||||||
|
}},
|
||||||
|
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
|
||||||
|
uri = {},
|
||||||
|
i = 14;
|
||||||
|
|
||||||
|
while (i--) uri[o.key[i]] = m[i] || "";
|
||||||
|
|
||||||
|
uri[o.q.name] = {};
|
||||||
|
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
|
||||||
|
if ($1) uri[o.q.name][$1] = $2;
|
||||||
|
});
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function pkdbf2 (password, salt, iterations, outlen) {
|
||||||
|
var result = "";
|
||||||
|
var temp = "";
|
||||||
|
var temp2 = "";
|
||||||
|
var temp_res = "";
|
||||||
|
var temp_res2 = "";
|
||||||
|
|
||||||
|
for (i=1; result.length < outlen; i++)
|
||||||
|
{
|
||||||
|
temp = hmac256(password, salt +
|
||||||
|
String.fromCharCode((i & 0xff000000) >> 24) +
|
||||||
|
String.fromCharCode((i & 0x00ff0000) >> 16) +
|
||||||
|
String.fromCharCode((i & 0x0000ff00) >> 8) +
|
||||||
|
String.fromCharCode((i & 0x000000ff) >> 0)
|
||||||
|
);
|
||||||
|
temp_res = temp;
|
||||||
|
|
||||||
|
for(a=1; a<iterations; a++)
|
||||||
|
{
|
||||||
|
temp2 = hmac256(password, temp);
|
||||||
|
temp_res2 = "";
|
||||||
|
for(b = 0; b<temp_res.length; b++)
|
||||||
|
temp_res2 += String.fromCharCode(temp_res.charCodeAt(b) ^ temp2.charCodeAt(b));
|
||||||
|
temp_res = temp_res2;
|
||||||
|
temp = temp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += temp_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.substr(0, outlen);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
|
||||||
|
"name": "gpass",
|
||||||
|
"version": "0.6",
|
||||||
|
"description": "gPass : global password manager",
|
||||||
|
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["https://*/*", "http://*/*"],
|
||||||
|
"js": ["lib/parseuri.js", "lib/jsaes.js", "lib/jssha256.js", "lib/hmac.js", "lib/pkdbf2.js", "lib/main.js"],
|
||||||
|
"run_at" : "document_idle",
|
||||||
|
"all_frames" : true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"persistent": false,
|
||||||
|
"scripts": ["background.js"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"options_page": "options.html",
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"https://*/",
|
||||||
|
"http://*/",
|
||||||
|
"notifications",
|
||||||
|
"storage"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>gPass</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<b>Account URL</b> URL of your gPass account <input id="account_url" type="text"/><br />
|
||||||
|
<b>WARNING</b> It should be a valid HTTPS URL because doesn't like mixed content (https/http) with a recognized certificate. Of not, requests will silentely failed. If you have an auto-signed certificate, add it to trusted ones.<br/>
|
||||||
|
<br/>
|
||||||
|
<b>PKDBF2 level</b> Number of iterations used to derivate master key <input id="pkdbf2" type="number"/><br />
|
||||||
|
<br/>
|
||||||
|
<input type="button" id="save" value="Save"/>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="options.js">
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,31 @@
|
||||||
|
var default_preferences = {"pkdbf2_level": 1000,
|
||||||
|
"account_url": "http://gpass-demo.soutade.fr/demo"};
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
var account_url = document.getElementById('account_url').value;
|
||||||
|
var pkdbf2 = document.getElementById('pkdbf2').value;
|
||||||
|
|
||||||
|
chrome.storage.local.set({
|
||||||
|
'account_url': account_url,
|
||||||
|
'pkdbf2': pkdbf2,
|
||||||
|
}, function() {
|
||||||
|
alert('Saved');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.local.get(null, function(prefs) {
|
||||||
|
if (!prefs.hasOwnProperty("account_url"))
|
||||||
|
account_url = default_preferences['account_url'];
|
||||||
|
else
|
||||||
|
account_url = prefs['account_url'];
|
||||||
|
|
||||||
|
if (!prefs.hasOwnProperty("pkdbf2_level"))
|
||||||
|
pkdbf2 = default_preferences['pkdbf2_level'];
|
||||||
|
else
|
||||||
|
pkdbf2 = prefs['pkdbf2_level'];
|
||||||
|
|
||||||
|
document.getElementById('account_url').value = account_url;
|
||||||
|
document.getElementById('pkdbf2').value = pkdbf2;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('save').addEventListener("click", save);
|
Loading…
Reference in New Issue