diff --git a/www/login/index.html b/www/login/index.html
index 04fc47400..b4143365d 100644
--- a/www/login/index.html
+++ b/www/login/index.html
@@ -12,16 +12,43 @@
width: 100%;
box-sizing: border-box;
}
+ body {
+ width: 80vw;
+ min-width: 1000px;
+ margin: auto;
+ }
+ div.box, div.logout {
+ width: 50%;
+ border: 1px solid black;
+ padding: 15px;
+ display: none;
+ }
+ input[type="text"], input[type="password"] {
+ width: 80%;
+ }
+
+ #confirm { display: none; }
-
-
-
+
+
diff --git a/www/login/main.js b/www/login/main.js
index d24829503..239ca9c6b 100644
--- a/www/login/main.js
+++ b/www/login/main.js
@@ -3,10 +3,11 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptpad-common.js',
+ '/login/credential.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'/bower_components/scrypt-async/scrypt-async.min.js',
'/bower_components/jquery/dist/jquery.min.js',
-], function (Config, Listmap, Crypto, Cryptpad) {
+], function (Config, Listmap, Crypto, Cryptpad, Cred) {
var $ = window.jQuery;
var Scrypt = window.scrypt;
var Nacl = window.nacl;
@@ -20,10 +21,6 @@ define([
Crypto: Crypto,
};
- var print = function (S, t) {
- $('body').append($('<' + (t || 'p') + '>').text(S));
- };
-
var hashFromCreds = function (username, password, len, cb) {
Scrypt(password,
username,
@@ -35,94 +32,122 @@ define([
undefined); // format, could be 'base64'
};
- var authenticated = function (password, next) {
- console.log("Authenticated!");
- var secret = {};
-
- secret.channel = password.slice(0, 32);
- secret.key = password.slice(32, 48);
- secret.junk = password.slice(48, 64); // consider reordering things
- secret.curve = password.slice(64, 96);
- secret.ed = password.slice(96, 128);
+ var Events = APP.Events = {};
+ var alreadyExists = Events.alreadyExists = function () {
+ Cryptpad.alert("user account already exists.");
+ };
+ var mismatchedPasswords = Events.mismatchedPasswords = function () {
+ Cryptpad.alert("passwords don't match!");
+ };
- print(JSON.stringify(secret, null, 2), 'pre');
+ var useBytes = function (bytes, opt) {
+ opt = opt || {};
+ if (opt.remember) {
+ console.log("user would like to stay logged in");
+ } else {
+ console.log("user would like to be forgotten");
+ }
- var config = {
- websocketURL: Config.websocketURL,
- channel: secret.channel,
- data: {},
- crypto: Crypto.createEncryptor(secret.key),
- loglevel: 0,
+ var entropy = {
+ used: 0,
};
- console.log("creating proxy!");
- var rt = module.rt = Listmap.create(config);
+ // crypto hygeine
+ var consume = function (n) {
+ // explode if you run out of bytes
+ if (entropy.used + n > bytes.length) {
+ throw new Error('exceeded available entropy');
+ }
+ if (typeof(n) !== 'number') { throw new Error('expected a number'); }
+ if (n <= 0) {
+ throw new Error('expected to consume a positive number of bytes');
+ }
- next(rt.proxy, function () {
- Cryptpad.log("Ready!");
- });
- };
+ // grab an unused slice of the entropy
+ var A = bytes.slice(entropy.used, entropy.used + n);
- var useBytes = function (bytes) {
- var firstSeed = bytes.slice(0, 18);
- var secondSeed = bytes.slice(18, 35);
+ // account for the bytes you used so you don't reuse bytes
+ entropy.used += n;
- var remainder = bytes.slice(34);
+ //console.info("%s bytes of entropy remaining", bytes.length - entropy.used);
+ return A;
+ };
- var seed = {};
- seed.keys = Crypto.createEditCryptor(null, firstSeed);
+ // consume 18 bytes of entropy for your encryption key
+ var encryptionSeed = consume(18);
+ // 16 bytes for a deterministic channel key
+ var channelSeed = consume(16);
+ // 32 bytes for a curve key
+ var curveSeed = consume(32);
+ // 32 more for a signing key
+ var edSeed = consume(32);
- seed.keys.editKeyStr = seed.keys.editKeyStr.replace(/\//g, '-');
+ var seed = {};
+ var keys = seed.keys = Crypto.createEditCryptor(null, encryptionSeed);
- seed.channel = Cryptpad.uint8ArrayToHex(secondSeed);
+ // 24 bytes of base64
+ keys.editKeyStr = keys.editKeyStr.replace(/\//g, '-');
- console.log(seed);
+ // 32 bytes of hex
+ seed.channel = Cryptpad.uint8ArrayToHex(channelSeed);
var channelHex = seed.channel;
- var channel64 = Cryptpad.hexToBase64(channelHex);
-
- console.log(seed.keys.editKeyStr);
- seed.editHash = Cryptpad.getEditHashFromKeys(channelHex, seed.keys.editKeyStr);
+ if (channelHex.length !== 32) {
+ throw new Error('invalid channel id');
+ }
- var secret = Cryptpad.getSecrets(seed.editHash);
- console.log(secret);
+ var channel64 = Cryptpad.hexToBase64(channelHex);
- console.log(seed.editHash);
+ seed.editHash = Cryptpad.getEditHashFromKeys(channelHex, keys.editKeyStr);
+ //console.log("edithash: %s", seed.editHash);
- //return;
+ var secret = Cryptpad.getSecrets(seed.editHash);
var config = {
websocketURL: Cryptpad.getWebsocketURL(),
channel: channelHex,
data: {},
- validateKey: seed.keys.validateKey || undefined,
- readOnly: seed.keys && !seed.keys.editKeyStr,
+ validateKey: keys.validateKey, // derived validation key
crypto: Crypto.createEncryptor(seed.keys),
};
var rt = APP.rt = Listmap.create(config);
rt.proxy.on('create', function (info) {
- console.log('created');
- //console.log(info);
+ console.log("loading user profile");
})
.on('ready', function (info) {
+ console.log(info);
console.log('ready');
- //console.log(info);
-
var proxy = rt.proxy;
+/* if the user is registering, we expect that the userDoc will be empty
+*/
+ if (opt.register) {
+ if (Object.keys(proxy).length) {
+ alreadyExists();
+ }
+ }
+
var now = +(new Date());
if (!proxy.atime) {
console.log("first time visiting!");
proxy.atime = now;
+
+ var name = proxy['cryptpad.username'] = opt.name;
+ console.log("setting name to %s", name);
} else {
console.log("last visit was %ss ago", (now - proxy.atime) / 1000);
proxy.atime = now;
}
- console.log(proxy);
+ var userHash = '/1/edit/' + [channel64, keys.editKeyStr].join('/');
+
+ console.log("remembering your userhash");
+ Cryptpad.login(userHash, opt.remember);
+ //console.log(userHash);
+ //console.log(proxy);
})
.on('disconnect', function (info) {
console.log('disconnected');
@@ -130,38 +155,81 @@ define([
});
};
- var isValidUsername = function (name) {
- return !!name;
- };
+ var $warning = $('#warning');
+ var $login = $('#login');
+ var $username = $('#username');
+ var $password = $('#password');
+ var $confirm = $('#confirm');
+ var $remember = $('#remember');
- var isValidPassword = function (passwd) {
- return !!passwd;
+ var revealLogin = function () {
+ $('.box').slideDown();
};
- var $username = $('#username');
- var $password = $('#password');
+ var $logoutBox = $('div.logout');
+ var $logout = $('#logout').click(function () {
+ Cryptpad.logout(function () {
+ // noop?
+ $logout.slideUp();
+ revealLogin();
+ });
+ });
- 0 && hashFromCreds('ansuz', 'pewpewpew', 128, useBytes);
+ var $register = $('#register').click(function () {
+ if (!$register.length) { return; }
+ var e = $register[0];
+ if (e.checked) {
+ $confirm.slideDown();
+ $login.text(Cryptpad.Messages._getKey('login_register'));
+ }
+ else {
+ $confirm.slideUp();
+ $login.text(Cryptpad.Messages._getKey('login_login'));
+ }
+ });
+
+ var resetUI = function () {
+ $username.val("");
+ $password.val("");
+ $confirm.val("");
+ $remember[0].checked = false;
+ $register[0].checked = false;
+ };
- $('#login').click(function () {
+ if (Cryptpad.getUserHash()) {
+ //Cryptpad.alert("You are already logged in!");
+ $logoutBox.slideDown();
+ } else {
+ revealLogin();
+ }
+
+ $login.click(function () {
var uname = $username.val();
var passwd = $password.val();
+ var confirm = $confirm.val();
+ var remember = $remember[0].checked;
+ var register = $register[0].checked;
- if (!isValidUsername(uname)) {
+ if (!Cred.isValidUsername(uname)) {
return void Cryptpad.alert('invalid username');
}
-
- if (!isValidPassword(passwd)) {
+ if (!Cred.isValidPassword(passwd)) {
return void Cryptpad.alert('invalid password');
}
+ if (register && !Cred.passwordsMatch(passwd, confirm)) {
+ return mismatchedPasswords();
+ }
- $username.val("");
- $password.val("");
+ resetUI();
- // we need 18 bytes for the regular crypto
+ // consume 128 bytes, to be divided later
+ // we can safely increase this size, but we don't need much right now
hashFromCreds(uname, passwd, 128, function (bytes) {
- //console.log(bytes);
- useBytes(bytes);
+ useBytes(bytes, {
+ remember: remember,
+ register: register,
+ name: uname,
+ });
});
});
});