Seperated common crypto operations to common file and made common toolbar used for both pad and spreadsheet

pull/1/head
Caleb James DeLisle 10 years ago
parent e039e90a24
commit 0e44b10aeb

@ -0,0 +1,77 @@
define([
'/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
var Nacl = window.nacl;
var module = { exports: {} };
var encryptStr = function (str, key) {
var array = Nacl.util.decodeUTF8(str);
var nonce = Nacl.randomBytes(24);
var packed = Nacl.secretbox(array, nonce, key);
if (!packed) { throw new Error(); }
return Nacl.util.encodeBase64(nonce) + "|" + Nacl.util.encodeBase64(packed);
};
var decryptStr = function (str, key) {
var arr = str.split('|');
if (arr.length !== 2) { throw new Error(); }
var nonce = Nacl.util.decodeBase64(arr[0]);
var packed = Nacl.util.decodeBase64(arr[1]);
var unpacked = Nacl.secretbox.open(packed, nonce, key);
if (!unpacked) { throw new Error(); }
return Nacl.util.encodeUTF8(unpacked);
};
// this is crap because of bencoding messages... it should go away....
var splitMessage = function (msg, sending) {
var idx = 0;
var nl;
for (var i = ((sending) ? 0 : 1); i < 3; i++) {
nl = msg.indexOf(':',idx);
idx = nl + Number(msg.substring(idx,nl)) + 1;
}
return [ msg.substring(0,idx), msg.substring(msg.indexOf(':',idx) + 1) ];
};
var encrypt = module.exports.encrypt = function (msg, key) {
var spl = splitMessage(msg, true);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
json[1] = encryptStr(JSON.stringify(json[1]), key);
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var decrypt = module.exports.decrypt = function (msg, key) {
var spl = splitMessage(msg, false);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
if (typeof(json[1]) !== 'string') { throw new Error(); }
json[1] = JSON.parse(decryptStr(json[1], key));
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var parseKey = module.exports.parseKey = function (str) {
var array = Nacl.util.decodeBase64(str);
var hash = Nacl.hash(array);
var lk = hash.subarray(32);
return {
lookupKey: lk,
cryptKey: hash.subarray(0,32),
channel: Nacl.util.encodeBase64(lk).substring(0,10)
};
};
var rand64 = module.exports.rand64 = function (bytes) {
return Nacl.util.encodeBase64(Nacl.randomBytes(bytes));
};
var genKey = module.exports.genKey = function () {
return rand64(18);
};
return module.exports;
});

@ -1,4 +1,6 @@
var Toolbar = function ($, container, Messages, myUserName, realtime) { define([
'/common/messages.js'
], function (Messages) {
/** Id of the element for getting debug info. */ /** Id of the element for getting debug info. */
var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link'; var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link';
@ -22,15 +24,15 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
return 'rtwysiwyg-uid-' + String(Math.random()).substring(2); return 'rtwysiwyg-uid-' + String(Math.random()).substring(2);
}; };
var createRealtimeToolbar = function (container) { var createRealtimeToolbar = function ($container) {
var id = uid(); var id = uid();
$(container).prepend( $container.prepend(
'<div class="' + TOOLBAR_CLS + '" id="' + id + '">' + '<div class="' + TOOLBAR_CLS + '" id="' + id + '">' +
'<div class="rtwysiwyg-toolbar-leftside"></div>' + '<div class="rtwysiwyg-toolbar-leftside"></div>' +
'<div class="rtwysiwyg-toolbar-rightside"></div>' + '<div class="rtwysiwyg-toolbar-rightside"></div>' +
'</div>' '</div>'
); );
var toolbar = $('#'+id); var toolbar = $container.find('#'+id);
toolbar.append([ toolbar.append([
'<style>', '<style>',
'.' + TOOLBAR_CLS + ' {', '.' + TOOLBAR_CLS + ' {',
@ -77,10 +79,10 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
return toolbar; return toolbar;
}; };
var createSpinner = function (container) { var createSpinner = function ($container) {
var id = uid(); var id = uid();
$(container).append('<div class="rtwysiwyg-spinner" id="'+id+'"></div>'); $container.append('<div class="rtwysiwyg-spinner" id="'+id+'"></div>');
return $('#'+id)[0]; return $container.find('#'+id)[0];
}; };
var kickSpinner = function (spinnerElement, reversed) { var kickSpinner = function (spinnerElement, reversed) {
@ -93,10 +95,10 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
}, SPINNER_DISAPPEAR_TIME); }, SPINNER_DISAPPEAR_TIME);
}; };
var createUserList = function (container) { var createUserList = function ($container) {
var id = uid(); var id = uid();
$(container).prepend('<div class="' + USER_LIST_CLS + '" id="'+id+'"></div>'); $container.prepend('<div class="' + USER_LIST_CLS + '" id="'+id+'"></div>');
return $('#'+id)[0]; return $container.find('#'+id)[0];
}; };
var updateUserList = function (myUserName, listElement, userList) { var updateUserList = function (myUserName, listElement, userList) {
@ -115,10 +117,10 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
} }
}; };
var createLagElement = function (container) { var createLagElement = function ($container) {
var id = uid(); var id = uid();
$(container).append('<div class="' + LAG_ELEM_CLS + '" id="'+id+'"></div>'); $container.append('<div class="' + LAG_ELEM_CLS + '" id="'+id+'"></div>');
return $('#'+id)[0]; return $container.find('#'+id)[0];
}; };
var checkLag = function (realtime, lagElement) { var checkLag = function (realtime, lagElement) {
@ -133,8 +135,8 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
lagElement.textContent = lagMsg; lagElement.textContent = lagMsg;
}; };
var create = function ($container, myUserName, realtime) {
var toolbar = createRealtimeToolbar(container); var toolbar = createRealtimeToolbar($container);
var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside')); var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside'));
var spinner = createSpinner(toolbar.find('.rtwysiwyg-toolbar-rightside')); var spinner = createSpinner(toolbar.find('.rtwysiwyg-toolbar-rightside'));
var lagElement = createLagElement(toolbar.find('.rtwysiwyg-toolbar-rightside')); var lagElement = createLagElement(toolbar.find('.rtwysiwyg-toolbar-rightside'));
@ -152,7 +154,8 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
}; };
realtime.onPatch(ks); realtime.onPatch(ks);
realtime.onMessage(ks); // Try to filter out non-patch messages, doesn't have to be perfect this is just the spinner
realtime.onMessage(function (msg) { if (msg.indexOf(':[2,') > -1) { ks(); } });
setInterval(function () { setInterval(function () {
if (!connected) { return; } if (!connected) { return; }
@ -160,6 +163,11 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
}, 3000); }, 3000);
return { return {
failed: function () {
connected = false;
userListElement.textContent = '';
lagElement.textContent = '';
},
reconnecting: function () { reconnecting: function () {
connected = false; connected = false;
userListElement.textContent = Messages.reconnecting; userListElement.textContent = Messages.reconnecting;
@ -170,3 +178,6 @@ var Toolbar = function ($, container, Messages, myUserName, realtime) {
} }
}; };
}; };
return { create: create };
});

@ -2,39 +2,22 @@ define([
'/api/config?cb=' + Math.random().toString(16).substring(2), '/api/config?cb=' + Math.random().toString(16).substring(2),
'/pad/realtime-wysiwyg.js', '/pad/realtime-wysiwyg.js',
'/common/messages.js', '/common/messages.js',
'/common/crypto.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/ckeditor/ckeditor.js', '/bower_components/ckeditor/ckeditor.js',
'/bower_components/tweetnacl/nacl-fast.min.js', ], function (Config, RTWysiwyg, Messages, Crypto) {
], function (Config, RTWysiwyg, Messages) {
var Ckeditor = window.CKEDITOR; var Ckeditor = window.CKEDITOR;
var Nacl = window.nacl; var $ = window.jQuery;
var $ = jQuery;
var module = { exports: {} };
var parseKey = function (str) {
var array = Nacl.util.decodeBase64(str);
var hash = Nacl.hash(array);
return { lookupKey: hash.subarray(32), cryptKey: hash.subarray(0,32) };
};
var genKey = function () {
return Nacl.util.encodeBase64(Nacl.randomBytes(18));
};
var userName = function () {
return Nacl.util.encodeBase64(Nacl.randomBytes(8));
};
$(function () { $(function () {
$(window).on('hashchange', function() { $(window).on('hashchange', function() {
window.location.reload(); window.location.reload();
}); });
if (window.location.href.indexOf('#') === -1) { if (window.location.href.indexOf('#') === -1) {
window.location.href = window.location.href + '#' + genKey(); window.location.href = window.location.href + '#' + Crypto.genKey();
return; return;
} }
var key = parseKey(window.location.hash.substring(1)); var key = Crypto.parseKey(window.location.hash.substring(1));
var editor = Ckeditor.replace('editor1', { var editor = Ckeditor.replace('editor1', {
removeButtons: 'Source,Maximize', removeButtons: 'Source,Maximize',
// This plugin inserts html crap into the document which is not part of the document // This plugin inserts html crap into the document which is not part of the document
@ -48,8 +31,8 @@ define([
var rtw = var rtw =
RTWysiwyg.start(Config.websocketURL, RTWysiwyg.start(Config.websocketURL,
userName(), Crypto.rand64(8),
Nacl.util.encodeBase64(key.lookupKey).substring(0,10), key.channel,
key.cryptKey); key.cryptKey);
editor.on('change', function () { rtw.onEvent(); }); editor.on('change', function () { rtw.onEvent(); });
}); });

@ -19,13 +19,13 @@ define([
'/pad/errorbox.js', '/pad/errorbox.js',
'/common/messages.js', '/common/messages.js',
'/bower_components/reconnectingWebsocket/reconnecting-websocket.js', '/bower_components/reconnectingWebsocket/reconnecting-websocket.js',
'/common/crypto.js',
'/common/toolbar.js',
'/pad/rangy.js', '/pad/rangy.js',
'/common/chainpad.js', '/common/chainpad.js',
'/common/otaml.js', '/common/otaml.js',
'/common/toolbar.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/tweetnacl/nacl-fast.min.js' ], function (HTMLPatcher, ErrorBox, Messages, ReconnectingWebSocket, Crypto, Toolbar) {
], function (HTMLPatcher, ErrorBox, Messages, ReconnectingWebSocket) {
window.ErrorBox = ErrorBox; window.ErrorBox = ErrorBox;
@ -34,7 +34,6 @@ window.ErrorBox = ErrorBox;
Rangy.init(); Rangy.init();
var ChainPad = window.ChainPad; var ChainPad = window.ChainPad;
var Otaml = window.Otaml; var Otaml = window.Otaml;
var Nacl = window.nacl;
var PARANOIA = true; var PARANOIA = true;
@ -49,23 +48,6 @@ window.ErrorBox = ErrorBox;
/** Maximum number of milliseconds of lag before we fail the connection. */ /** Maximum number of milliseconds of lag before we fail the connection. */
var MAX_LAG_BEFORE_DISCONNECT = 20000; var MAX_LAG_BEFORE_DISCONNECT = 20000;
/** Id of the element for getting debug info. */
var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link';
/** Id of the div containing the user list. */
var USER_LIST_CLS = 'rtwysiwyg-user-list';
/** Id of the div containing the lag info. */
var LAG_ELEM_CLS = 'rtwysiwyg-lag';
/** The toolbar class which contains the user list, debug link and lag. */
var TOOLBAR_CLS = 'rtwysiwyg-toolbar';
/** Key in the localStore which indicates realtime activity should be disallowed. */
var LOCALSTORAGE_DISALLOW = 'rtwysiwyg-disallow';
var SPINNER_DISAPPEAR_TIME = 3000;
// ------------------ Trapping Keyboard Events ---------------------- // // ------------------ Trapping Keyboard Events ---------------------- //
var bindEvents = function (element, events, callback, unbind) { var bindEvents = function (element, events, callback, unbind) {
@ -108,9 +90,8 @@ window.ErrorBox = ErrorBox;
var abort = function (socket, realtime) { var abort = function (socket, realtime) {
realtime.abort(); realtime.abort();
realtime.toolbar.failed();
try { socket._socket.close(); } catch (e) { } try { socket._socket.close(); } catch (e) { }
$('.'+USER_LIST_CLS).text(Messages.disconnected);
$('.'+LAG_ELEM_CLS).text("");
}; };
var createDebugInfo = function (cause, realtime, docHTML, allMessages) { var createDebugInfo = function (cause, realtime, docHTML, allMessages) {
@ -266,55 +247,6 @@ window.ErrorBox = ErrorBox;
return out; return out;
}; };
var encryptStr = function (str, key) {
var array = Nacl.util.decodeUTF8(str);
var nonce = Nacl.randomBytes(24);
var packed = Nacl.secretbox(array, nonce, key);
if (!packed) { throw new Error(); }
return Nacl.util.encodeBase64(nonce) + "|" + Nacl.util.encodeBase64(packed);
};
var decryptStr = function (str, key) {
var arr = str.split('|');
if (arr.length !== 2) { throw new Error(); }
var nonce = Nacl.util.decodeBase64(arr[0]);
var packed = Nacl.util.decodeBase64(arr[1]);
var unpacked = Nacl.secretbox.open(packed, nonce, key);
if (!unpacked) { throw new Error(); }
return Nacl.util.encodeUTF8(unpacked);
};
// this is crap because of bencoding messages... it should go away....
var splitMessage = function (msg, sending) {
var idx = 0;
var nl;
for (var i = ((sending) ? 0 : 1); i < 3; i++) {
nl = msg.indexOf(':',idx);
idx = nl + Number(msg.substring(idx,nl)) + 1;
}
return [ msg.substring(0,idx), msg.substring(msg.indexOf(':',idx) + 1) ];
};
var encrypt = function (msg, key) {
var spl = splitMessage(msg, true);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
json[1] = encryptStr(JSON.stringify(json[1]), key);
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var decrypt = function (msg, key) {
var spl = splitMessage(msg, false);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
if (typeof(json[1]) !== 'string') { throw new Error(); }
json[1] = JSON.parse(decryptStr(json[1], key));
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var start = module.exports.start = function (websocketUrl, userName, channel, cryptKey) var start = module.exports.start = function (websocketUrl, userName, channel, cryptKey)
{ {
var passwd = 'y'; var passwd = 'y';
@ -370,7 +302,8 @@ console.log(new Error().stack);
getDocHTML(doc), getDocHTML(doc),
{ transformFunction: Otaml.transform }); { transformFunction: Otaml.transform });
var toolbar = Toolbar($, $('#cke_1_toolbox'), Messages, userName, realtime); var toolbar = realtime.toolbar =
Toolbar.create($('#cke_1_toolbox'), userName, realtime);
onEvent = function () { onEvent = function () {
if (isErrorState) { return; } if (isErrorState) { return; }
@ -417,7 +350,7 @@ console.log(new Error().stack);
socket.onMessage.push(function (evt) { socket.onMessage.push(function (evt) {
if (isErrorState) { return; } if (isErrorState) { return; }
var message = decrypt(evt.data, cryptKey); var message = Crypto.decrypt(evt.data, cryptKey);
allMessages.push(message); allMessages.push(message);
if (!initializing) { if (!initializing) {
if (PARANOIA) { onEvent(); } if (PARANOIA) { onEvent(); }
@ -427,7 +360,7 @@ console.log(new Error().stack);
}); });
realtime.onMessage(function (message) { realtime.onMessage(function (message) {
if (isErrorState) { return; } if (isErrorState) { return; }
message = encrypt(message, cryptKey); message = Crypto.encrypt(message, cryptKey);
try { try {
socket.send(message); socket.send(message);
} catch (e) { } catch (e) {

@ -1,34 +1,18 @@
define([ define([
'/api/config?cb=' + Math.random().toString(16).substring(2), '/api/config?cb=' + Math.random().toString(16).substring(2),
'/common/messages.js', '/common/messages.js',
'/common/crypto.js',
'/common/toolbar.js', '/common/toolbar.js',
'/common/chainpad.js', '/common/chainpad.js',
'/bower_components/jquery/dist/jquery.min.js', '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'/common/otaml.js' '/common/otaml.js'
], function (Config, Messages) { ], function (Config, Messages, Crypto, Toolbar) {
var Nacl = window.nacl;
var $ = jQuery; var $ = jQuery;
var ChainPad = window.ChainPad; var ChainPad = window.ChainPad;
var Otaml = window.Otaml; var Otaml = window.Otaml;
var Toolbar = window.Toolbar;
var module = { exports: {} }; var module = { exports: {} };
var parseKey = function (str) {
var array = Nacl.util.decodeBase64(str);
var hash = Nacl.hash(array);
return { lookupKey: hash.subarray(32), cryptKey: hash.subarray(0,32) };
};
var genKey = function () {
return Nacl.util.encodeBase64(Nacl.randomBytes(18));
};
var userName = function () {
return 'Other-' + Nacl.util.encodeBase64(Nacl.randomBytes(8));
};
var sheetToJson = function (ifrWindow) { var sheetToJson = function (ifrWindow) {
var xx = ifrWindow.sh[0].jS var xx = ifrWindow.sh[0].jS
var m = []; var m = [];
@ -65,55 +49,6 @@ define([
} }
}; };
var encryptStr = function (str, key) {
var array = Nacl.util.decodeUTF8(str);
var nonce = Nacl.randomBytes(24);
var packed = Nacl.secretbox(array, nonce, key);
if (!packed) { throw new Error(); }
return Nacl.util.encodeBase64(nonce) + "|" + Nacl.util.encodeBase64(packed);
};
var decryptStr = function (str, key) {
var arr = str.split('|');
if (arr.length !== 2) { throw new Error(); }
var nonce = Nacl.util.decodeBase64(arr[0]);
var packed = Nacl.util.decodeBase64(arr[1]);
var unpacked = Nacl.secretbox.open(packed, nonce, key);
if (!unpacked) { throw new Error(); }
return Nacl.util.encodeUTF8(unpacked);
};
// this is crap because of bencoding messages... it should go away....
var splitMessage = function (msg, sending) {
var idx = 0;
var nl;
for (var i = ((sending) ? 0 : 1); i < 3; i++) {
nl = msg.indexOf(':',idx);
idx = nl + Number(msg.substring(idx,nl)) + 1;
}
return [ msg.substring(0,idx), msg.substring(msg.indexOf(':',idx) + 1) ];
};
var encrypt = function (msg, key) {
var spl = splitMessage(msg, true);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
json[1] = encryptStr(JSON.stringify(json[1]), key);
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var decrypt = function (msg, key) {
var spl = splitMessage(msg, false);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
if (typeof(json[1]) !== 'string') { throw new Error(); }
json[1] = JSON.parse(decryptStr(json[1], key));
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
};
var applyChange = function(ctx, oldval, newval) { var applyChange = function(ctx, oldval, newval) {
if (oldval === newval) return; if (oldval === newval) return;
@ -139,7 +74,7 @@ define([
$(function () { $(function () {
if (window.location.href.indexOf('#') === -1) { if (window.location.href.indexOf('#') === -1) {
window.location.href = window.location.href + '#' + genKey(); window.location.href = window.location.href + '#' + Crypto.genKey();
} }
$(window).on('hashchange', function() { $(window).on('hashchange', function() {
window.location.reload(); window.location.reload();
@ -168,20 +103,19 @@ define([
}, 0); }, 0);
}; };
var key = parseKey(window.location.hash.substring(1)); var key = Crypto.parseKey(window.location.hash.substring(1));
var channel = Nacl.util.encodeBase64(key.lookupKey).substring(0,10); var myUserName = Crypto.rand64(8);
var myUserName = userName();
var socket = new WebSocket(Config.websocketURL); var socket = new WebSocket(Config.websocketURL);
socket.onopen = function () { socket.onopen = function () {
var realtime = ChainPad.create( var realtime = ChainPad.create(
myUserName, 'x', channel, '', { transformFunction: Otaml.transform }); myUserName, 'x', key.channel, '', { transformFunction: Otaml.transform });
socket.onmessage = function (evt) { socket.onmessage = function (evt) {
var message = decrypt(evt.data, key.cryptKey); var message = Crypto.decrypt(evt.data, key.cryptKey);
realtime.message(message); realtime.message(message);
}; };
realtime.onMessage(function (message) { realtime.onMessage(function (message) {
message = encrypt(message, key.cryptKey); message = Crypto.encrypt(message, key.cryptKey);
try { try {
socket.send(message); socket.send(message);
} catch (e) { } catch (e) {
@ -193,7 +127,7 @@ define([
realtime.onPatch(function () { realtimeEvent(realtime); }); realtime.onPatch(function () { realtimeEvent(realtime); });
ifrw.$('.jSTitle').html(''); ifrw.$('.jSTitle').html('');
Toolbar(ifrw.$, ifrw.$('.jSTitle'), Messages, myUserName, realtime); Toolbar.create(ifrw.$('.jSTitle'), myUserName, realtime);
realtime.start(); realtime.start();
}; };

Loading…
Cancel
Save