diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index b7a491714..0bf990412 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -14,14 +14,14 @@ define([ '/customize/application_config.js', '/customize/pages.js', '/bower_components/nthen/index.js', + '/common/invitation.js', '/bower_components/scrypt-async/scrypt-async.js', 'css!/customize/fonts/cptools/style.css', '/bower_components/croppie/croppie.min.js', 'css!/bower_components/croppie/croppie.css', ], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard, - Messages, AppConfig, Pages, NThen) { - var Scrypt = window.scrypt; + Messages, AppConfig, Pages, NThen, InviteInner) { var UIElements = {}; // Configure MediaTags to use our local viewer @@ -1710,6 +1710,10 @@ define([ $(linkError).text(Messages.team_inviteLinkErrorName).show(); // XXX return true; } + + var seeds = InviteInner.deriveSeeds(hashData.key); + var salt = InviteInner.deriveSalt(pw, AppConfig.loginSalt); + var bytes64; NThen(function (waitFor) { $(linkForm).hide(); @@ -1717,17 +1721,9 @@ define([ $nav.find('button.cp-teams-invite-create').prop('disabled', 'disabled'); setTimeout(waitFor(), 150); }).nThen(function (waitFor) { - // Scrypt - Scrypt(hashData.key, - (pw || '') + (AppConfig.loginSalt || ''), // salt - 8, // memoryCost (n) - 1024, // block size parameter (r) - 192, // dkLen - 200, // interruptStep - waitFor(function (_bytes) { - bytes64 = _bytes; - }), - 'base64'); // format, could be 'base64' + InviteInner.deriveBytes(seeds.scrypt, salt, waitFor(function (_bytes) { + bytes64 = _bytes; + })); }).nThen(function (waitFor) { $(linkSpinText).text('Add invite link to team'); // XXX module.execCommand('CREATE_INVITE_LINK', { @@ -1737,6 +1733,7 @@ define([ bytes64: bytes64, hash: hash, teamId: config.teamId, + seeds: seeds, }, waitFor(function (obj) { if (obj && obj.error) { waitFor.abort(); diff --git a/www/common/invitation.js b/www/common/invitation.js index 0bad771de..e26a39597 100644 --- a/www/common/invitation.js +++ b/www/common/invitation.js @@ -1,57 +1,57 @@ (function () { -var factory = function (Hash, Nacl/*, Util, Cred, nThen */) { +var factory = function (Hash, Nacl, Scrypt/*, Util, Cred, nThen */) { var Invite = {}; - /* XXX ansuz - inner invitation components + Invite.deriveSeeds = function (seed) { + // take the hash of the provided seed + var u8_seed = Nacl.hash(Nacl.util.decodeBase64(seed)); - * create an invitation link - * derive secrets from a v2 link and password - * split hash into two preseeds - * preseed1 => preview hash - * scrypt(scrypt_seed) => b64_bytes - * preview an invitation link - * get preview hash from invitation link - * decrypt an invitation link - * (slowly) get b64_bytes from hash + // hash the first half again for scrypt's input + var subseed1 = Nacl.hash(u8_seed.subarray(0, 32)); + // hash the remainder for the invite content + var subseed2 = Nacl.hash(u8_seed.subarray(32)); - */ - - Invite.deriveSeeds = function (key) { - var seeds = {}; - -/* - var preview_channel; - var preview_cryptKey; -*/ - var preview_secrets; - (function () { - var b64_seed = key; - if (typeof(b64_seed) !== 'string') { - return console.error('invite seed is not a string'); - } + return { + scrypt: Nacl.util.encodeBase64(subseed1), + preview: Nacl.util.encodeBase64(subseed2), + }; + }; - var u8_seed = Nacl.util.decodeBase64(b64_seed); - var step1 = Nacl.hash(u8_seed); - seeds.scrypt = Nacl.util.encodeBase64(step1.subarray(0, 32)); + Invite.derivePreviewHash = function (seeds) { + return '#/2/invite/view/' + + Nacl.util.encodeBase64(seeds.preview.slice(0, 18)).replace('/', '-') + + '/'; + }; - var preview_hash = '#/2/invite/view/' + - Nacl.util.encodeBase64(step1.subarray(32, 50)).replace('/', '-') - + '/'; + Invite.derivePreviewSecrets = function (seeds) { + return Hash.getSecrets('pad', Invite.derivePreviewHash(seeds)); + }; - preview_secrets = Hash.getSecrets('pad', preview_hash); - }()); - return seeds; + Invite.deriveSalt = function (password, instance_salt) { + return (password || '') + (instance_salt || ''); }; // seed => bytes64 - Invite.deriveBytes = function (scrypt_seed, cb) { - // XXX do scrypt stuff... - cb = cb; + Invite.deriveBytes = function (scrypt_seed, salt, cb) { + Scrypt(scrypt_seed, + salt, + 8, // memoryCost (n) + 1024, // block size parameter (r) + 192, // dkLen + 200, // interruptStep + cb, + 'base64'); // format, could be 'base64' + }; + + Invite.getPreviewContent = function (seeds, cb) { + var secrets = Invite.derivePreviewSecrets(seeds); + secrets = secrets; + cb("NOT_IMPLEMENTED"); // XXX cryptget }; - Invite.derivePreviewHash = function (preview_seed) { - preview_seed = preview_seed; + // XXX remember to pin invites... + Invite.setPreviewContent = function (seeds, cb) { + cb = cb; }; return Invite; @@ -60,19 +60,15 @@ var factory = function (Hash, Nacl/*, Util, Cred, nThen */) { module.exports = factory( require("../common-hash"), require("tweetnacl/nacl-fast"), - require("../common-util"), - require("../common-credential.js"), - require("nthen") + require("scrypt-async") ); } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { define([ '/common/common-hash.js', - '/common/common-util.js', - '/common/common-credential.js', - '/bower_components/nthen/index.js', '/bower_components/tweetnacl/nacl-fast.min.js', - ], function (Hash, Util, Cred, nThen) { - return factory(Hash, window.nacl, Util, Cred, nThen); + '/bower_components/scrypt_async/scrypt-async.min.js', + ], function (Hash /*, Nacl, Scrypt */) { + return factory(Hash, window.nacl, window.Scrypt); }); } }()); diff --git a/www/teams/inner.js b/www/teams/inner.js index 6b8fb35e7..21883609d 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -18,7 +18,6 @@ define([ '/common/invitation.js', '/customize/messages.js', - '/bower_components/scrypt-async/scrypt-async.min.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/teams/app-team.less', @@ -45,7 +44,6 @@ define([ var APP = {}; var driveAPP = {}; //var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName; - var Scrypt = window.scrypt; var copyObjectValue = function (objRef, objToCopy) { for (var k in objRef) { delete objRef[k]; } @@ -1055,33 +1053,45 @@ define([ var $div = $(div); $div.empty(); var bytes64; + nThen(function (waitFor) { - $div.append(h('div', [ - h('i.fa.fa-spin.fa-spinner'), - h('span', 'Scrypt...') // XXX - ])); - setTimeout(waitFor(), 150); + // XXX show something while we're waiting for the invite preview content + waitFor = waitFor; }).nThen(function (waitFor) { - // XXX ansuz InviteInner.deriveBytes - Scrypt(seeds.scrypt, - (pw || '') + (AppConfig.loginSalt || ''), // salt - 8, // memoryCost (n) - 1024, // block size parameter (r) - 192, // dkLen - 200, // interruptStep - waitFor(function (_bytes) { - bytes64 = _bytes; - }), - 'base64'); // format, could be 'base64' - }).nThen(function (waitFor) { - APP.module.execCommand('GET_LINK_DATA', { - bytes64: bytes64, - hash: hash, - password: pw, - }, waitFor(function () { - $div.empty(); - // TODO - // Accept/decline/decide later UI + InviteInner.getPreviewContent(seeds, waitFor(function (err, json) { + json = json; // XXX {message: "", author: "", ???} + if (err) { + // XXX handle errors + } + // XXX show invite preview content + + var button = h('button', 'XXX'); + button.onclick = function () { + nThen(function (waitFor) { + $div.append(h('div', [ + h('i.fa.fa-spin.fa-spinner'), + h('span', 'Scrypt...') // XXX + ])); + setTimeout(waitFor(), 150); + }).nThen(function (waitFor) { + var salt = InviteInner.deriveSalt(pw, AppConfig.loginSalt); + InviteInner.deriveBytes(seeds.scrypt, salt, waitFor(function (bytes) { + bytes64 = bytes; + })); + }).nThen(function (waitFor) { + APP.module.execCommand('GET_LINK_DATA', { + bytes64: bytes64, + hash: hash, + password: pw, + }, waitFor(function () { + $div.empty(); + // TODO + // Accept/decline/decide later UI + })); + }); + }; + + $div.append(button); })); }); };