Add new encryption scheme in server part.
This commit is contained in:
parent
89465f4c68
commit
cef1194ad0
|
@ -44,7 +44,7 @@ function load_database()
|
|||
// Brute force ??
|
||||
$db->close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$db->query("UPDATE conf SET last_access_time=$usec");
|
||||
$db->close();
|
||||
$db = new SQLite3("./gpass.bdd", SQLITE3_OPEN_READONLY);
|
||||
|
@ -79,6 +79,7 @@ for ($i=0; $i<$MAX_PASSWORDS_PER_REQUEST && isset($_POST["k$i"]); $i++)
|
|||
$result->finalize();
|
||||
if (isset($row["password"]))
|
||||
{
|
||||
echo "matched_key=" . $i . "\n";
|
||||
echo "pass=" . $row["password"] . "\n";
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -80,4 +80,11 @@ $REQUESTS_MIN_DELAY=1000;
|
|||
Clear master keys and reset passwords after 15 minutes of inactivity
|
||||
*/
|
||||
$CLEAR_TIME=15*60*1000;
|
||||
|
||||
/*
|
||||
The first crypto schema use an AES-ECB process to encrypt logins.
|
||||
It's used until version 0.7.
|
||||
Since version 0.8, we use AES-CBC + SHA256.
|
||||
*/
|
||||
$CRYPTO_V1_COMPATIBLE=1;
|
||||
?>
|
|
@ -20,11 +20,13 @@
|
|||
|
||||
/*
|
||||
login is stored as :
|
||||
@@url;login
|
||||
url;login + 16 bytes padding * \0 + sha256(url;login + padding)[8:24]
|
||||
|
||||
Password is salted (3 random characters) and encrypted
|
||||
|
||||
All is encrypted with AES256 and key : PKDBF2(hmac_sha256, master key, url, 1000)
|
||||
All is encrypted with AES256-CBC and key PBKDF2(hmac_sha256, master key, server url, 1000)
|
||||
level is server configurable
|
||||
iv is PBKDF2(hmac_sha256, server url, master key, 1000)[0:16]
|
||||
*/
|
||||
$MAX_ENTRY_LEN = 512;
|
||||
$USERS_PATH = "./users/";
|
||||
|
|
|
@ -79,6 +79,7 @@ else
|
|||
<?php
|
||||
echo "pbkdf2_level=$PBKDF2_LEVEL; use_shadow_logins=$USE_SHADOW_LOGINS;\n";
|
||||
echo "CLEAR_TIME=$CLEAR_TIME; // Clear master key after 15 minutes\n";
|
||||
echo "CRYPTO_V1_COMPATIBLE=$CRYPTO_V1_COMPATIBLE;\n";
|
||||
?>
|
||||
</script>
|
||||
<script src="resources/misc.js"></script>
|
||||
|
|
|
@ -119,16 +119,18 @@ function a2hex(str) {
|
|||
return hex;
|
||||
}
|
||||
|
||||
function derive_mkey(user, mkey)
|
||||
async function derive_mkey(user, mkey)
|
||||
{
|
||||
url = url_domain(document.URL) + "/" + user;
|
||||
return pbkdf2(mkey, url, pbkdf2_level, 256/8);
|
||||
global_iv = simple_pbkdf2(url, mkey, pbkdf2_level);
|
||||
return crypto_pbkdf2(mkey, url, pbkdf2_level);
|
||||
}
|
||||
|
||||
var passwords;
|
||||
var current_user = "";
|
||||
var current_mkey = "";
|
||||
var clearTimer = null;
|
||||
var global_iv = null;
|
||||
|
||||
function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
||||
this.ciphered_login = ciphered_login;
|
||||
|
@ -165,13 +167,19 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
if (masterkey == null || this.clear_url == "" || this.clear_login == "")
|
||||
return false;
|
||||
|
||||
ciphered_login = "@@" + this.clear_url + ";" + this.clear_login;
|
||||
var ciphered_login = this.clear_url + ";" + this.clear_login;
|
||||
while ((ciphered_login.length % 16))
|
||||
ciphered_login += "\0";
|
||||
var computed_hash = await digest(ciphered_login);
|
||||
ciphered_login += computed_hash.slice(8, 24);
|
||||
var iv = await global_iv;
|
||||
iv = iv.slice(0, 16);
|
||||
|
||||
// Add salt
|
||||
ciphered_password = this.clear_password + generate_random(3, false);
|
||||
var ciphered_password = generate_random(3, false) + this.clear_password ;
|
||||
|
||||
this.ciphered_login = a2hex(await encrypt(masterkey, ciphered_login));
|
||||
this.ciphered_password = a2hex(await encrypt(masterkey, ciphered_password));
|
||||
this.ciphered_login = a2hex(await encrypt_cbc(masterkey, iv, ciphered_login));
|
||||
this.ciphered_password = a2hex(await encrypt_cbc(masterkey, iv, ciphered_password));
|
||||
|
||||
this.unciphered = true;
|
||||
this.masterkey = masterkey;
|
||||
|
@ -188,25 +196,53 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
if (masterkey == this.masterkey)
|
||||
return (this.unciphered == true);
|
||||
|
||||
login = await decrypt(masterkey, hex2a(this.ciphered_login));
|
||||
login = login.replace(/\0*$/, "");
|
||||
if (login.indexOf("@@") != 0)
|
||||
var old = false;
|
||||
var iv = await global_iv;
|
||||
iv = iv.slice(0, 16);
|
||||
var login = await decrypt_cbc(masterkey, iv, hex2a(this.ciphered_login));
|
||||
|
||||
var computed_digest = await digest(login.slice(0, login.length-16))
|
||||
computed_digest = computed_digest.slice(8, 24);
|
||||
|
||||
if (login.indexOf(computed_digest) == login.length-16)
|
||||
{
|
||||
return false;
|
||||
login = login.slice(0, login.length-16).replace(/\0*$/, "");
|
||||
}
|
||||
// Remove @@
|
||||
login = login.substring(2);
|
||||
else if (CRYPTO_V1_COMPATIBLE)
|
||||
{
|
||||
login = await decrypt_ecb(masterkey, hex2a(this.ciphered_login));
|
||||
if (login.indexOf("@@") != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
login = login.replace(/\0*$/, "");
|
||||
// Remove @@
|
||||
login = login.substring(2);
|
||||
old = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
infos = login.split(";");
|
||||
this.clear_url = infos[0];
|
||||
this.clear_login = infos[1];
|
||||
this.clear_password = await decrypt(masterkey, hex2a(this.ciphered_password));
|
||||
if (old)
|
||||
{
|
||||
this.clear_password = await decrypt_ecb(masterkey, hex2a(this.ciphered_password));
|
||||
// Remove salt
|
||||
this.clear_password = this.clear_password.replace(/\0*$/, "");
|
||||
this.clear_password = this.clear_password.substr(0, this.clear_password.length-3);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clear_password = await decrypt_cbc(masterkey, iv, hex2a(this.ciphered_password));
|
||||
// Remove salt
|
||||
this.clear_password = this.clear_password.replace(/\0*$/, "");
|
||||
this.clear_password = this.clear_password.substr(3, this.clear_password.length);
|
||||
}
|
||||
this.unciphered = true;
|
||||
this.masterkey = masterkey;
|
||||
|
||||
// Remove salt
|
||||
this.clear_password = this.clear_password.replace(/\0*$/, "");
|
||||
this.clear_password = this.clear_password.substr(0, this.clear_password.length-3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -222,7 +258,7 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
|
||||
this.shadow_login_to_access_token = async function(masterkey)
|
||||
{
|
||||
this.access_token = await encrypt(masterkey, hex2a(this.shadow_login));
|
||||
this.access_token = await encrypt_ecb(masterkey, hex2a(this.shadow_login));
|
||||
this.access_token = a2hex(this.access_token);
|
||||
}
|
||||
|
||||
|
@ -857,11 +893,14 @@ async function update_masterkey()
|
|||
return;
|
||||
|
||||
oldmkey = derive_mkey(current_user, oldmkey);
|
||||
old_global_iv = global_iv;
|
||||
current_mkey = derive_mkey(current_user, newmkey);
|
||||
new_global_iv = global_iv;
|
||||
|
||||
var found = 0;
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
global_iv = old_global_iv;
|
||||
if (await passwords[i].decrypt(oldmkey))
|
||||
{
|
||||
ok = remove_password_server(current_user, passwords[i].ciphered_login, passwords[i].access_token);
|
||||
|
@ -871,6 +910,7 @@ async function update_masterkey()
|
|||
break;
|
||||
}
|
||||
|
||||
global_iv = new_global_iv;
|
||||
await passwords[i].encrypt(current_mkey);
|
||||
ok = add_password_server(current_user, passwords[i]);
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
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;
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
function AES(a) {
|
||||
this.init = function (myKey){
|
||||
AES_Init();
|
||||
var key = string2Bin(myKey);
|
||||
AES_ExpandKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
this.encrypt = function ( inputStr,key ) {
|
||||
var block = string2Bin(inputStr);
|
||||
AES_Encrypt(block, key);
|
||||
var data=bin2String(block);
|
||||
return data;
|
||||
}
|
||||
|
||||
this.decrypt = function ( inputStr,key ) {
|
||||
block = string2Bin(inputStr);
|
||||
AES_Decrypt(block, key);
|
||||
var data=bin2String(block);
|
||||
return data;
|
||||
}
|
||||
|
||||
this.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);
|
||||
}
|
||||
}
|
||||
|
||||
this.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);
|
||||
}
|
||||
}
|
||||
|
||||
this.finish = function(){
|
||||
AES_Done();
|
||||
}
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
/*
|
||||
* 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";
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ function str2ab(str) {
|
|||
return bufView;
|
||||
}
|
||||
|
||||
function pbkdf2(mkey, salt, level)
|
||||
function crypto_pbkdf2(mkey, salt, level)
|
||||
{
|
||||
AESCBC = {
|
||||
name: "AES-CBC",
|
||||
|
@ -76,19 +76,49 @@ function pbkdf2(mkey, salt, level)
|
|||
});
|
||||
}
|
||||
|
||||
function _encrypt(mkey, data)
|
||||
function simple_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, true, ["unwrapKey", "wrapKey"])
|
||||
.then(function(key) {
|
||||
return crypto.subtle.exportKey("raw", key)
|
||||
.then(function (key) {
|
||||
return ab2str(key);
|
||||
});
|
||||
})
|
||||
.catch(function(err){
|
||||
console.log("Error derive key " + err);
|
||||
});
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log("Error import key" + err);
|
||||
});
|
||||
}
|
||||
|
||||
function _encrypt(mkey, iv, 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
|
||||
iv: iv
|
||||
}, mkey, data)})
|
||||
.then(function(encrypted) {
|
||||
return ab2str(encrypted);
|
||||
|
@ -100,22 +130,21 @@ function _encrypt(mkey, data)
|
|||
return promise;
|
||||
}
|
||||
|
||||
async function _decrypt(mkey, data)
|
||||
async function _decrypt(mkey, iv, data)
|
||||
{
|
||||
while ((data.length % 16))
|
||||
data += "\0";
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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));
|
||||
pkcs7_padding = await _encrypt(mkey, nulliv, 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
|
||||
iv: iv
|
||||
}, mkey, data)})
|
||||
.then(function(decrypted) {
|
||||
return ab2str(decrypted);
|
||||
|
@ -127,36 +156,63 @@ async function _decrypt(mkey, data)
|
|||
return promise;
|
||||
}
|
||||
|
||||
async function encrypt(mkey, data)
|
||||
async function encrypt_ecb(mkey, data)
|
||||
{
|
||||
var result = "";
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
while (data.length > 16)
|
||||
{
|
||||
res = await _encrypt(mkey, data.slice(0, 16));
|
||||
res = await _encrypt(mkey, nulliv, data.slice(0, 16));
|
||||
// Remove PKCS7 padding
|
||||
result += res.slice(0, 16);
|
||||
data = data.slice(16);
|
||||
}
|
||||
res = await _encrypt(mkey, data);
|
||||
res = await _encrypt(mkey, nulliv, data);
|
||||
result += res.slice(0, 16);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function decrypt(mkey, data)
|
||||
async function decrypt_ecb(mkey, data)
|
||||
{
|
||||
var result = "";
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
while (data.length > 16)
|
||||
{
|
||||
res = await _decrypt(mkey, data.slice(0, 16));
|
||||
res = await _decrypt(mkey, nulliv, data.slice(0, 16));
|
||||
// Remove PKCS7 padding
|
||||
result += res.slice(0, 16);
|
||||
data = data.slice(16);
|
||||
}
|
||||
res = await _decrypt(mkey, data);
|
||||
res = await _decrypt(mkey, nulliv, data);
|
||||
result += res.slice(0, 16);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function encrypt_cbc(mkey, iv, data)
|
||||
{
|
||||
var result = await _encrypt(mkey, str2ab(iv), data);
|
||||
|
||||
// Remove PKCS7 padding
|
||||
return result.slice(0, result.length-16);
|
||||
}
|
||||
|
||||
async function decrypt_cbc(mkey, iv, data)
|
||||
{
|
||||
var result = await _decrypt(mkey, str2ab(iv), data);
|
||||
|
||||
// Remove PKCS7 padding
|
||||
return result.slice(0, result.length-16);
|
||||
}
|
||||
|
||||
async function digest(data)
|
||||
{
|
||||
return crypto.subtle.digest("SHA-256", str2ab(data)).then(function (hash) {
|
||||
return ab2str(hash);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user