extract all code for encoding and decoding hashes
This commit is contained in:
parent
fdd2716ad5
commit
e618483395
@ -3,6 +3,7 @@ define([
|
|||||||
'/customize/messages.js?app=' + window.location.pathname.split('/').filter(function (x) { return x; }).join('.'),
|
'/customize/messages.js?app=' + window.location.pathname.split('/').filter(function (x) { return x; }).join('.'),
|
||||||
'/common/fsStore.js',
|
'/common/fsStore.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
|
'/common/hash.js',
|
||||||
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
|
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
|
||||||
'/bower_components/alertifyjs/dist/js/alertify.js',
|
'/bower_components/alertifyjs/dist/js/alertify.js',
|
||||||
@ -12,7 +13,7 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
|
|
||||||
'/bower_components/jquery/dist/jquery.min.js',
|
'/bower_components/jquery/dist/jquery.min.js',
|
||||||
], function (Config, Messages, Store, Util, Crypto, Alertify, Clipboard, Pinpad, AppConfig) {
|
], function (Config, Messages, Store, Util, Hash, Crypto, Alertify, Clipboard, Pinpad, AppConfig) {
|
||||||
/* This file exposes functionality which is specific to Cryptpad, but not to
|
/* This file exposes functionality which is specific to Cryptpad, but not to
|
||||||
any particular pad type. This includes functions for committing metadata
|
any particular pad type. This includes functions for committing metadata
|
||||||
about pads to your local storage for future use and improved usability.
|
about pads to your local storage for future use and improved usability.
|
||||||
@ -41,6 +42,7 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
var store;
|
var store;
|
||||||
var rpc;
|
var rpc;
|
||||||
|
|
||||||
|
// import common utilities for export
|
||||||
var find = common.find = Util.find;
|
var find = common.find = Util.find;
|
||||||
var fixHTML = common.fixHTML = Util.fixHTML;
|
var fixHTML = common.fixHTML = Util.fixHTML;
|
||||||
var hexToBase64 = common.hexToBase64 = Util.hexToBase64;
|
var hexToBase64 = common.hexToBase64 = Util.hexToBase64;
|
||||||
@ -51,6 +53,22 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
var getHash = common.getHash = Util.getHash;
|
var getHash = common.getHash = Util.getHash;
|
||||||
var fixFileName = common.fixFileName = Util.fixFileName;
|
var fixFileName = common.fixFileName = Util.fixFileName;
|
||||||
|
|
||||||
|
// import hash utilities for export
|
||||||
|
var createRandomHash = Hash.createRandomHash;
|
||||||
|
var parsePadUrl = common.parsePadUrl = Hash.parsePadUrl;
|
||||||
|
var isNotStrongestStored = common.isNotStrongestStored = Hash.isNotStrongestStored;
|
||||||
|
var hrefToHexChannelId = common.hrefToHexChannelId = Hash.hrefToHexChannelId;
|
||||||
|
var parseHash = common.parseHash = Hash.parseHash;
|
||||||
|
var getRelativeHref = common.getRelativeHref = Hash.getRelativeHref;
|
||||||
|
|
||||||
|
common.getEditHashFromKeys = Hash.getEditHashFromKeys;
|
||||||
|
common.getViewHashFromKeys = Hash.getViewHashFromKeys;
|
||||||
|
common.getSecrets = Hash.getSecrets;
|
||||||
|
common.getHashes = Hash.getHashes;
|
||||||
|
common.createChannelId = Hash.createChannelId;
|
||||||
|
common.findWeaker = Hash.findWeaker;
|
||||||
|
common.findStronger = Hash.findStronger;
|
||||||
|
|
||||||
var getStore = common.getStore = function () {
|
var getStore = common.getStore = function () {
|
||||||
if (store) { return store; }
|
if (store) { return store; }
|
||||||
throw new Error("Store is not ready!");
|
throw new Error("Store is not ready!");
|
||||||
@ -197,134 +215,6 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseHash = common.parseHash = function (hash) {
|
|
||||||
var parsed = {};
|
|
||||||
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
|
||||||
// Old hash
|
|
||||||
parsed.channel = hash.slice(0, 32);
|
|
||||||
parsed.key = hash.slice(32);
|
|
||||||
parsed.version = 0;
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
var hashArr = hash.split('/');
|
|
||||||
if (hashArr[1] && hashArr[1] === '1') {
|
|
||||||
parsed.version = 1;
|
|
||||||
parsed.mode = hashArr[2];
|
|
||||||
parsed.channel = hashArr[3];
|
|
||||||
parsed.key = hashArr[4];
|
|
||||||
parsed.present = hashArr[5] && hashArr[5] === 'present';
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// CRYPTO
|
|
||||||
var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) {
|
|
||||||
if (typeof keys === 'string') {
|
|
||||||
return chanKey + keys;
|
|
||||||
}
|
|
||||||
if (!keys.editKeyStr) { return; }
|
|
||||||
return '/1/edit/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.editKeyStr);
|
|
||||||
};
|
|
||||||
// CRYPTO
|
|
||||||
var getViewHashFromKeys = common.getViewHashFromKeys = function (chanKey, keys) {
|
|
||||||
if (typeof keys === 'string') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return '/1/view/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.viewKeyStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns all needed keys for a realtime channel
|
|
||||||
* - no argument: use the URL hash or create one if it doesn't exist
|
|
||||||
* - secretHash provided: use secretHash to find the keys
|
|
||||||
*/
|
|
||||||
// CRYPTO
|
|
||||||
var getSecrets = common.getSecrets = function (secretHash) {
|
|
||||||
var secret = {};
|
|
||||||
var generate = function () {
|
|
||||||
secret.keys = Crypto.createEditCryptor();
|
|
||||||
secret.key = Crypto.createEditCryptor().editKeyStr;
|
|
||||||
};
|
|
||||||
if (!secretHash && !/#/.test(window.location.href)) {
|
|
||||||
generate();
|
|
||||||
return secret;
|
|
||||||
} else {
|
|
||||||
var hash = secretHash || window.location.hash.slice(1);
|
|
||||||
if (hash.length === 0) {
|
|
||||||
generate();
|
|
||||||
return secret;
|
|
||||||
}
|
|
||||||
// old hash system : #{hexChanKey}{cryptKey}
|
|
||||||
// new hash system : #/{hashVersion}/{b64ChanKey}/{cryptKey}
|
|
||||||
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
|
||||||
// Old hash
|
|
||||||
secret.channel = hash.slice(0, 32);
|
|
||||||
secret.key = hash.slice(32);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// New hash
|
|
||||||
var hashArray = hash.split('/');
|
|
||||||
if (hashArray.length < 4) {
|
|
||||||
common.alert("Unable to parse the key");
|
|
||||||
throw new Error("Unable to parse the key");
|
|
||||||
}
|
|
||||||
var version = hashArray[1];
|
|
||||||
if (version === "1") {
|
|
||||||
var mode = hashArray[2];
|
|
||||||
if (mode === 'edit') {
|
|
||||||
secret.channel = base64ToHex(hashArray[3]);
|
|
||||||
var keys = Crypto.createEditCryptor(hashArray[4].replace(/-/g, '/'));
|
|
||||||
secret.keys = keys;
|
|
||||||
secret.key = keys.editKeyStr;
|
|
||||||
if (secret.channel.length !== 32 || secret.key.length !== 24) {
|
|
||||||
common.alert("The channel key and/or the encryption key is invalid");
|
|
||||||
throw new Error("The channel key and/or the encryption key is invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mode === 'view') {
|
|
||||||
secret.channel = base64ToHex(hashArray[3]);
|
|
||||||
secret.keys = Crypto.createViewCryptor(hashArray[4].replace(/-/g, '/'));
|
|
||||||
if (secret.channel.length !== 32) {
|
|
||||||
common.alert("The channel key is invalid");
|
|
||||||
throw new Error("The channel key is invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return secret;
|
|
||||||
};
|
|
||||||
|
|
||||||
// CRYPTO
|
|
||||||
var getHashes = common.getHashes = function (channel, secret) {
|
|
||||||
var hashes = {};
|
|
||||||
if (secret.keys.editKeyStr) {
|
|
||||||
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
|
|
||||||
}
|
|
||||||
if (secret.keys.viewKeyStr) {
|
|
||||||
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
|
|
||||||
}
|
|
||||||
return hashes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// CRYPTO
|
|
||||||
var createChannelId = common.createChannelId = function () {
|
|
||||||
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
|
|
||||||
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
|
|
||||||
throw new Error('channel ids must consist of 32 hex characters');
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
// CRYPTO
|
|
||||||
var createRandomHash = common.createRandomHash = function () {
|
|
||||||
// 16 byte channel Id
|
|
||||||
var channelId = hexToBase64(createChannelId());
|
|
||||||
// 18 byte encryption key
|
|
||||||
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
|
|
||||||
return '/1/edit/' + [channelId, key].join('/');
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* localStorage formatting
|
* localStorage formatting
|
||||||
@ -407,36 +297,6 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var getRelativeHref = common.getRelativeHref = function (href) {
|
|
||||||
if (!href) { return; }
|
|
||||||
if (href.indexOf('#') === -1) { return; }
|
|
||||||
var parsed = common.parsePadUrl(href);
|
|
||||||
return '/' + parsed.type + '/#' + parsed.hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
var parsePadUrl = common.parsePadUrl = function (href) {
|
|
||||||
var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
|
|
||||||
|
|
||||||
var ret = {};
|
|
||||||
|
|
||||||
if (!href) { return ret; }
|
|
||||||
|
|
||||||
if (!/^https*:\/\//.test(href)) {
|
|
||||||
var idx = href.indexOf('/#');
|
|
||||||
ret.type = href.slice(1, idx);
|
|
||||||
ret.hash = href.slice(idx + 2);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash = href.replace(patt, function (a, domain, type, hash) {
|
|
||||||
ret.domain = domain;
|
|
||||||
ret.type = type;
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
ret.hash = hash.replace(/#/g, '');
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
var isNameAvailable = function (title, parsed, pads) {
|
var isNameAvailable = function (title, parsed, pads) {
|
||||||
return !pads.some(function (pad) {
|
return !pads.some(function (pad) {
|
||||||
// another pad is already using that title
|
// another pad is already using that title
|
||||||
@ -619,55 +479,6 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// STORAGE
|
|
||||||
var findWeaker = common.findWeaker = function (href, recents) {
|
|
||||||
var rHref = href || getRelativeHref(window.location.href);
|
|
||||||
var parsed = parsePadUrl(rHref);
|
|
||||||
if (!parsed.hash) { return false; }
|
|
||||||
var weaker;
|
|
||||||
recents.some(function (pad) {
|
|
||||||
var p = parsePadUrl(pad.href);
|
|
||||||
if (p.type !== parsed.type) { return; } // Not the same type
|
|
||||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
|
||||||
var pHash = parseHash(p.hash);
|
|
||||||
var parsedHash = parseHash(parsed.hash);
|
|
||||||
if (!parsedHash || !pHash) { return; }
|
|
||||||
if (pHash.version !== parsedHash.version) { return; }
|
|
||||||
if (pHash.channel !== parsedHash.channel) { return; }
|
|
||||||
if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
|
|
||||||
weaker = pad.href;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
return weaker;
|
|
||||||
};
|
|
||||||
var findStronger = common.findStronger = function (href, recents) {
|
|
||||||
var rHref = href || getRelativeHref(window.location.href);
|
|
||||||
var parsed = parsePadUrl(rHref);
|
|
||||||
if (!parsed.hash) { return false; }
|
|
||||||
var stronger;
|
|
||||||
recents.some(function (pad) {
|
|
||||||
var p = parsePadUrl(pad.href);
|
|
||||||
if (p.type !== parsed.type) { return; } // Not the same type
|
|
||||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
|
||||||
var pHash = parseHash(p.hash);
|
|
||||||
var parsedHash = parseHash(parsed.hash);
|
|
||||||
if (!parsedHash || !pHash) { return; }
|
|
||||||
if (pHash.version !== parsedHash.version) { return; }
|
|
||||||
if (pHash.channel !== parsedHash.channel) { return; }
|
|
||||||
if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
|
|
||||||
stronger = pad.href;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
return stronger;
|
|
||||||
};
|
|
||||||
var isNotStrongestStored = common.isNotStrongestStored = function (href, recents) {
|
|
||||||
return findStronger(href, recents);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO integrate pinning
|
// TODO integrate pinning
|
||||||
var setPadTitle = common.setPadTitle = function (name, cb) {
|
var setPadTitle = common.setPadTitle = function (name, cb) {
|
||||||
var href = window.location.href;
|
var href = window.location.href;
|
||||||
@ -746,7 +557,7 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
var getPadTitle = common.getPadTitle = function (cb) {
|
var getPadTitle = common.getPadTitle = function (cb) {
|
||||||
var href = window.location.href;
|
var href = window.location.href;
|
||||||
var parsed = parsePadUrl(window.location.href);
|
var parsed = parsePadUrl(window.location.href);
|
||||||
var hashSlice = window.location.hash.slice(1,9);
|
var hashSlice = window.location.hash.slice(1,9); // TODO remove
|
||||||
var title = '';
|
var title = '';
|
||||||
|
|
||||||
getRecentPads(function (err, pads) {
|
getRecentPads(function (err, pads) {
|
||||||
@ -862,27 +673,6 @@ load pinpad dynamically only after you know that it will be needed */
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var hrefToHexChannelId = common.hrefToHexChannelId = function (href) {
|
|
||||||
var parsed = common.parsePadUrl(href);
|
|
||||||
if (!parsed || !parsed.hash) { return; }
|
|
||||||
|
|
||||||
parsed = common.parseHash(parsed.hash);
|
|
||||||
|
|
||||||
if (parsed.version === 0) {
|
|
||||||
return parsed.channel;
|
|
||||||
} else if (parsed.version !== 1) {
|
|
||||||
console.error("parsed href had no version");
|
|
||||||
console.error(parsed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel = parsed.channel;
|
|
||||||
if (!channel) { return; }
|
|
||||||
|
|
||||||
var hex = common.base64ToHex(channel);
|
|
||||||
return hex;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getUserChannelList = common.getUserChannelList = function () {
|
var getUserChannelList = common.getUserChannelList = function () {
|
||||||
var store = common.getStore();
|
var store = common.getStore();
|
||||||
var proxy = store.getProxy();
|
var proxy = store.getProxy();
|
||||||
|
|||||||
236
www/common/hash.js
Normal file
236
www/common/hash.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
define([
|
||||||
|
'/Hash/Hash-util.js',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
], function (Util, Crypto) {
|
||||||
|
var Hash = {};
|
||||||
|
|
||||||
|
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
||||||
|
var hexToBase64 = Util.hexToBase64;
|
||||||
|
var base64ToHex = Util.base64ToHex;
|
||||||
|
|
||||||
|
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
|
||||||
|
if (typeof keys === 'string') {
|
||||||
|
return chanKey + keys;
|
||||||
|
}
|
||||||
|
if (!keys.editKeyStr) { return; }
|
||||||
|
return '/1/edit/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.editKeyStr);
|
||||||
|
};
|
||||||
|
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
|
||||||
|
if (typeof keys === 'string') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return '/1/view/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.viewKeyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var parsePadUrl = Hash.parsePadUrl = function (href) {
|
||||||
|
var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
|
||||||
|
|
||||||
|
var ret = {};
|
||||||
|
|
||||||
|
if (!href) { return ret; }
|
||||||
|
|
||||||
|
if (!/^https*:\/\//.test(href)) {
|
||||||
|
var idx = href.indexOf('/#');
|
||||||
|
ret.type = href.slice(1, idx);
|
||||||
|
ret.hash = href.slice(idx + 2);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = href.replace(patt, function (a, domain, type, hash) {
|
||||||
|
ret.domain = domain;
|
||||||
|
ret.type = type;
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
ret.hash = hash.replace(/#/g, '');
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getRelativeHref = Hash.getRelativeHref = function (href) {
|
||||||
|
if (!href) { return; }
|
||||||
|
if (href.indexOf('#') === -1) { return; }
|
||||||
|
var parsed = parsePadUrl(href);
|
||||||
|
return '/' + parsed.type + '/#' + parsed.hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns all needed keys for a realtime channel
|
||||||
|
* - no argument: use the URL hash or create one if it doesn't exist
|
||||||
|
* - secretHash provided: use secretHash to find the keys
|
||||||
|
*/
|
||||||
|
var getSecrets = Hash.getSecrets = function (secretHash) {
|
||||||
|
var secret = {};
|
||||||
|
var generate = function () {
|
||||||
|
secret.keys = Crypto.createEditCryptor();
|
||||||
|
secret.key = Crypto.createEditCryptor().editKeyStr;
|
||||||
|
};
|
||||||
|
if (!secretHash && !/#/.test(window.location.href)) {
|
||||||
|
generate();
|
||||||
|
return secret;
|
||||||
|
} else {
|
||||||
|
var hash = secretHash || window.location.hash.slice(1);
|
||||||
|
if (hash.length === 0) {
|
||||||
|
generate();
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
// old hash system : #{hexChanKey}{cryptKey}
|
||||||
|
// new hash system : #/{hashVersion}/{b64ChanKey}/{cryptKey}
|
||||||
|
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
||||||
|
// Old hash
|
||||||
|
secret.channel = hash.slice(0, 32);
|
||||||
|
secret.key = hash.slice(32);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// New hash
|
||||||
|
var hashArray = hash.split('/');
|
||||||
|
if (hashArray.length < 4) {
|
||||||
|
Hash.alert("Unable to parse the key");
|
||||||
|
throw new Error("Unable to parse the key");
|
||||||
|
}
|
||||||
|
var version = hashArray[1];
|
||||||
|
if (version === "1") {
|
||||||
|
var mode = hashArray[2];
|
||||||
|
if (mode === 'edit') {
|
||||||
|
secret.channel = base64ToHex(hashArray[3]);
|
||||||
|
var keys = Crypto.createEditCryptor(hashArray[4].replace(/-/g, '/'));
|
||||||
|
secret.keys = keys;
|
||||||
|
secret.key = keys.editKeyStr;
|
||||||
|
if (secret.channel.length !== 32 || secret.key.length !== 24) {
|
||||||
|
Hash.alert("The channel key and/or the encryption key is invalid");
|
||||||
|
throw new Error("The channel key and/or the encryption key is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode === 'view') {
|
||||||
|
secret.channel = base64ToHex(hashArray[3]);
|
||||||
|
secret.keys = Crypto.createViewCryptor(hashArray[4].replace(/-/g, '/'));
|
||||||
|
if (secret.channel.length !== 32) {
|
||||||
|
Hash.alert("The channel key is invalid");
|
||||||
|
throw new Error("The channel key is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return secret;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getHashes = Hash.getHashes = function (channel, secret) {
|
||||||
|
var hashes = {};
|
||||||
|
if (secret.keys.editKeyStr) {
|
||||||
|
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
|
||||||
|
}
|
||||||
|
if (secret.keys.viewKeyStr) {
|
||||||
|
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
|
||||||
|
}
|
||||||
|
return hashes;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createChannelId = Hash.createChannelId = function () {
|
||||||
|
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
|
||||||
|
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
|
||||||
|
throw new Error('channel ids must consist of 32 hex characters');
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createRandomHash = Hash.createRandomHash = function () {
|
||||||
|
// 16 byte channel Id
|
||||||
|
var channelId = Util.hexToBase64(createChannelId());
|
||||||
|
// 18 byte encryption key
|
||||||
|
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
|
||||||
|
return '/1/edit/' + [channelId, key].join('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
var parseHash = Hash.parseHash = function (hash) {
|
||||||
|
var parsed = {};
|
||||||
|
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
||||||
|
// Old hash
|
||||||
|
parsed.channel = hash.slice(0, 32);
|
||||||
|
parsed.key = hash.slice(32);
|
||||||
|
parsed.version = 0;
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
var hashArr = hash.split('/');
|
||||||
|
if (hashArr[1] && hashArr[1] === '1') {
|
||||||
|
parsed.version = 1;
|
||||||
|
parsed.mode = hashArr[2];
|
||||||
|
parsed.channel = hashArr[3];
|
||||||
|
parsed.key = hashArr[4];
|
||||||
|
parsed.present = hashArr[5] && hashArr[5] === 'present';
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// STORAGE
|
||||||
|
var findWeaker = Hash.findWeaker = function (href, recents) {
|
||||||
|
var rHref = href || getRelativeHref(window.location.href);
|
||||||
|
var parsed = parsePadUrl(rHref);
|
||||||
|
if (!parsed.hash) { return false; }
|
||||||
|
var weaker;
|
||||||
|
recents.some(function (pad) {
|
||||||
|
var p = parsePadUrl(pad.href);
|
||||||
|
if (p.type !== parsed.type) { return; } // Not the same type
|
||||||
|
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||||
|
var pHash = parseHash(p.hash);
|
||||||
|
var parsedHash = parseHash(parsed.hash);
|
||||||
|
if (!parsedHash || !pHash) { return; }
|
||||||
|
if (pHash.version !== parsedHash.version) { return; }
|
||||||
|
if (pHash.channel !== parsedHash.channel) { return; }
|
||||||
|
if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
|
||||||
|
weaker = pad.href;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
return weaker;
|
||||||
|
};
|
||||||
|
var findStronger = Hash.findStronger = function (href, recents) {
|
||||||
|
var rHref = href || getRelativeHref(window.location.href);
|
||||||
|
var parsed = parsePadUrl(rHref);
|
||||||
|
if (!parsed.hash) { return false; }
|
||||||
|
var stronger;
|
||||||
|
recents.some(function (pad) {
|
||||||
|
var p = parsePadUrl(pad.href);
|
||||||
|
if (p.type !== parsed.type) { return; } // Not the same type
|
||||||
|
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||||
|
var pHash = parseHash(p.hash);
|
||||||
|
var parsedHash = parseHash(parsed.hash);
|
||||||
|
if (!parsedHash || !pHash) { return; }
|
||||||
|
if (pHash.version !== parsedHash.version) { return; }
|
||||||
|
if (pHash.channel !== parsedHash.channel) { return; }
|
||||||
|
if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
|
||||||
|
stronger = pad.href;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
return stronger;
|
||||||
|
};
|
||||||
|
var isNotStrongestStored = Hash.isNotStrongestStored = function (href, recents) {
|
||||||
|
return findStronger(href, recents);
|
||||||
|
};
|
||||||
|
|
||||||
|
var hrefToHexChannelId = Hash.hrefToHexChannelId = function (href) {
|
||||||
|
var parsed = Hash.parsePadUrl(href);
|
||||||
|
if (!parsed || !parsed.hash) { return; }
|
||||||
|
|
||||||
|
parsed = Hash.parseHash(parsed.hash);
|
||||||
|
|
||||||
|
if (parsed.version === 0) {
|
||||||
|
return parsed.channel;
|
||||||
|
} else if (parsed.version !== 1) {
|
||||||
|
console.error("parsed href had no version");
|
||||||
|
console.error(parsed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel = parsed.channel;
|
||||||
|
if (!channel) { return; }
|
||||||
|
|
||||||
|
var hex = Hash.base64ToHex(channel);
|
||||||
|
return hex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return Hash;
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user