diff --git a/server/_user b/server/_user index ac5d4c7..10a1288 100644 --- a/server/_user +++ b/server/_user @@ -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); @@ -69,9 +69,7 @@ $statement = $db->prepare("SELECT password FROM gpass WHERE login=:login"); echo "protocol=gpass-$PROTOCOL_VERSION\n"; if ($PBKDF2_LEVEL != 1000) -{ echo "pbkdf2_level=$PBKDF2_LEVEL\n"; -} for ($i=0; $i<$MAX_PASSWORDS_PER_REQUEST && isset($_POST["k$i"]); $i++) { @@ -81,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; } diff --git a/server/conf.php b/server/conf.php index a19cf38..a7381ea 100644 --- a/server/conf.php +++ b/server/conf.php @@ -37,7 +37,7 @@ $ADMIN_MODE=true; !! Warning !! This impact master keys. So if you change this value with existings masterkeys, they will unusable ! */ -$BKDF2_LEVEL=1000; +$PBKDF2_LEVEL=1000; /* This is a security feature : It protects from database dump @@ -50,16 +50,13 @@ $BKDF2_LEVEL=1000; encrypted login/password values and remove them. It's a kind of challenge. - This option is backward compatible with old version < 0.6, but - once activated it cannot be reverted as access tokens will be - generated for all values. So, if you want to test it, make - a copy of your databases before ! + This option is backward compatible with old version < 0.6 For now it's deactivated because it requires high cpu bandwidth (one derivation + two decryption for each password !). When standard crypto API will be stable it will be enabled by default. */ -$USE_SHADOW_LOGINS=0; +$USE_SHADOW_LOGINS=1; /* Protection against DDoS. @@ -83,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; ?> \ No newline at end of file diff --git a/server/functions.php b/server/functions.php index 116ca66..696b968 100755 --- a/server/functions.php +++ b/server/functions.php @@ -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 : PBKDF2(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/"; @@ -129,10 +131,10 @@ function migrate_database($user, $db) $migration_functions = ['_migrate_0', '_migrate_1']; $version = $db->querySingle("SELECT db_version FROM conf"); - if ($version == false || $version == -1) + if ($version == NULL || $version == -1) { $version = $db->querySingle("SELECT version FROM db_version"); - if ($version == false || $version == -1) + if ($version == NULL || $version == -1) $version = 0; } @@ -170,6 +172,8 @@ function load_database($user) function add_entry($user, $login, $password, $shadow_login, $salt, $access_token) { + global $USE_SHADOW_LOGINS; + $db = load_database($user); if ($db == null) @@ -178,22 +182,30 @@ function add_entry($user, $login, $password, return false; } + if ($USE_SHADOW_LOGINS && (strlen($shadow_login) != 32 || + strlen($salt) != 32 || strlen($access_token) != 32)) + { + $db->close(); + echo "Shadow login not configured"; + return false; + } + $count = $db->querySingle("SELECT COUNT(*) FROM gpass WHERE login='" . $login . "'"); - if ($count != 0) + if ($count != NULL && $count != 0) { echo "Entry already exists"; return false; } - $result = $db->query("INSERT INTO gpass ('login', 'password', 'shadow_login', 'salt', 'access_token') VALUES + $result = $db->exec("INSERT INTO gpass ('login', 'password', 'shadow_login', 'salt', 'access_token') VALUES ('" . $login . "', '" . $password . "', '" . $shadow_login . "', '" . $salt . "', '" . $access_token . "')"); /* error_log("INSERT INTO gpass ('login', 'password', 'shadow_login', 'salt', 'access_token') VALUES */ /* ('" . $login . "', '" . $password . "', '" . $shadow_login . "', '" . $salt . "', '" . $access_token . "')"); */ $db->close(); - if ($result == FALSE) + if (!$result) { echo "Error " . $db->lastErrorMsg(); return false; @@ -207,6 +219,8 @@ function add_entry($user, $login, $password, function delete_entry($user, $login, $access_token) { + global $USE_SHADOW_LOGINS; + $db = load_database($user); if ($db == null) @@ -215,28 +229,29 @@ function delete_entry($user, $login, $access_token) return false; } - $db_ac = $db->querySingle("SELECT access_token FROM gpass WHERE login='" . $login . "'"); - if (strlen($db_ac) != 0 && strcmp($db_ac, $access_token)) + if ($USE_SHADOW_LOGINS) { - $db->close(); - echo "Bad access token"; + $db_ac = $db->querySingle("SELECT access_token FROM gpass WHERE login='" . $login . "'"); + if ($db_ac != NULL && strcmp($db_ac, $access_token)) + { + $db->close(); + echo "Bad access token"; + return false; + } + } + + $result = $db->exec("DELETE FROM gpass WHERE login='" . $login . "'"); + $db->close(); + + if (!$result) + { + echo "Error " . $db->lastErrorMsg(); return false; } else { - $result = $db->query("DELETE FROM gpass WHERE login='" . $login . "'"); - $db->close(); - - if ($result == FALSE) - { - echo "Error " . $db->lastErrorMsg(); - return false; - } - else - { - echo "OK"; - return true; - } + echo "OK"; + return true; } } @@ -250,6 +265,8 @@ function update_entry($user, $mkey, $old_login, $url, $login, $password, $shadow function list_entries($user) { + global $USE_SHADOW_LOGINS; + $db = load_database($user); if ($db == null) return; @@ -264,7 +281,7 @@ function list_entries($user) { if ($first) echo ","; else $first = true; - if (!strlen($row['shadow_login'])) + if (!strlen($row['shadow_login']) || !$USE_SHADOW_LOGINS) echo "{\"login\" : \"" . $row['login'] . "\", \"password\" : \"" . $row['password'] . "\" }\n"; else echo "{\"shadow_login\" : \"" . $row['shadow_login'] . "\", \"salt\" : \"" . $row['salt'] . "\" }\n"; diff --git a/server/index.php b/server/index.php index 845b781..7ae897d 100644 --- a/server/index.php +++ b/server/index.php @@ -18,9 +18,8 @@ along with gPass. If not, see . */ -include('functions.php'); - include('conf.php'); +include('functions.php'); session_start(); @@ -80,17 +79,31 @@ else + document.addEventListener('DOMContentLoaded', function() { + window.onscroll = function(ev) { + document.getElementById("buttonTop").className = (window.pageYOffset > 500) ? "cVisible" : "cInvisible"; + }; +}); + function scrollToTop() + { + if (window.pageYOffset == 0) + return; + target = (window.innerHeight) ? window.innerHeight/5 : 200; + toScroll = (window.pageYOffset > target) ? target : window.pageYOffset; + window.scrollBy(0, -toScroll); + + setTimeout(scrollToTop, 24); + } - - - - + gPass : global Password +
@@ -137,8 +150,6 @@ else echo "
Current addon address is : https://" . $_SERVER['SERVER_NAME'] . "/" . $user . "
\n"; } ?> -
-
+
+
\n"; + text = "\n"; for(i=0; i= 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;i16){ - var data=''; - for(var i=0;i -* -* 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> 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"; -} - - diff --git a/server/resources/misc.js b/server/resources/misc.js new file mode 100644 index 0000000..a85bdf4 --- /dev/null +++ b/server/resources/misc.js @@ -0,0 +1,220 @@ +/* + 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 . +*/ + +var default_preferences = {"pbkdf2_level": 1000, + "account_url": "https://gpass-demo.soutade.fr/demo"}; + +var browser = browser; +if (typeof chrome !== 'undefined') + 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 crypto_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 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); + + promise = mkey.then(function(mkey){ + return crypto.subtle.encrypt({ + name: "AES-CBC", + iv: iv + }, mkey, data)}) + .then(function(encrypted) { + return ab2str(encrypted); + }) + .catch(function(encryption) { + console.log("Encryption rejected " + encryption); + }); + + return promise; +} + +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, nulliv, ab2str(pkcs7_padding)); + + data = str2ab(data + pkcs7_padding); + + promise = mkey.then(function(mkey){ + return crypto.subtle.decrypt({ + name: "AES-CBC", + iv: iv + }, mkey, data)}) + .then(function(decrypted) { + return ab2str(decrypted); + }) + .catch(function(decryption) { + console.log("Decryption rejected " + decryption); + }); + + return promise; +} + +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, nulliv, data.slice(0, 16)); + // Remove PKCS7 padding + result += res.slice(0, 16); + data = data.slice(16); + } + res = await _encrypt(mkey, nulliv, data); + result += res.slice(0, 16); + + return result; +} + +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, nulliv, data.slice(0, 16)); + // Remove PKCS7 padding + result += res.slice(0, 16); + data = data.slice(16); + } + 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); + }); +} diff --git a/server/tests/test.js b/server/tests/test.js new file mode 100644 index 0000000..05e5719 --- /dev/null +++ b/server/tests/test.js @@ -0,0 +1,201 @@ +server_url = "https://gpass.soutade.fr"; +current_user = "test-v7"; +pbkdf2_level = 1000; +CRYPTO_V1_COMPATIBLE = 1; +use_shadow_logins = false; + +/* + Must contains : + URL login password masterkey + ----------------------------------------------------------------- +v7 format + test test test test + test2 test2 test2 test2 (+shadow login) + test16 test16 testtesttesttest test16 + test17 test17 testtesttesttestt test17 +v8 format + testv8 testv8 testv8 testv8 + testv8_2 testv8_2 testv8_2 testv8_2 (+shadow login) + testv8_16 testv8_16 testtesttesttest testv8_16 + testv8_17 testv8_17 testtesttesttestt testv8_17 + + We assume shadow logins are enabled in server side +*/ + +function alert(a) {} +function nb_unciphered_passwords(passwords) +{ + var nb_unciphered = 0; + for(i=0;i + + + + + QUnit Example + + + +
+
+ + + + + + diff --git a/server/tests/users/test-v7/gpass.bdd b/server/tests/users/test-v7/gpass.bdd new file mode 100644 index 0000000..b122377 Binary files /dev/null and b/server/tests/users/test-v7/gpass.bdd differ diff --git a/server/tests/users/test-v7/gpass.bdd.init b/server/tests/users/test-v7/gpass.bdd.init new file mode 100644 index 0000000..b122377 Binary files /dev/null and b/server/tests/users/test-v7/gpass.bdd.init differ diff --git a/server/resources/hmac.js b/server/tests/users/test-v7/index.php similarity index 58% rename from server/resources/hmac.js rename to server/tests/users/test-v7/index.php index e8af667..3f09c8a 100644 --- a/server/resources/hmac.js +++ b/server/tests/users/test-v7/index.php @@ -1,3 +1,4 @@ +. */ -function hmac256(key, message) { - var ipad = ""; - var opad = ""; +include "../../_user"; - 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; -} +?> \ No newline at end of file