Merge branch 'master' of soutade.fr:gpass
This commit is contained in:
commit
09e0d85d97
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
?>
|
|
@ -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";
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
include('functions.php');
|
||||
|
||||
include('conf.php');
|
||||
include('functions.php');
|
||||
|
||||
session_start();
|
||||
|
||||
|
@ -80,17 +79,31 @@ 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";
|
||||
?>
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<script src="resources/jsaes.js"></script>
|
||||
<script src="resources/jssha256.js"></script>
|
||||
<script src="resources/hmac.js"></script>
|
||||
<script src="resources/pbkdf2.js"></script>
|
||||
<script src="resources/misc.js"></script>
|
||||
<script src="resources/gpass.js"></script>
|
||||
<script src="resources/pwdmeter.js"></script>
|
||||
<title>gPass : global Password</title>
|
||||
</head>
|
||||
<body onload="start();">
|
||||
<div><a id="buttonTop" class="cInvisible" onclick="scrollToTop();"></a></div>
|
||||
<div id="logo">
|
||||
<a href="http://indefero.soutade.fr/p/gpass"><img src="resources/gpass.png" alt="logo"/></a>
|
||||
</div>
|
||||
|
@ -137,8 +150,6 @@ else
|
|||
echo "<div id=\"addon_address\">Current addon address is : https://" . $_SERVER['SERVER_NAME'] . "/" . $user . "</div>\n";
|
||||
}
|
||||
?>
|
||||
<div id="passwords">
|
||||
</div>
|
||||
<div id="add_new_password">
|
||||
<?php
|
||||
global $user;
|
||||
|
@ -158,6 +169,8 @@ if ($user != "")
|
|||
}
|
||||
?>
|
||||
</div>
|
||||
<div id="passwords">
|
||||
</div>
|
||||
<div id="update_masterkey">
|
||||
<?php
|
||||
global $user;
|
||||
|
|
|
@ -126,4 +126,37 @@ body {
|
|||
position:absolute;
|
||||
width: 100px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* From http://www.trucsweb.com/tutoriels/javascript/retour-haut/ */
|
||||
a#buttonTop{
|
||||
border-radius:3px;
|
||||
padding:0 10px 10px 10px;
|
||||
font-size:3em;
|
||||
text-align:center;
|
||||
color:#fff;
|
||||
background:rgba(0, 0, 0, 0.25);
|
||||
position:fixed;
|
||||
right:3%;
|
||||
opacity:1;
|
||||
z-index:99999;
|
||||
transition:all ease-in 0.2s;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
a#buttonTop:before{ content: "\25b2"; }
|
||||
a#buttonTop:hover{
|
||||
background:rgba(0, 0, 0, 1);
|
||||
transition:all ease-in 0.2s;
|
||||
}
|
||||
a#buttonTop.cInvisible{
|
||||
bottom:-35px;
|
||||
opacity:0;
|
||||
transition:all ease-in 0.5s;
|
||||
}
|
||||
|
||||
a#buttonTop.cVisible{
|
||||
bottom:20px;
|
||||
opacity:1;
|
||||
}
|
||||
|
|
|
@ -119,17 +119,19 @@ function a2hex(str) {
|
|||
return hex;
|
||||
}
|
||||
|
||||
function derive_mkey(user, mkey)
|
||||
async function derive_mkey(user, mkey)
|
||||
{
|
||||
url = url_domain(document.URL) + "/" + user;
|
||||
mkey = a2hex(pbkdf2(mkey, url, pbkdf2_level, 256/8));
|
||||
return mkey;
|
||||
url = url_domain(server_url) + "/" + user;
|
||||
global_iv = simple_pbkdf2(url, mkey, pbkdf2_level);
|
||||
return crypto_pbkdf2(mkey, url, pbkdf2_level);
|
||||
}
|
||||
|
||||
var passwords;
|
||||
var passwords = null;
|
||||
var current_user = "";
|
||||
var current_mkey = "";
|
||||
var clearTimer = null;
|
||||
var global_iv = null;
|
||||
var server_url = document.documentURI;
|
||||
|
||||
function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
||||
this.ciphered_login = ciphered_login;
|
||||
|
@ -138,7 +140,7 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
this.clear_url = "";
|
||||
this.clear_login = "";
|
||||
this.clear_password = "";
|
||||
this.masterkey = "";
|
||||
this.masterkey = null;
|
||||
this.salt = salt;
|
||||
this.shadow_login = shadow_login;
|
||||
this.access_token = "";
|
||||
|
@ -149,73 +151,105 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
this.clear_url = "";
|
||||
this.clear_login = "";
|
||||
this.clear_password = "";
|
||||
this.masterkey = "";
|
||||
this.masterkey = null;
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
this.encrypt = function(masterkey)
|
||||
this.reset_master_key = function()
|
||||
{
|
||||
this.masterkey = null;
|
||||
}
|
||||
|
||||
this.encrypt = async function(masterkey)
|
||||
{
|
||||
if (masterkey == this.masterkey)
|
||||
return true;
|
||||
|
||||
if (masterkey == "" || this.clear_url == "" || this.clear_login == "")
|
||||
if (masterkey == null || this.clear_url == "" || this.clear_login == "")
|
||||
return false;
|
||||
|
||||
ciphered_login = "@@" + this.clear_url + ";" + this.clear_login;
|
||||
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 ;
|
||||
|
||||
aes = new AES();
|
||||
a_masterkey = aes.init(hex2a(masterkey));
|
||||
this.ciphered_login = a2hex(aes.encryptLongString(ciphered_login, a_masterkey));
|
||||
this.ciphered_password = a2hex(aes.encryptLongString(ciphered_password, a_masterkey));
|
||||
aes.finish();
|
||||
this.ciphered_login = a2hex(await encrypt_cbc(masterkey, iv, ciphered_login));
|
||||
this.ciphered_password = a2hex(await encrypt_cbc(masterkey, iv, ciphered_password));
|
||||
|
||||
this.unciphered = true;
|
||||
this.masterkey = masterkey;
|
||||
|
||||
if (use_shadow_logins)
|
||||
this.generate_access_token(masterkey);
|
||||
await this.generate_access_token(masterkey);
|
||||
}
|
||||
|
||||
this.decrypt = function(masterkey)
|
||||
this.decrypt = async function(masterkey)
|
||||
{
|
||||
if (masterkey == this.masterkey && this.unciphered == true)
|
||||
return true;
|
||||
|
||||
if (masterkey == "" || this.unciphered == true)
|
||||
if (masterkey == null)
|
||||
return false;
|
||||
|
||||
aes = new AES();
|
||||
a_masterkey = aes.init(hex2a(masterkey));
|
||||
login = aes.decryptLongString(hex2a(this.ciphered_login), a_masterkey);
|
||||
login = login.replace(/\0*$/, "");
|
||||
if (login.indexOf("@@") != 0)
|
||||
if (masterkey == this.masterkey)
|
||||
return (this.unciphered == true);
|
||||
|
||||
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)
|
||||
{
|
||||
aes.finish();
|
||||
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 = aes.decryptLongString(hex2a(this.ciphered_password), a_masterkey);
|
||||
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;
|
||||
aes.finish();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
this.isUnciphered = function(masterkey)
|
||||
{
|
||||
return (this.unciphered == true && masterkey == this.masterkey && masterkey != "")
|
||||
return (this.unciphered == true && masterkey == this.masterkey && masterkey != null)
|
||||
}
|
||||
|
||||
this.isCiphered = function(masterkey)
|
||||
|
@ -223,28 +257,24 @@ function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
|||
return !(this.isUnciphered(masterkey));
|
||||
}
|
||||
|
||||
this.shadow_login_to_access_token = function(masterkey)
|
||||
this.shadow_login_to_access_token = async function(masterkey)
|
||||
{
|
||||
var aes = new AES();
|
||||
var key = pbkdf2(hex2a(masterkey), hex2a(this.salt), pbkdf2_level, 256/8);
|
||||
var a_key = aes.init(hex2a(key));
|
||||
this.access_token = aes.encryptLongString(hex2a(this.shadow_login), a_key);
|
||||
this.access_token = await encrypt_ecb(masterkey, hex2a(this.shadow_login));
|
||||
this.access_token = a2hex(this.access_token);
|
||||
aes.finish();
|
||||
}
|
||||
|
||||
this.generate_access_token = function(masterkey)
|
||||
this.generate_access_token = async function(masterkey)
|
||||
{
|
||||
this.salt = a2hex(generate_random(16, false));
|
||||
this.shadow_login = a2hex(generate_random(16, false));
|
||||
|
||||
return this.shadow_login_to_access_token(masterkey);
|
||||
return await this.shadow_login_to_access_token(masterkey);
|
||||
}
|
||||
}
|
||||
|
||||
function clearMasterKey()
|
||||
{
|
||||
current_mkey = "";
|
||||
current_mkey = null;
|
||||
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
|
@ -269,6 +299,7 @@ function startClearTimer()
|
|||
{
|
||||
clearMasterKey();
|
||||
change_master_key(false);
|
||||
scrollToTop();
|
||||
}
|
||||
, CLEAR_TIME);
|
||||
}
|
||||
|
@ -290,7 +321,7 @@ function list_all_entries(user)
|
|||
}
|
||||
}
|
||||
, false);
|
||||
req.open("POST", document.documentURI, false);
|
||||
req.open("POST", server_url, false);
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
req.send("get_passwords=1&user=" + user);
|
||||
}
|
||||
|
@ -328,17 +359,17 @@ function update_stats()
|
|||
}
|
||||
|
||||
// Remove all password without credentials
|
||||
function put_ciphered_credentials(passwords, masterkey)
|
||||
async function put_ciphered_credentials(passwords, masterkey)
|
||||
{
|
||||
for(var i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].generate_access_token(masterkey);
|
||||
await passwords[i].generate_access_token(masterkey);
|
||||
remove_password_server(current_user, passwords[i].ciphered_login, '');
|
||||
add_password_server(current_user, passwords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function get_ciphered_credentials(masterkey)
|
||||
async function get_ciphered_credentials(masterkey)
|
||||
{
|
||||
access_tokens = '';
|
||||
old_passwords = new Array();
|
||||
|
@ -349,17 +380,21 @@ function get_ciphered_credentials(masterkey)
|
|||
if (passwords[i].ciphered_login.length)
|
||||
{
|
||||
if (!passwords[i].access_token.length)
|
||||
old_passwords.push(passwords[i]);
|
||||
{
|
||||
res = await passwords[i].decrypt(masterkey);
|
||||
if(res)
|
||||
old_passwords.push(passwords[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
passwords[i].shadow_login_to_access_token(masterkey);
|
||||
await passwords[i].shadow_login_to_access_token(masterkey);
|
||||
if (access_tokens.length) access_tokens += ",";
|
||||
access_tokens += passwords[i].access_token;
|
||||
}
|
||||
|
||||
if (old_passwords.length)
|
||||
put_ciphered_credentials(old_passwords, masterkey);
|
||||
await put_ciphered_credentials(old_passwords, masterkey);
|
||||
|
||||
if (!access_tokens.length)
|
||||
return;
|
||||
|
@ -380,21 +415,21 @@ function get_ciphered_credentials(masterkey)
|
|||
}
|
||||
}
|
||||
}, false);
|
||||
req.open("POST", document.documentURI, false);
|
||||
req.open("POST", server_url, false);
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
req.send("get_secure_passwords=1&user=" + user + "&access_tokens=" + access_tokens);
|
||||
req.send("get_secure_passwords=1&user=" + current_user + "&access_tokens=" + access_tokens);
|
||||
}
|
||||
|
||||
function change_master_key(warning_unciphered)
|
||||
async function change_master_key(warning_unciphered)
|
||||
{
|
||||
var nb_unciphered = 0;
|
||||
|
||||
if (current_mkey.length && use_shadow_logins)
|
||||
get_ciphered_credentials(current_mkey);
|
||||
if (current_mkey && use_shadow_logins)
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].decrypt(current_mkey))
|
||||
if (await passwords[i].decrypt(current_mkey))
|
||||
nb_unciphered++;
|
||||
}
|
||||
|
||||
|
@ -534,7 +569,7 @@ function update_master_key(warning_unciphered)
|
|||
addon_address = document.getElementById("addon_address");
|
||||
addon_address.removeAllChilds();
|
||||
|
||||
addon_address.appendChild(document.createTextNode("Current addon address is : " + document.documentURI + current_user));
|
||||
addon_address.appendChild(document.createTextNode("Current addon address is : " + server_url + current_user));
|
||||
|
||||
warning_unciphered = false;
|
||||
}
|
||||
|
@ -543,11 +578,16 @@ function update_master_key(warning_unciphered)
|
|||
|
||||
if (current_mkey != "")
|
||||
{
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].reset_master_key();
|
||||
}
|
||||
current_mkey = derive_mkey(current_user, current_mkey);
|
||||
startClearTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
current_mkey = null;
|
||||
// Disable warning on empty master key (clear passwords from others)
|
||||
warning_unciphered = false;
|
||||
stopClearTimer();
|
||||
|
@ -577,14 +617,14 @@ function add_password_server(user, pentry)
|
|||
else
|
||||
alert(resp);
|
||||
}, false);
|
||||
req.open("POST", document.documentURI, false);
|
||||
req.open("POST", server_url, false);
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
req.send("add_entry=1&user=" + user + "&login=" + pentry.ciphered_login + "&password=" + pentry.ciphered_password + "&shadow_login=" + pentry.shadow_login + "&salt=" + pentry.salt + "&access_token=" + pentry.access_token);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
||||
async function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
||||
{
|
||||
var ret = null;
|
||||
|
||||
|
@ -613,7 +653,7 @@ function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
|||
}
|
||||
|
||||
if (derive_masterkey)
|
||||
mkey = derive_mkey(current_user, mkey);
|
||||
mkey = derive_mkey(user, mkey);
|
||||
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
|
@ -632,7 +672,7 @@ function construct_pentry(user, url, password, login, mkey, derive_masterkey)
|
|||
pentry.clear_url = url;
|
||||
pentry.clear_login = login;
|
||||
pentry.clear_password = password;
|
||||
pentry.encrypt(mkey);
|
||||
await pentry.encrypt(mkey);
|
||||
|
||||
return pentry;
|
||||
}
|
||||
|
@ -649,7 +689,7 @@ function remove_password_server(user, login, access_token)
|
|||
else
|
||||
alert(resp);
|
||||
}, false);
|
||||
req.open("POST", document.documentURI, false);
|
||||
req.open("POST", server_url, false);
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
req.send("delete_entry=1&user=" + user + "&login=" + login + "&access_token=" + access_token);
|
||||
|
||||
|
@ -679,26 +719,33 @@ function add_password()
|
|||
mkey = inputs[i].value;
|
||||
}
|
||||
|
||||
pentry = construct_pentry(current_user, url, password, login, mkey, true)
|
||||
construct_pentry(current_user, url, password, login, mkey, true).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return false;
|
||||
|
||||
if (pentry == null) return;
|
||||
res = add_password_server(current_user, pentry);
|
||||
|
||||
res = add_password_server(current_user, pentry);
|
||||
if (!res) return false;
|
||||
|
||||
if (!res) return false;
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
passwords[i].reset_master_key();
|
||||
}
|
||||
|
||||
current_mkey = pentry.masterkey;
|
||||
|
||||
passwords.push(pentry);
|
||||
passwords.push(pentry);
|
||||
|
||||
change_master_key(false);
|
||||
current_mkey = pentry.masterkey;
|
||||
change_master_key(false);
|
||||
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("type") == "text" ||
|
||||
inputs[i].getAttribute("type") == "password")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("type") == "text" ||
|
||||
inputs[i].getAttribute("type") == "password")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
|
||||
startClearTimer();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -735,7 +782,8 @@ function delete_entry(entry_number)
|
|||
var found = -1;
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].ciphered_login == ciphered_login.getAttribute("login"))
|
||||
if (passwords[i].ciphered_login == ciphered_login.getAttribute("login") ||
|
||||
passwords[i].shadow_login == ciphered_login.getAttribute("login"))
|
||||
{
|
||||
found = i;
|
||||
break;
|
||||
|
@ -814,23 +862,24 @@ function update_entry(entry_number)
|
|||
if(!confirm("Are you sure want to update this entry ?"))
|
||||
return;
|
||||
|
||||
pentry = construct_pentry(current_user, url, password, login, current_mkey, false);
|
||||
construct_pentry(current_user, url, password, login, current_mkey, false).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return;
|
||||
|
||||
if (pentry == null) return;
|
||||
ok = remove_password_server(current_user, passwords[found].ciphered_login, passwords[found].access_token);
|
||||
if (!ok) return;
|
||||
|
||||
ok = remove_password_server(current_user, passwords[found].ciphered_login, passwords[found].access_token);
|
||||
if (!ok) return;
|
||||
ok = add_password_server(current_user, pentry);
|
||||
if (!ok) return;
|
||||
|
||||
ok = add_password_server(current_user, pentry);
|
||||
if (!ok) return;
|
||||
passwords[found] = pentry;
|
||||
ciphered_login.setAttribute("login", pentry.ciphered_login);
|
||||
|
||||
passwords[found] = pentry;
|
||||
ciphered_login.setAttribute("login", pentry.ciphered_login);
|
||||
|
||||
alert("Entry updated");
|
||||
alert("Entry updated");
|
||||
});
|
||||
}
|
||||
|
||||
function update_masterkey()
|
||||
async function update_masterkey()
|
||||
{
|
||||
var url = "";
|
||||
var login = "";
|
||||
|
@ -851,12 +900,15 @@ 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++)
|
||||
{
|
||||
if (passwords[i].decrypt(oldmkey))
|
||||
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);
|
||||
if (!ok)
|
||||
|
@ -865,7 +917,11 @@ function update_masterkey()
|
|||
break;
|
||||
}
|
||||
|
||||
passwords[i].encrypt(current_mkey);
|
||||
if (use_shadow_logins)
|
||||
await passwords[i].generate_access_token(current_mkey);
|
||||
|
||||
global_iv = new_global_iv;
|
||||
await passwords[i].encrypt(current_mkey);
|
||||
ok = add_password_server(current_user, passwords[i]);
|
||||
|
||||
if (!ok)
|
||||
|
@ -904,7 +960,7 @@ function export_database()
|
|||
|
||||
if (text_link != null) window.URL.revokeObjectURL(text_link);
|
||||
|
||||
text = "<passwords user=\"" + current_user + "\" addon_address=\"" + document.documentURI + current_user + "\">\n";
|
||||
text = "<passwords user=\"" + current_user + "\" addon_address=\"" + server_url + current_user + "\">\n";
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (!passwords[i].unciphered) continue;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
220
server/resources/misc.js
Normal file
220
server/resources/misc.js
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
201
server/tests/test.js
Normal file
201
server/tests/test.js
Normal file
|
@ -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<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].unciphered)
|
||||
nb_unciphered++;
|
||||
}
|
||||
return nb_unciphered;
|
||||
}
|
||||
|
||||
function find_password(passwords, login)
|
||||
{
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].clear_login == login)
|
||||
return passwords[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function decrypt_passwords(passwords, masterkey)
|
||||
{
|
||||
var nb_unciphered = 0;
|
||||
|
||||
for(nb_unciphered = 0, i=0;i<passwords.length; i++)
|
||||
{
|
||||
res = await passwords[i].decrypt(current_mkey);
|
||||
if (res == true)
|
||||
nb_unciphered++;
|
||||
}
|
||||
|
||||
return nb_unciphered;
|
||||
}
|
||||
|
||||
QUnit.test( "Testbench", async function( assert ) {
|
||||
assert.ok( passwords == null, "Initial passwords null" );
|
||||
list_all_entries(current_user);
|
||||
assert.ok( passwords != null, "Retrieved passwords" );
|
||||
assert.equal( passwords.length, 8, "8 passwords retrieved" );
|
||||
var shadow_logins = 0;
|
||||
for(i=0;i<passwords.length; i++)
|
||||
if (passwords[i].shadow_login.length)
|
||||
shadow_logins++;
|
||||
assert.ok( shadow_logins == 2, "2 shadow logins" );
|
||||
|
||||
current_mkey = derive_mkey(current_user, "test");
|
||||
var nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal( nb_unciphered, 1, "test #1 decrypted" );
|
||||
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
res = nb_unciphered_passwords(passwords);
|
||||
assert.equal(res, 1, "No more passwords unciphered with shadow logins");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "test2");
|
||||
|
||||
// Get ciphered credentials for "test2"
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
res = nb_unciphered_passwords(passwords);
|
||||
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Shadow logins OK");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "test16");
|
||||
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Test16 OK");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "test17");
|
||||
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Test17 OK");
|
||||
|
||||
// V8
|
||||
current_mkey = derive_mkey(current_user, "testv8");
|
||||
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Testv8 OK");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "testv8_2");
|
||||
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 0, "Testv8_2 without shadow login");
|
||||
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Testv8_2 OK");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "testv8_16");
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Testv8_16 OK");
|
||||
|
||||
current_mkey = derive_mkey(current_user, "testv8_17");
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Testv8_17 OK");
|
||||
|
||||
nb_unciphered = nb_unciphered_passwords(passwords);
|
||||
assert.equal(nb_unciphered, 8, "All passwords unciphered");
|
||||
|
||||
password = find_password(passwords, "testv8");
|
||||
var ok = remove_password_server(current_user, passwords[i].ciphered_login,
|
||||
passwords[i].access_token);
|
||||
assert.equal(ok, true, "Remove OK");
|
||||
alert(passwords[i].ciphered_login);
|
||||
ok = remove_password_server(current_user, passwords[i].ciphered_login,
|
||||
passwords[i].access_token);
|
||||
assert.equal(ok, true, "Double remove OK");
|
||||
|
||||
password = find_password(passwords, "testv8_2");
|
||||
ok = remove_password_server(current_user, passwords[i].ciphered_login,
|
||||
"");
|
||||
assert.equal(ok, false, "Remove without access token OK");
|
||||
|
||||
ok = remove_password_server(current_user, passwords[i].ciphered_login,
|
||||
"AAAAAAAAAAAAAAAA");
|
||||
assert.equal(ok, false, "Remove Bad access token");
|
||||
|
||||
|
||||
res = await construct_pentry(current_user, "testv8_new", "testv8_new", "testv8_new", "testv8_new", true).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return false;
|
||||
|
||||
return add_password_server(current_user, pentry);
|
||||
});
|
||||
|
||||
assert.equal(res, false, "Add without access token OK");
|
||||
|
||||
use_shadow_logins = true;
|
||||
|
||||
res = await construct_pentry(current_user, "testv8_new", "testv8_new", "testv8_new", "testv8_new", true).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return false;
|
||||
|
||||
return add_password_server(current_user, pentry);
|
||||
});
|
||||
|
||||
assert.equal(res, true, "Add with access token OK");
|
||||
res = add_password_server(current_user, passwords[passwords.length-1]);
|
||||
assert.equal(res, false, "Double add OK");
|
||||
|
||||
res = await construct_pentry(current_user, "testv8_new2", "testv8_new2", "testv8_new2", "testv8_new2", true).then(
|
||||
function (pentry) {
|
||||
if (pentry == null) return false;
|
||||
|
||||
pentry.shadow_login = "AAA";
|
||||
return add_password_server(current_user, pentry);
|
||||
});
|
||||
|
||||
assert.equal(res, false, "Add with truncated shadow login OK");
|
||||
|
||||
password = find_password(passwords, "test16");
|
||||
ok = remove_password_server(current_user, password.ciphered_login,
|
||||
password.access_token);
|
||||
assert.equal(ok, true, "Remove v7");
|
||||
|
||||
password = find_password(passwords, "test16");
|
||||
ok = remove_password_server(current_user, password.ciphered_login,
|
||||
"AAAAAAAAAAAAAAAA");
|
||||
assert.equal(ok, true, "Remove v7 bad access token");
|
||||
});
|
||||
|
||||
QUnit.test( "Updated database", async function( assert ) {
|
||||
passwords = null;
|
||||
list_all_entries(current_user);
|
||||
assert.ok( passwords != null, "Passed!" );
|
||||
assert.equal( passwords.length, 7, "7 passwords retrieved" );
|
||||
|
||||
current_mkey = derive_mkey(current_user, "testv8");
|
||||
var nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal( nb_unciphered, 0, "Password removed" );
|
||||
|
||||
current_mkey = derive_mkey(current_user, "testv8_new");
|
||||
await get_ciphered_credentials(current_mkey);
|
||||
nb_unciphered = await decrypt_passwords(passwords, current_user);
|
||||
assert.equal(nb_unciphered, 1, "Password added");
|
||||
});
|
17
server/tests/tests.html
Normal file
17
server/tests/tests.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>QUnit Example</title>
|
||||
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.3.0.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
<script src="https://code.jquery.com/qunit/qunit-2.3.0.js"></script>
|
||||
<script src="../resources/misc.js"></script>
|
||||
<script src="../resources/gpass.js"></script>
|
||||
<script src="test.js"></script>
|
||||
</body>
|
||||
</html>
|
BIN
server/tests/users/test-v7/gpass.bdd
Normal file
BIN
server/tests/users/test-v7/gpass.bdd
Normal file
Binary file not shown.
BIN
server/tests/users/test-v7/gpass.bdd.init
Normal file
BIN
server/tests/users/test-v7/gpass.bdd.init
Normal file
Binary file not shown.
|
@ -1,3 +1,4 @@
|
|||
<?php
|
||||
/*
|
||||
Copyright (C) 2013 Grégory Soutadé
|
||||
|
||||
|
@ -17,30 +18,6 @@
|
|||
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue
Block a user