diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28e71bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +bower_components +dist \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..a927593 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,40 @@ +{ + "camelcase": false, + "curly": true, + "forin": false, + "latedef": "nofunc", + "newcap": false, + "noarg": true, + "nonew": true, + "quotmark": "single", + "undef": true, + "unused": "vars", + "strict": true, + "trailing": true, + "maxlen": 80, + + "eqnull": true, + "esnext": true, + "expr": true, + "globalstrict": true, + + "maxerr": 1000, + "regexdash": true, + "laxcomma": true, + "proto": true, + + "browser": true, + "devel": true, + "nonstandard": true, + "worker": true, + "bitwise": true, + + "-W078": true, + + "globals": { + "require" : false, + "module" : false, + "define" : false, + "global" : false + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..328faf1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 - Vincent Cassé + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5080c0f --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# Amaretti.js + +Amaretti.js is a library to encrypt and decrypt message into the browser. They use native implementation (WebCrypto APIs) when available, or SJCL library when not. + +## Getting started + +### Installation + +This library can be installed with npm or bower, as you prefer: + +```bash +bower install amaretti +``` + +```bash +npm install amaretti +``` + +### How to use it + +Just import the javascript file and require the library. Require system is included into amaretti library + +```html + + +var Amaretti = require('amaretti').init(); +``` + +### Generate a salt + +Salt are used into key generation and to randomize the encryption of a message. You can get a base64 salt using this `Amaretti.getSalt()` + +```javascript +Amaretti.getSalt().then(function(salt) { + // Manipulate your salt +}, function (error) { + // There was an error +}); +``` + +### Generate a key + +To encrypt or decrypt messages, you need to use a key. You can generate a key usable with a passphrase (like a password). Key generated is returned as base64. To randomize the generation, you need to give a salt and a hash algorithm + +```javascript +Amaretti.generateKey(passphrase, salt, hash).then(function(key) { + // Manipulate your key +}, function (error) { + // There was an error +}); +``` + * __passphrase__: is the passphrase used to encrypt or decrypt messages + * __salt__: is the salt, base64 encoded, used to randomize the key generator + * __hash__: is the name of algorithm used to hash the key. It could be _SHA-1_ or _SHA-256_ + +### Encrypt a message + +You can encrypt a message with your key. Amaretti use AES-GCM to encrypt data. To avoid brut-force attack agains the encrypted data, each data had to be encrypt with a different and random nonce. You can use a salt as nonce. Don't lose this nonce, you will need it to decrypt the message. + +```javascript +Amaretti.encrypt(key, message, nonce).then(function(encrypted) { + // Manipulate your encrypted message +}, function (error) { + // There was an error +}); +``` + * __key__: is the base64 used to encrypt message + * __message__: is the message to encrypt + * __nonce__: is a random value, in base64 format, use to avoid attacks + +### Decrypt a message + +```javascript +Amaretti..decrypt(key, encrypted, nonce).then(function(decrypted) { + // Manipulate your encrypted message +}, function (error) { + // There was an error +}); +``` + + * __key__: is the base64 used to encrypt message + * __encrypted: is the encrypted message to decrypt, in base64 format + * __nonce__: is a random value, in base64 format, use to avoid attacks + +## License + +MIT + +## How to contribute + +Hum ... on github :) + +### To build library + +```bash +npm install +bower install +brunch build +``` + +### To run tests + +```bash +npm run test +``` + +## Ideas for a roadmap + +* Return key and crypted data with JOSE standard (JWE and JWT) +* Check sha-256 for firefox and sha-1 for SJCL ito key generation diff --git a/app/amaretti.js b/app/amaretti.js new file mode 100644 index 0000000..d60878b --- /dev/null +++ b/app/amaretti.js @@ -0,0 +1,339 @@ +"use strict"; + +var encode = function(str) { + return new TextEncoder('utf-8').encode(str); +}; +var decode = function(buf) { + return new TextDecoder('utf-8').decode(new Uint8Array(buf)); +}; +/*var ab2str = function(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); +};*/ +function b64ToUint6 (nChr) { + + return nChr > 64 && nChr < 91 ? + nChr - 65 + : nChr > 96 && nChr < 123 ? + nChr - 71 + : nChr > 47 && nChr < 58 ? + nChr + 4 + : nChr === 43 ? + 62 + : nChr === 47 ? + 63 + : + 0; + +} + +function arrayBufferToBase64 (arrayBuffer) { + return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer))); +} +/** +* TODO add better way to use atob whe possible +*/ +//https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer +function base64ToArrayByte(sBase64, nBlocksSize) { + + var + sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, + nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); + + for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { + nMod4 = nInIdx & 3; + nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4); + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { + taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; + } + nUint24 = 0; + + } + } + + return taBytes; + +} +var arrayByteToBase64 = function(arrayByte) { + //http://stackoverflow.com/a/12713326 + var CHUNK_SZ = 0x8000; + var c = []; + for (var i = 0; i < arrayByte.length; i += CHUNK_SZ) { + c.push(String.fromCharCode.apply( + null, + arrayByte.subarray(i, i + CHUNK_SZ) + )); + } + return btoa(c.join('')); +}; + +var Amaretti = { + + authTagLength : 128, + paranoia: 10, + iterations: 5000, + enableNative: true, + + init: function (opts) { + this.authTagUsedSpace = this.authTagLength / 4; + sjcl.random.startCollectors(); + + if (opts) { + this.iterations = opts.iterations ? opts.iterations : 5000; + this.enableNative = opts.enableNative !== undefined ? opts.enableNative : true; + } + return this; + }, + + getCrypto: function() { + return window.crypto || window.msCrypto; + }, + + getSalt: function() { + var origSalt; + var salt; + return new Promise(function(resolve, reject) { + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().getRandomValues) { + try { + origSalt = Amaretti.getCrypto().getRandomValues(new Uint8Array(64)); + salt = arrayByteToBase64(origSalt); + resolve(salt); + } catch (exception) { + reject(exception.message); + } + return; + } + + if (sjcl.random.isReady(Amaretti.paranoia) === 0) { + reject('sjcl random is not ready'); + return; + } + try { + origSalt = sjcl.random.randomWords(8, Amaretti.paranoia); + salt = sjcl.codec.base64.fromBits(origSalt); + resolve(salt); + } catch (exception) { + reject(exception.message); + } + }); + }, + + generateKey: function(passphrase, salt, algo) { + if (!algo) { + return new Promise(function(resolve, reject) { + reject('Please add an algo'); + }); + } + + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.deriveKey && + Amaretti.getCrypto().subtle.exportKey) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + encode(passphrase), + { name: 'PBKDF2' }, + false, + ['deriveKey'] + ).then(function(baseKey) { + + var cryptoAlgo; + if (algo === 'SHA-1') { + cryptoAlgo = 'SHA-1'; + } else if (algo === 'SHA-256') { + throw 'SHA-256 is not yet available on firefox'; + } else { + throw 'Your algo is not available'; + } + + return Amaretti.getCrypto().subtle.deriveKey( + { + name: 'PBKDF2', + hash: cryptoAlgo, + salt: base64ToArrayByte(salt), + iterations: Amaretti.iterations + }, + baseKey, + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (key) { + return Amaretti.getCrypto().subtle.exportKey('raw', key) + .then(function (rawKey) { + return new Promise(function(resolve, reject) { + try { + resolve(arrayByteToBase64(new Uint8Array(rawKey))); + } catch (exception) { + reject(exception.message); + } + }); + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var sjclAlgo; + + if (algo === 'SHA-1') { + sjclAlgo = function (key) { + // todo sha-1 + var hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 ); + this.encrypt = function () { + return hasher.encrypt.apply( hasher, arguments ); + }; + }; + } else if (algo === 'SHA-256') { + sjclAlgo = function (key) { + var hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 ); + this.encrypt = function () { + return hasher.encrypt.apply( hasher, arguments ); + }; + }; + } else { + throw 'Your algo is not available'; + } + + var key = sjcl.misc.pbkdf2( + passphrase, + sjcl.codec.base64.toBits(salt), + Amaretti.iterations, + 256, + sjclAlgo + ); + // TODO fix SHA1 + // https://github.com/bitwiseshiftleft/sjcl/issues/75 + resolve(sjcl.codec.base64.fromBits(key)); + } catch (exception) { + reject(exception.message); + } + }); + } + }, + + encrypt: function(key, message, nonce) { + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.encrypt) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + base64ToArrayByte(key), + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (rawKey) { + + return Amaretti.getCrypto().subtle.encrypt( + { + name: 'AES-GCM', + iv: base64ToArrayByte(nonce), + tagLength: Amaretti.authTagLength + }, + rawKey, + encode(message) + ).then(function (crypted) { + return new Promise(function (resolve, reject) { + try { + resolve(arrayBufferToBase64(crypted)); + } catch (exception) { + reject(exception.message); + } + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var rawKey = sjcl.codec.base64.toBits(key); + var rawNonce = sjcl.codec.base64.toBits(nonce); + var keyAES = new sjcl.cipher.aes(rawKey); + var crypt = sjcl.mode.gcm.encrypt( + keyAES, + sjcl.codec.utf8String.toBits(message), + rawNonce, + sjcl.codec.utf8String.toBits(Amaretti.authData), + Amaretti.authentificationTagLength); + + resolve(sjcl.codec.base64.fromBits(crypt)); + } catch (exception) { + reject(exception.message); + } + }); + } + }, + + decrypt: function(key, crypted, nonce) { + + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.decrypt) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + base64ToArrayByte(key), + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (rawKey) { + return Amaretti.getCrypto().subtle.decrypt( + { + name: 'AES-GCM', + iv: base64ToArrayByte(nonce), + tagLength: Amaretti.authTagLength + }, + rawKey, + base64ToArrayByte(crypted) + ).then(function(plaintext) { + return new Promise(function(resolve, reject) { + resolve(decode(plaintext)); + }); + }, function (err) { + return new Promise(function(resolve, reject) { + reject('Integrity/Authenticity check failed! Invalid password?'); + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var rawKey = sjcl.codec.base64.toBits(key); + var rawNonce = sjcl.codec.base64.toBits(nonce); + var rawCrypted = sjcl.codec.base64.toBits(crypted); + var keyAES = new sjcl.cipher.aes(rawKey); + + var crypt = sjcl.mode.gcm.decrypt( + keyAES, + rawCrypted, + rawNonce, + sjcl.codec.utf8String.toBits(Amaretti.authData), + Amaretti.authentificationTagLength); + var decrypt = sjcl.codec.utf8String.fromBits(crypt); + resolve(decrypt); + } catch (exception) { + reject(exception.message); + } + }); + } + } +}; + +module.exports = Amaretti; diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..fcb35b3 --- /dev/null +++ b/bower.json @@ -0,0 +1,22 @@ +{ + "name": "amaretti", + "description": "Wrapper of mutiple encryption library to encrypt/decrypt messages with a passphrase, everywhere", + "version": "0.1.0", + "author": { + "name": "Vincent Cassé", + "url": "https://beveloper.fr" + }, + "main": [ + "public/vendor.js", + "public/amaretti.js" + ], + "license": "MIT", + "ignore": [ + "node_modules", + "bower_components" + ], + "dependencies": { + "sjcl": "^1.0.3", + "es6-promise": "^2.1.x" + } +} diff --git a/brunch-config.coffee b/brunch-config.coffee new file mode 100644 index 0000000..50cd359 --- /dev/null +++ b/brunch-config.coffee @@ -0,0 +1,30 @@ +module.exports = config: + paths: + watched: + ['app'] + files: + javascripts: + joinTo: + 'vendor.js': /^bower_components/ + 'amaretti.js': /^app/ + plugins: + jshint: + pattern: /^app\/.*\.js$/ + options: + bitwise: false + curly: true + node: true + globals: + jQuery: true + window: true + global: true + btoa: true + Uint8Array: true + require: true + console: true + module: true + Promise: true + TextDecoder: true + TextEncoder: true + sjcl: true + warnOnly: true diff --git a/karma.conf.coffee b/karma.conf.coffee new file mode 100644 index 0000000..76485ac --- /dev/null +++ b/karma.conf.coffee @@ -0,0 +1,53 @@ +module.exports = (config) -> config.set + # base path, that will be used to resolve files and exclude + basePath: '' + + frameworks: ['jasmine'] + + # list of files / patterns to load in the browser + files: [ + 'public/vendor.js', + 'public/amaretti.js', + 'node_modules/mock-promises/lib/mock-promises.js', + 'test/*.js' + ] + + # list of files to exclude + exclude: [] + + # use dots reporter, as travis terminal does not support escaping sequences + # possible values: 'dots' || 'progress' + reporters: ['progress'] + + # Where to save JUnit test results + junitReporter: + outputFile: 'test-results-frontend.xml' + + # web server port + port: 9876 + + # cli runner port + runnerPort: 9100 + + # enable / disable colors in the output (reporters and logs) + colors: true + + # level of logging + # possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + logLevel: config.LOG_INFO + + # enable / disable watching file and executing tests whenever any file changes + autoWatch: true + + # Start these browsers, currently available: + # - Chrome + # - ChromeCanary + # - Firefox + # - Opera + # - Safari + # - PhantomJS + browsers: ['Chrome', 'ChromeCanary', 'Firefox'] + + # Auto run tests on start (when browsers are captured) and exit + #singleRun: true + singleRun: false diff --git a/package.json b/package.json new file mode 100644 index 0000000..d9be1b9 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "Amaretti", + "description": "Wrapper of mutiple encryption library to encrypt/decrypt messages with a passphrase, everywhere", + "version": "0.1.0", + "author": { + "name": "Vincent Cassé", + "url": "https://beveloper.fr" + }, + "scripts": { + "start": "npm install && bower install && rm -rf public/*; brunch watch --server", + "test": "npm install && bower install && node_modules/.bin/karma start", + "clean": "rm -rf node_modules/; rm -rf bower_components/; rm -rf public/" + }, + "main": "public/amaretti.js", + "licenses": [ + { + "type": "MIT License", + "url": "http://opensource.org/licenses/mit-license.html" + } + ], + "readme": "README.md", + "devDependencies": { + "brunch": "^1.8.2", + "coffee-script": "^1.9.2", + "del": "^1.1.1", + "fb-flo-brunch": "^1.7.22", + "javascript-brunch": "1.7.1", + "jshint-brunch": "^1.8.0", + "karma": "~0.12.0", + "karma-chrome-launcher": "~0.1.0", + "karma-cli": "0.0.4", + "karma-firefox-launcher": "^0.1.6", + "karma-jasmine": "~0.2.0", + "karma-junit-reporter": "~0.2.0", + "karma-phantomjs-launcher": "^0.1.4", + "mock-promises": "^0.3.0" + } +} diff --git a/public/amaretti.js b/public/amaretti.js new file mode 100644 index 0000000..71482eb --- /dev/null +++ b/public/amaretti.js @@ -0,0 +1,456 @@ +(function() { + 'use strict'; + + var globals = typeof window === 'undefined' ? global : window; + if (typeof globals.require === 'function') return; + + var modules = {}; + var cache = {}; + var has = ({}).hasOwnProperty; + + var aliases = {}; + + var endsWith = function(str, suffix) { + return str.indexOf(suffix, str.length - suffix.length) !== -1; + }; + + var unalias = function(alias, loaderPath) { + var start = 0; + if (loaderPath) { + if (loaderPath.indexOf('components/' === 0)) { + start = 'components/'.length; + } + if (loaderPath.indexOf('/', start) > 0) { + loaderPath = loaderPath.substring(start, loaderPath.indexOf('/', start)); + } + } + var result = aliases[alias + '/index.js'] || aliases[loaderPath + '/deps/' + alias + '/index.js']; + if (result) { + return 'components/' + result.substring(0, result.length - '.js'.length); + } + return alias; + }; + + var expand = (function() { + var reg = /^\.\.?(\/|$)/; + return function(root, name) { + var results = [], parts, part; + parts = (reg.test(name) ? root + '/' + name : name).split('/'); + for (var i = 0, length = parts.length; i < length; i++) { + part = parts[i]; + if (part === '..') { + results.pop(); + } else if (part !== '.' && part !== '') { + results.push(part); + } + } + return results.join('/'); + }; + })(); + var dirname = function(path) { + return path.split('/').slice(0, -1).join('/'); + }; + + var localRequire = function(path) { + return function(name) { + var absolute = expand(dirname(path), name); + return globals.require(absolute, path); + }; + }; + + var initModule = function(name, definition) { + var module = {id: name, exports: {}}; + cache[name] = module; + definition(module.exports, localRequire(name), module); + return module.exports; + }; + + var require = function(name, loaderPath) { + var path = expand(name, '.'); + if (loaderPath == null) loaderPath = '/'; + path = unalias(name, loaderPath); + + if (has.call(cache, path)) return cache[path].exports; + if (has.call(modules, path)) return initModule(path, modules[path]); + + var dirIndex = expand(path, './index'); + if (has.call(cache, dirIndex)) return cache[dirIndex].exports; + if (has.call(modules, dirIndex)) return initModule(dirIndex, modules[dirIndex]); + + throw new Error('Cannot find module "' + name + '" from '+ '"' + loaderPath + '"'); + }; + + require.alias = function(from, to) { + aliases[to] = from; + }; + + require.register = require.define = function(bundle, fn) { + if (typeof bundle === 'object') { + for (var key in bundle) { + if (has.call(bundle, key)) { + modules[key] = bundle[key]; + } + } + } else { + modules[bundle] = fn; + } + }; + + require.list = function() { + var result = []; + for (var item in modules) { + if (has.call(modules, item)) { + result.push(item); + } + } + return result; + }; + + require.brunch = true; + globals.require = require; +})(); +require.register("amaretti", function(exports, require, module) { +"use strict"; + +var encode = function(str) { + return new TextEncoder('utf-8').encode(str); +}; +var decode = function(buf) { + return new TextDecoder('utf-8').decode(new Uint8Array(buf)); +}; +/*var ab2str = function(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); +};*/ +function b64ToUint6 (nChr) { + + return nChr > 64 && nChr < 91 ? + nChr - 65 + : nChr > 96 && nChr < 123 ? + nChr - 71 + : nChr > 47 && nChr < 58 ? + nChr + 4 + : nChr === 43 ? + 62 + : nChr === 47 ? + 63 + : + 0; + +} + +function arrayBufferToBase64 (arrayBuffer) { + return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer))); +} +/** +* TODO add better way to use atob whe possible +*/ +//https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer +function base64ToArrayByte(sBase64, nBlocksSize) { + + var + sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, + nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); + + for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { + nMod4 = nInIdx & 3; + nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4); + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { + taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; + } + nUint24 = 0; + + } + } + + return taBytes; + +} +var arrayByteToBase64 = function(arrayByte) { + //http://stackoverflow.com/a/12713326 + var CHUNK_SZ = 0x8000; + var c = []; + for (var i = 0; i < arrayByte.length; i += CHUNK_SZ) { + c.push(String.fromCharCode.apply( + null, + arrayByte.subarray(i, i + CHUNK_SZ) + )); + } + return btoa(c.join('')); +}; + +var Amaretti = { + + authTagLength : 128, + paranoia: 10, + iterations: 5000, + enableNative: true, + + init: function (opts) { + this.authTagUsedSpace = this.authTagLength / 4; + sjcl.random.startCollectors(); + + if (opts) { + this.iterations = opts.iterations ? opts.iterations : 5000; + this.enableNative = opts.enableNative !== undefined ? opts.enableNative : true; + } + return this; + }, + + getCrypto: function() { + return window.crypto || window.msCrypto; + }, + + getSalt: function() { + var origSalt; + var salt; + return new Promise(function(resolve, reject) { + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().getRandomValues) { + try { + origSalt = Amaretti.getCrypto().getRandomValues(new Uint8Array(64)); + salt = arrayByteToBase64(origSalt); + resolve(salt); + } catch (exception) { + reject(exception.message); + } + return; + } + + if (sjcl.random.isReady(Amaretti.paranoia) === 0) { + reject('sjcl random is not ready'); + return; + } + try { + origSalt = sjcl.random.randomWords(8, Amaretti.paranoia); + salt = sjcl.codec.base64.fromBits(origSalt); + resolve(salt); + } catch (exception) { + reject(exception.message); + } + }); + }, + + generateKey: function(passphrase, salt, algo) { + if (!algo) { + return new Promise(function(resolve, reject) { + reject('Please add an algo'); + }); + } + + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.deriveKey && + Amaretti.getCrypto().subtle.exportKey) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + encode(passphrase), + { name: 'PBKDF2' }, + false, + ['deriveKey'] + ).then(function(baseKey) { + + var cryptoAlgo; + if (algo === 'SHA-1') { + cryptoAlgo = 'SHA-1'; + } else if (algo === 'SHA-256') { + throw 'SHA-256 is not yet available on firefox'; + } else { + throw 'Your algo is not available'; + } + + return Amaretti.getCrypto().subtle.deriveKey( + { + name: 'PBKDF2', + hash: cryptoAlgo, + salt: base64ToArrayByte(salt), + iterations: Amaretti.iterations + }, + baseKey, + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (key) { + return Amaretti.getCrypto().subtle.exportKey('raw', key) + .then(function (rawKey) { + return new Promise(function(resolve, reject) { + try { + resolve(arrayByteToBase64(new Uint8Array(rawKey))); + } catch (exception) { + reject(exception.message); + } + }); + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var sjclAlgo; + + if (algo === 'SHA-1') { + sjclAlgo = function (key) { + // todo sha-1 + var hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 ); + this.encrypt = function () { + return hasher.encrypt.apply( hasher, arguments ); + }; + }; + } else if (algo === 'SHA-256') { + sjclAlgo = function (key) { + var hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 ); + this.encrypt = function () { + return hasher.encrypt.apply( hasher, arguments ); + }; + }; + } else { + throw 'Your algo is not available'; + } + + var key = sjcl.misc.pbkdf2( + passphrase, + sjcl.codec.base64.toBits(salt), + Amaretti.iterations, + 256, + sjclAlgo + ); + // TODO fix SHA1 + // https://github.com/bitwiseshiftleft/sjcl/issues/75 + resolve(sjcl.codec.base64.fromBits(key)); + } catch (exception) { + reject(exception.message); + } + }); + } + }, + + encrypt: function(key, message, nonce) { + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.encrypt) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + base64ToArrayByte(key), + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (rawKey) { + + return Amaretti.getCrypto().subtle.encrypt( + { + name: 'AES-GCM', + iv: base64ToArrayByte(nonce), + tagLength: Amaretti.authTagLength + }, + rawKey, + encode(message) + ).then(function (crypted) { + return new Promise(function (resolve, reject) { + try { + resolve(arrayBufferToBase64(crypted)); + } catch (exception) { + reject(exception.message); + } + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var rawKey = sjcl.codec.base64.toBits(key); + var rawNonce = sjcl.codec.base64.toBits(nonce); + var keyAES = new sjcl.cipher.aes(rawKey); + var crypt = sjcl.mode.gcm.encrypt( + keyAES, + sjcl.codec.utf8String.toBits(message), + rawNonce, + sjcl.codec.utf8String.toBits(Amaretti.authData), + Amaretti.authentificationTagLength); + + resolve(sjcl.codec.base64.fromBits(crypt)); + } catch (exception) { + reject(exception.message); + } + }); + } + }, + + decrypt: function(key, crypted, nonce) { + + if (Amaretti.enableNative && + Amaretti.getCrypto() && + Amaretti.getCrypto().subtle && + Amaretti.getCrypto().subtle.importKey && + Amaretti.getCrypto().subtle.decrypt) { + + return Amaretti.getCrypto().subtle.importKey( + 'raw', + base64ToArrayByte(key), + { + name: 'AES-GCM', + length: 256 + }, + true, + ['encrypt', 'decrypt'] + ).then(function (rawKey) { + return Amaretti.getCrypto().subtle.decrypt( + { + name: 'AES-GCM', + iv: base64ToArrayByte(nonce), + tagLength: Amaretti.authTagLength + }, + rawKey, + base64ToArrayByte(crypted) + ).then(function(plaintext) { + return new Promise(function(resolve, reject) { + resolve(decode(plaintext)); + }); + }, function (err) { + return new Promise(function(resolve, reject) { + reject('Integrity/Authenticity check failed! Invalid password?'); + }); + }); + }); + } else { + return new Promise(function(resolve, reject) { + try { + var rawKey = sjcl.codec.base64.toBits(key); + var rawNonce = sjcl.codec.base64.toBits(nonce); + var rawCrypted = sjcl.codec.base64.toBits(crypted); + var keyAES = new sjcl.cipher.aes(rawKey); + + var crypt = sjcl.mode.gcm.decrypt( + keyAES, + rawCrypted, + rawNonce, + sjcl.codec.utf8String.toBits(Amaretti.authData), + Amaretti.authentificationTagLength); + var decrypt = sjcl.codec.utf8String.fromBits(crypt); + resolve(decrypt); + } catch (exception) { + reject(exception.message); + } + }); + } + } +}; + +module.exports = Amaretti; + +}); + + +//# sourceMappingURL=amaretti.js.map \ No newline at end of file diff --git a/public/amaretti.js.map b/public/amaretti.js.map new file mode 100644 index 0000000..910f2eb --- /dev/null +++ b/public/amaretti.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["app/amaretti.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAnVA;AAAA","file":"public/amaretti.js","sourcesContent":["\"use strict\";\n\nvar encode = function(str) {\n\treturn new TextEncoder('utf-8').encode(str);\n};\nvar decode = function(buf) {\n\treturn new TextDecoder('utf-8').decode(new Uint8Array(buf));\n};\n/*var ab2str = function(buf) {\n\treturn String.fromCharCode.apply(null, new Uint16Array(buf));\n};*/\nfunction b64ToUint6 (nChr) {\n\n return nChr > 64 && nChr < 91 ?\n nChr - 65\n : nChr > 96 && nChr < 123 ?\n nChr - 71\n : nChr > 47 && nChr < 58 ?\n nChr + 4\n : nChr === 43 ?\n 62\n : nChr === 47 ?\n 63\n :\n 0;\n\n}\n\nfunction arrayBufferToBase64 (arrayBuffer) {\n\treturn btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));\n}\n/**\n* TODO add better way to use atob whe possible\n*/\n//https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer\nfunction base64ToArrayByte(sBase64, nBlocksSize) { \n\n var\n sB64Enc = sBase64.replace(/[^A-Za-z0-9\\+\\/]/g, \"\"), nInLen = sB64Enc.length,\n nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);\n\n for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {\n nMod4 = nInIdx & 3;\n nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);\n if (nMod4 === 3 || nInLen - nInIdx === 1) {\n for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {\n taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;\n }\n nUint24 = 0;\n\n }\n }\n\n return taBytes;\n\n}\nvar arrayByteToBase64 = function(arrayByte) {\n\t//http://stackoverflow.com/a/12713326\n\tvar CHUNK_SZ = 0x8000;\n\tvar c = [];\n\tfor (var i = 0; i < arrayByte.length; i += CHUNK_SZ) {\n\t\tc.push(String.fromCharCode.apply(\n\t\t\tnull,\n\t\t\tarrayByte.subarray(i, i + CHUNK_SZ)\n\t\t));\n\t}\n\treturn btoa(c.join(''));\n};\n\nvar Amaretti = {\n\n\tauthTagLength : 128,\n\tparanoia: 10,\n\titerations: 5000,\n\tenableNative: true,\n\n\tinit: function (opts) {\n\t\tthis.authTagUsedSpace = this.authTagLength / 4;\n\t\tsjcl.random.startCollectors();\n\n\t\tif (opts) {\n\t\t\tthis.iterations = opts.iterations ? opts.iterations : 5000;\n\t\t\tthis.enableNative = opts.enableNative !== undefined ? opts.enableNative : true; \n\t\t}\n\t\treturn this;\n\t},\n\n\tgetCrypto: function() {\n\t\treturn window.crypto || window.msCrypto;\n\t},\n\n\tgetSalt: function() {\n\t\tvar origSalt;\n\t\tvar salt;\n\t\treturn new Promise(function(resolve, reject) {\n\t\t\tif (Amaretti.enableNative &&\n\t\t\t\tAmaretti.getCrypto() &&\n\t\t\t\tAmaretti.getCrypto().getRandomValues) {\n\t\t\t\ttry {\n\t\t\t\t\torigSalt = Amaretti.getCrypto().getRandomValues(new Uint8Array(64));\n\t\t\t\t\tsalt = arrayByteToBase64(origSalt);\n\t\t\t\t\tresolve(salt);\n\t\t\t\t} catch (exception) {\n\t\t\t\t\treject(exception.message);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (sjcl.random.isReady(Amaretti.paranoia) === 0) {\n\t\t\t\treject('sjcl random is not ready');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\torigSalt = sjcl.random.randomWords(8, Amaretti.paranoia);\n\t\t\t\tsalt = sjcl.codec.base64.fromBits(origSalt);\n\t\t\t\tresolve(salt);\n\t\t\t} catch (exception) {\n\t\t\t\treject(exception.message);\n\t\t\t}\n\t\t});\n\t},\n\n\tgenerateKey: function(passphrase, salt, algo) {\n\t\tif (!algo) {\n\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\treject('Please add an algo');\n\t\t\t});\n\t\t}\n\n\t\tif (Amaretti.enableNative &&\n\t\t\tAmaretti.getCrypto() &&\n\t\t\tAmaretti.getCrypto().subtle &&\n\t\t\tAmaretti.getCrypto().subtle.importKey && \n\t\t\tAmaretti.getCrypto().subtle.deriveKey && \n\t\t\tAmaretti.getCrypto().subtle.exportKey) {\n\n\t\t\treturn Amaretti.getCrypto().subtle.importKey(\n\t\t\t\t'raw',\n\t\t\t\tencode(passphrase),\n\t\t\t\t{ name: 'PBKDF2' },\n\t\t\t\tfalse,\n\t\t\t\t['deriveKey']\n\t\t\t).then(function(baseKey) {\n\n\t\t\t\tvar cryptoAlgo;\n\t\t\t\tif (algo === 'SHA-1') {\n\t\t\t\t\tcryptoAlgo = 'SHA-1';\n\t\t\t\t} else if (algo === 'SHA-256') {\n\t\t\t\t\tthrow 'SHA-256 is not yet available on firefox';\n\t\t\t\t} else {\n\t\t\t\t\tthrow 'Your algo is not available';\n\t\t\t\t}\n\n\t\t\t\treturn Amaretti.getCrypto().subtle.deriveKey(\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'PBKDF2',\n\t\t\t\t\t\thash: cryptoAlgo,\n\t\t\t\t\t\tsalt: base64ToArrayByte(salt),\n\t\t\t\t\t\titerations: Amaretti.iterations\n\t\t\t\t\t},\n\t\t\t\t\tbaseKey,\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\t\tlength: 256\n\t\t\t\t\t},\n\t\t\t\t\ttrue,\n\t\t\t\t\t['encrypt', 'decrypt']\n\t\t\t\t).then(function (key) {\n\t\t\t\t\treturn Amaretti.getCrypto().subtle.exportKey('raw', key)\n\t\t\t\t\t\t.then(function (rawKey) {\n\t\t\t\t\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t \t\tresolve(arrayByteToBase64(new Uint8Array(rawKey)));\n\t\t\t\t\t\t\t \t} catch (exception) {\n\t\t\t\t\t\t\t \t\treject(exception.message);\n\t\t\t\t\t\t\t \t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\ttry {\n\t\t\t\t\tvar sjclAlgo;\n\n\t\t\t\t\tif (algo === 'SHA-1') {\n\t\t\t\t\t\tsjclAlgo = function (key) {\n\t\t\t\t\t\t\t// todo sha-1\n\t\t\t\t\t \tvar hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 );\n\t\t\t\t\t\t this.encrypt = function () {\n\t\t\t\t\t\t return hasher.encrypt.apply( hasher, arguments );\n\t\t\t\t\t\t };\n\t\t\t\t\t\t};\n\t\t\t\t\t} else if (algo === 'SHA-256') {\n\t\t\t\t\t\tsjclAlgo = function (key) {\n\t\t\t\t\t \tvar hasher = new sjcl.misc.hmac( key, sjcl.hash.sha256 );\n\t\t\t\t\t\t this.encrypt = function () {\n\t\t\t\t\t\t return hasher.encrypt.apply( hasher, arguments );\n\t\t\t\t\t\t };\n\t\t\t\t\t\t};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow 'Your algo is not available';\n\t\t\t\t\t}\n\n\t\t\t\t\tvar key = sjcl.misc.pbkdf2(\n\t\t\t\t\t\tpassphrase,\n\t\t\t\t\t\tsjcl.codec.base64.toBits(salt),\n\t\t\t\t\t\tAmaretti.iterations,\n\t\t\t\t\t\t256,\n\t\t\t\t\t\tsjclAlgo\n\t\t\t\t\t);\n\t\t\t\t\t// TODO fix SHA1\n\t\t\t\t\t// https://github.com/bitwiseshiftleft/sjcl/issues/75\n\t\t\t\t\tresolve(sjcl.codec.base64.fromBits(key));\n\t\t\t\t} catch (exception) {\n\t\t\t\t\treject(exception.message);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t},\n\n\tencrypt: function(key, message, nonce) {\n\t\tif (Amaretti.enableNative &&\n\t\t\tAmaretti.getCrypto() &&\n\t\t\tAmaretti.getCrypto().subtle &&\n\t\t\tAmaretti.getCrypto().subtle.importKey &&\n\t\t\tAmaretti.getCrypto().subtle.encrypt) {\n\n\t\t\treturn Amaretti.getCrypto().subtle.importKey(\n\t\t\t\t'raw',\n\t\t\t\tbase64ToArrayByte(key),\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tlength: 256\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t\t['encrypt', 'decrypt']\n\t\t\t).then(function (rawKey) {\n\t\t\t\t\n\t\t\t\treturn Amaretti.getCrypto().subtle.encrypt(\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\t\tiv: base64ToArrayByte(nonce),\n\t\t\t\t\t\ttagLength: Amaretti.authTagLength\n\t\t\t\t\t},\n\t\t\t\t\trawKey,\n\t\t\t\t\tencode(message)\n\t\t\t\t).then(function (crypted) {\n\t\t\t\t\treturn new Promise(function (resolve, reject) {\n\t\t\t\t\t\ttry {\t\n\t\t\t\t\t\t\tresolve(arrayBufferToBase64(crypted));\n\t\t\t\t\t\t} catch (exception) {\n\t\t\t\t\t\t\treject(exception.message);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\ttry {\n\t\t\t\t\tvar rawKey = sjcl.codec.base64.toBits(key);\n\t\t\t\t\tvar rawNonce = sjcl.codec.base64.toBits(nonce);\n\t\t\t\t\tvar keyAES = new sjcl.cipher.aes(rawKey);\n\t\t\t\t\tvar crypt = sjcl.mode.gcm.encrypt(\n\t\t\t\t\t\tkeyAES,\n\t\t\t\t\t\tsjcl.codec.utf8String.toBits(message),\n\t\t\t\t\t\trawNonce,\n\t\t\t\t\t\tsjcl.codec.utf8String.toBits(Amaretti.authData),\n\t\t\t\t\t\tAmaretti.authentificationTagLength);\n\n\t\t\t\t\tresolve(sjcl.codec.base64.fromBits(crypt));\n\t\t\t\t} catch (exception) {\n\t\t\t\t\treject(exception.message);\n\t\t\t\t}\n\t\t\t});\n\t\t}\t\n\t},\n\n\tdecrypt: function(key, crypted, nonce) {\n\n\t\tif (Amaretti.enableNative &&\n\t\t\tAmaretti.getCrypto() &&\n\t\t\tAmaretti.getCrypto().subtle &&\n\t\t\tAmaretti.getCrypto().subtle.importKey &&\n\t\t\tAmaretti.getCrypto().subtle.decrypt) {\n\n\t\t\treturn Amaretti.getCrypto().subtle.importKey(\n\t\t\t\t'raw',\n\t\t\t\tbase64ToArrayByte(key),\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tlength: 256\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t\t['encrypt', 'decrypt']\n\t\t\t).then(function (rawKey) {\n\t\t\t\treturn Amaretti.getCrypto().subtle.decrypt(\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\t\tiv: base64ToArrayByte(nonce),\n\t\t\t\t\t\ttagLength: Amaretti.authTagLength\n\t\t\t\t\t},\n\t\t\t\t\trawKey,\n\t\t\t\t\tbase64ToArrayByte(crypted)\n\t\t\t\t).then(function(plaintext) {\n\t\t\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\t\t\tresolve(decode(plaintext));\n\t\t\t\t\t});\n\t\t\t\t}, function (err) {\n\t\t\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\t\t\treject('Integrity/Authenticity check failed! Invalid password?');\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\treturn new Promise(function(resolve, reject) {\n\t\t\t\ttry {\n\t\t\t\t\tvar rawKey = sjcl.codec.base64.toBits(key);\n\t\t\t\t\tvar rawNonce = sjcl.codec.base64.toBits(nonce);\n\t\t\t\t\tvar rawCrypted = sjcl.codec.base64.toBits(crypted);\n\t\t\t\t\tvar keyAES = new sjcl.cipher.aes(rawKey);\n\n\t\t\t\t\tvar crypt = sjcl.mode.gcm.decrypt(\n\t\t\t\t\t\tkeyAES,\n\t\t\t\t\t\trawCrypted,\n\t\t\t\t\t\trawNonce,\n\t\t\t\t\t\tsjcl.codec.utf8String.toBits(Amaretti.authData),\n\t\t\t\t\t\tAmaretti.authentificationTagLength);\n\t\t\t\t\tvar decrypt = sjcl.codec.utf8String.fromBits(crypt);\n\t\t\t\t\tresolve(decrypt);\n\t\t\t\t} catch (exception) {\n\t\t\t\t\treject(exception.message);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n};\n\nmodule.exports = Amaretti;\n"]} \ No newline at end of file diff --git a/public/vendor.js b/public/vendor.js new file mode 100644 index 0000000..2b5179f --- /dev/null +++ b/public/vendor.js @@ -0,0 +1,1137 @@ +(function() { + 'use strict'; + + var globals = typeof window === 'undefined' ? global : window; + if (typeof globals.require === 'function') return; + + var modules = {}; + var cache = {}; + var has = ({}).hasOwnProperty; + + var aliases = {}; + + var endsWith = function(str, suffix) { + return str.indexOf(suffix, str.length - suffix.length) !== -1; + }; + + var unalias = function(alias, loaderPath) { + var start = 0; + if (loaderPath) { + if (loaderPath.indexOf('components/' === 0)) { + start = 'components/'.length; + } + if (loaderPath.indexOf('/', start) > 0) { + loaderPath = loaderPath.substring(start, loaderPath.indexOf('/', start)); + } + } + var result = aliases[alias + '/index.js'] || aliases[loaderPath + '/deps/' + alias + '/index.js']; + if (result) { + return 'components/' + result.substring(0, result.length - '.js'.length); + } + return alias; + }; + + var expand = (function() { + var reg = /^\.\.?(\/|$)/; + return function(root, name) { + var results = [], parts, part; + parts = (reg.test(name) ? root + '/' + name : name).split('/'); + for (var i = 0, length = parts.length; i < length; i++) { + part = parts[i]; + if (part === '..') { + results.pop(); + } else if (part !== '.' && part !== '') { + results.push(part); + } + } + return results.join('/'); + }; + })(); + var dirname = function(path) { + return path.split('/').slice(0, -1).join('/'); + }; + + var localRequire = function(path) { + return function(name) { + var absolute = expand(dirname(path), name); + return globals.require(absolute, path); + }; + }; + + var initModule = function(name, definition) { + var module = {id: name, exports: {}}; + cache[name] = module; + definition(module.exports, localRequire(name), module); + return module.exports; + }; + + var require = function(name, loaderPath) { + var path = expand(name, '.'); + if (loaderPath == null) loaderPath = '/'; + path = unalias(name, loaderPath); + + if (has.call(cache, path)) return cache[path].exports; + if (has.call(modules, path)) return initModule(path, modules[path]); + + var dirIndex = expand(path, './index'); + if (has.call(cache, dirIndex)) return cache[dirIndex].exports; + if (has.call(modules, dirIndex)) return initModule(dirIndex, modules[dirIndex]); + + throw new Error('Cannot find module "' + name + '" from '+ '"' + loaderPath + '"'); + }; + + require.alias = function(from, to) { + aliases[to] = from; + }; + + require.register = require.define = function(bundle, fn) { + if (typeof bundle === 'object') { + for (var key in bundle) { + if (has.call(bundle, key)) { + modules[key] = bundle[key]; + } + } + } else { + modules[bundle] = fn; + } + }; + + require.list = function() { + var result = []; + for (var item in modules) { + if (has.call(modules, item)) { + result.push(item); + } + } + return result; + }; + + require.brunch = true; + globals.require = require; +})(); +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 2.2.0 + */ + +(function() { + "use strict"; + function lib$es6$promise$utils$$objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } + + function lib$es6$promise$utils$$isFunction(x) { + return typeof x === 'function'; + } + + function lib$es6$promise$utils$$isMaybeThenable(x) { + return typeof x === 'object' && x !== null; + } + + var lib$es6$promise$utils$$_isArray; + if (!Array.isArray) { + lib$es6$promise$utils$$_isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } else { + lib$es6$promise$utils$$_isArray = Array.isArray; + } + + var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; + var lib$es6$promise$asap$$len = 0; + var lib$es6$promise$asap$$toString = {}.toString; + var lib$es6$promise$asap$$vertxNext; + var lib$es6$promise$asap$$customSchedulerFn; + + function lib$es6$promise$asap$$asap(callback, arg) { + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; + lib$es6$promise$asap$$len += 2; + if (lib$es6$promise$asap$$len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (lib$es6$promise$asap$$customSchedulerFn) { + lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); + } else { + lib$es6$promise$asap$$scheduleFlush(); + } + } + } + + var lib$es6$promise$asap$$default = lib$es6$promise$asap$$asap; + function lib$es6$promise$asap$$setScheduler(scheduleFn) { + lib$es6$promise$asap$$customSchedulerFn = scheduleFn; + } + + var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; + var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; + var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; + var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; + + // test for web worker but not in IE10 + var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; + + // node + function lib$es6$promise$asap$$useNextTick() { + var nextTick = process.nextTick; + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // setImmediate should be used instead instead + var version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/); + if (Array.isArray(version) && version[1] === '0' && version[2] === '10') { + nextTick = setImmediate; + } + return function() { + nextTick(lib$es6$promise$asap$$flush); + }; + } + + // vertx + function lib$es6$promise$asap$$useVertxTimer() { + return function() { + lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); + }; + } + + function lib$es6$promise$asap$$useMutationObserver() { + var iterations = 0; + var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function() { + node.data = (iterations = ++iterations % 2); + }; + } + + // web worker + function lib$es6$promise$asap$$useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = lib$es6$promise$asap$$flush; + return function () { + channel.port2.postMessage(0); + }; + } + + function lib$es6$promise$asap$$useSetTimeout() { + return function() { + setTimeout(lib$es6$promise$asap$$flush, 1); + }; + } + + var lib$es6$promise$asap$$queue = new Array(1000); + function lib$es6$promise$asap$$flush() { + for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { + var callback = lib$es6$promise$asap$$queue[i]; + var arg = lib$es6$promise$asap$$queue[i+1]; + + callback(arg); + + lib$es6$promise$asap$$queue[i] = undefined; + lib$es6$promise$asap$$queue[i+1] = undefined; + } + + lib$es6$promise$asap$$len = 0; + } + + function lib$es6$promise$asap$$attemptVertex() { + try { + var r = require; + var vertx = r('vertx'); + lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; + return lib$es6$promise$asap$$useVertxTimer(); + } catch(e) { + return lib$es6$promise$asap$$useSetTimeout(); + } + } + + var lib$es6$promise$asap$$scheduleFlush; + // Decide what async method to use to triggering processing of queued callbacks: + if (lib$es6$promise$asap$$isNode) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); + } else if (lib$es6$promise$asap$$BrowserMutationObserver) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); + } else if (lib$es6$promise$asap$$isWorker) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); + } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertex(); + } else { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); + } + + function lib$es6$promise$$internal$$noop() {} + + var lib$es6$promise$$internal$$PENDING = void 0; + var lib$es6$promise$$internal$$FULFILLED = 1; + var lib$es6$promise$$internal$$REJECTED = 2; + + var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$selfFullfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } + + function lib$es6$promise$$internal$$cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } + + function lib$es6$promise$$internal$$getThen(promise) { + try { + return promise.then; + } catch(error) { + lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; + return lib$es6$promise$$internal$$GET_THEN_ERROR; + } + } + + function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } + + function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { + lib$es6$promise$asap$$default(function(promise) { + var sealed = false; + var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + lib$es6$promise$$internal$$resolve(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; + + lib$es6$promise$$internal$$reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + lib$es6$promise$$internal$$reject(promise, error); + } + }, promise); + } + + function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { + if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, thenable._result); + } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, thenable._result); + } else { + lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { + lib$es6$promise$$internal$$resolve(promise, value); + }, function(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } + } + + function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) { + if (maybeThenable.constructor === promise.constructor) { + lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); + } else { + var then = lib$es6$promise$$internal$$getThen(maybeThenable); + + if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); + } else if (then === undefined) { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } else if (lib$es6$promise$utils$$isFunction(then)) { + lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); + } else { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } + } + } + + function lib$es6$promise$$internal$$resolve(promise, value) { + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFullfillment()); + } else if (lib$es6$promise$utils$$objectOrFunction(value)) { + lib$es6$promise$$internal$$handleMaybeThenable(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + } + + function lib$es6$promise$$internal$$publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + lib$es6$promise$$internal$$publish(promise); + } + + function lib$es6$promise$$internal$$fulfill(promise, value) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + + promise._result = value; + promise._state = lib$es6$promise$$internal$$FULFILLED; + + if (promise._subscribers.length !== 0) { + lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publish, promise); + } + } + + function lib$es6$promise$$internal$$reject(promise, reason) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + promise._state = lib$es6$promise$$internal$$REJECTED; + promise._result = reason; + + lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publishRejection, promise); + } + + function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + parent._onerror = null; + + subscribers[length] = child; + subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; + subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; + + if (length === 0 && parent._state) { + lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publish, parent); + } + } + + function lib$es6$promise$$internal$$publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { return; } + + var child, callback, detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; + } + + function lib$es6$promise$$internal$$ErrorObject() { + this.error = null; + } + + var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; + return lib$es6$promise$$internal$$TRY_CATCH_ERROR; + } + } + + function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { + var hasCallback = lib$es6$promise$utils$$isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + value = lib$es6$promise$$internal$$tryCatch(callback, detail); + + if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); + return; + } + + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== lib$es6$promise$$internal$$PENDING) { + // noop + } else if (hasCallback && succeeded) { + lib$es6$promise$$internal$$resolve(promise, value); + } else if (failed) { + lib$es6$promise$$internal$$reject(promise, error); + } else if (settled === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, value); + } else if (settled === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } + } + + function lib$es6$promise$$internal$$initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + lib$es6$promise$$internal$$resolve(promise, value); + }, function rejectPromise(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } catch(e) { + lib$es6$promise$$internal$$reject(promise, e); + } + } + + function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { + var enumerator = this; + + enumerator._instanceConstructor = Constructor; + enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (enumerator._validateInput(input)) { + enumerator._input = input; + enumerator.length = input.length; + enumerator._remaining = input.length; + + enumerator._init(); + + if (enumerator.length === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } else { + enumerator.length = enumerator.length || 0; + enumerator._enumerate(); + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } + } + } else { + lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); + } + } + + lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { + return lib$es6$promise$utils$$isArray(input); + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { + this._result = new Array(this.length); + }; + + var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; + + lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { + var enumerator = this; + + var length = enumerator.length; + var promise = enumerator.promise; + var input = enumerator._input; + + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + enumerator._eachEntry(input[i], i); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { + var enumerator = this; + var c = enumerator._instanceConstructor; + + if (lib$es6$promise$utils$$isMaybeThenable(entry)) { + if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) { + entry._onerror = null; + enumerator._settledAt(entry._state, i, entry._result); + } else { + enumerator._willSettleAt(c.resolve(entry), i); + } + } else { + enumerator._remaining--; + enumerator._result[i] = entry; + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { + var enumerator = this; + var promise = enumerator.promise; + + if (promise._state === lib$es6$promise$$internal$$PENDING) { + enumerator._remaining--; + + if (state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } else { + enumerator._result[i] = value; + } + } + + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(promise, enumerator._result); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; + + lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { + enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); + }); + }; + function lib$es6$promise$promise$all$$all(entries) { + return new lib$es6$promise$enumerator$$default(this, entries).promise; + } + var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; + function lib$es6$promise$promise$race$$race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (!lib$es6$promise$utils$$isArray(entries)) { + lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } + + var length = entries.length; + + function onFulfillment(value) { + lib$es6$promise$$internal$$resolve(promise, value); + } + + function onRejection(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + } + + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); + } + + return promise; + } + var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; + function lib$es6$promise$promise$resolve$$resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$resolve(promise, object); + return promise; + } + var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; + function lib$es6$promise$promise$reject$$reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$reject(promise, reason); + return promise; + } + var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; + + var lib$es6$promise$promise$$counter = 0; + + function lib$es6$promise$promise$$needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } + + function lib$es6$promise$promise$$needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise’s eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor + */ + function lib$es6$promise$promise$$Promise(resolver) { + this._id = lib$es6$promise$promise$$counter++; + this._state = undefined; + this._result = undefined; + this._subscribers = []; + + if (lib$es6$promise$$internal$$noop !== resolver) { + if (!lib$es6$promise$utils$$isFunction(resolver)) { + lib$es6$promise$promise$$needsResolver(); + } + + if (!(this instanceof lib$es6$promise$promise$$Promise)) { + lib$es6$promise$promise$$needsNew(); + } + + lib$es6$promise$$internal$$initializePromise(this, resolver); + } + } + + lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; + lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; + lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; + lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; + lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; + lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$default; + + lib$es6$promise$promise$$Promise.prototype = { + constructor: lib$es6$promise$promise$$Promise, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + var result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + var author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; + + if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { + return this; + } + + var child = new this.constructor(lib$es6$promise$$internal$$noop); + var result = parent._result; + + if (state) { + var callback = arguments[state - 1]; + lib$es6$promise$asap$$default(function(){ + lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); + }); + } else { + lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); + } + + return child; + }, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection) { + return this.then(null, onRejection); + } + }; + function lib$es6$promise$polyfill$$polyfill() { + var local; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; + } + + local.Promise = lib$es6$promise$promise$$default; + } + var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; + + var lib$es6$promise$umd$$ES6Promise = { + 'Promise': lib$es6$promise$promise$$default, + 'polyfill': lib$es6$promise$polyfill$$default + }; + + /* global define:true module:true window: true */ + if (typeof define === 'function' && define['amd']) { + define(function() { return lib$es6$promise$umd$$ES6Promise; }); + } else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = lib$es6$promise$umd$$ES6Promise; + } else if (typeof this !== 'undefined') { + this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; + } + + lib$es6$promise$polyfill$$default(); +}).call(this); + + +"use strict";function q(a){throw a;}var s=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}}; +"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl}); +sjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c& +255]]}; +sjcl.cipher.aes.prototype={encrypt:function(a){return w(this,a,0)},decrypt:function(a){return w(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e= +0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; +function w(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,t=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],y=h[3],z=h[4];for(m=0;m>>24]^r[f>>16&255]^v[g>>8&255]^y[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^y[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^y[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^y[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4> +m;m++)t[c?3&-m:m]=z[e>>>24]<<24^z[f>>16&255]<<16^z[g>>8&255]<<8^z[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return t} +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===s?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}}; +sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;dh&&q(new sjcl.exception.invalid("this isn't base64!")),26>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)x(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/ +4294967296));for(b.push(this.h|0);b.length;)x(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}}; +function x(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],t=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(t^r))+h[c],v=r,r=t,t=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+t|0;g[6]= +g[6]+r|0;g[7]=g[7]+v|0} +sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b, +h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;de.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h, +d(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; +sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0); +for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.j&&(this.j=f);this.F++; +this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===s&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.j,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.I[a!==s?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&& +(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U),touchCollector:D(this,this.da)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector, +u),window.addEventListener("keypress",this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u),window.addEventListener("touchmove",this.a.touchCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener? +(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u),window.removeEventListener("touchmove",this.a.touchCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress", +this.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;cb&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6); +a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H); +else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))} +sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4< +f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(f.adata=c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f}, +encrypt:function(a,b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4 postsJSON\n values[1] // => commentsJSON\n\n return values;\n });\n ```\n\n @class Promise\n @param {function} resolver\n Useful for tooling.\n @constructor\n */\n function lib$es6$promise$promise$$Promise(resolver) {\n this._id = lib$es6$promise$promise$$counter++;\n this._state = undefined;\n this._result = undefined;\n this._subscribers = [];\n\n if (lib$es6$promise$$internal$$noop !== resolver) {\n if (!lib$es6$promise$utils$$isFunction(resolver)) {\n lib$es6$promise$promise$$needsResolver();\n }\n\n if (!(this instanceof lib$es6$promise$promise$$Promise)) {\n lib$es6$promise$promise$$needsNew();\n }\n\n lib$es6$promise$$internal$$initializePromise(this, resolver);\n }\n }\n\n lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default;\n lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default;\n lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default;\n lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default;\n lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler;\n lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$default;\n\n lib$es6$promise$promise$$Promise.prototype = {\n constructor: lib$es6$promise$promise$$Promise,\n\n /**\n The primary way of interacting with a promise is through its `then` method,\n which registers callbacks to receive either a promise's eventual value or the\n reason why the promise cannot be fulfilled.\n\n ```js\n findUser().then(function(user){\n // user is available\n }, function(reason){\n // user is unavailable, and you are given the reason why\n });\n ```\n\n Chaining\n --------\n\n The return value of `then` is itself a promise. This second, 'downstream'\n promise is resolved with the return value of the first promise's fulfillment\n or rejection handler, or rejected if the handler throws an exception.\n\n ```js\n findUser().then(function (user) {\n return user.name;\n }, function (reason) {\n return 'default name';\n }).then(function (userName) {\n // If `findUser` fulfilled, `userName` will be the user's name, otherwise it\n // will be `'default name'`\n });\n\n findUser().then(function (user) {\n throw new Error('Found user, but still unhappy');\n }, function (reason) {\n throw new Error('`findUser` rejected and we're unhappy');\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.\n // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.\n });\n ```\n If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.\n\n ```js\n findUser().then(function (user) {\n throw new PedagogicalException('Upstream error');\n }).then(function (value) {\n // never reached\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // The `PedgagocialException` is propagated all the way down to here\n });\n ```\n\n Assimilation\n ------------\n\n Sometimes the value you want to propagate to a downstream promise can only be\n retrieved asynchronously. This can be achieved by returning a promise in the\n fulfillment or rejection handler. The downstream promise will then be pending\n until the returned promise is settled. This is called *assimilation*.\n\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // The user's comments are now available\n });\n ```\n\n If the assimliated promise rejects, then the downstream promise will also reject.\n\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // If `findCommentsByAuthor` fulfills, we'll have the value here\n }, function (reason) {\n // If `findCommentsByAuthor` rejects, we'll have the reason here\n });\n ```\n\n Simple Example\n --------------\n\n Synchronous Example\n\n ```javascript\n var result;\n\n try {\n result = findResult();\n // success\n } catch(reason) {\n // failure\n }\n ```\n\n Errback Example\n\n ```js\n findResult(function(result, err){\n if (err) {\n // failure\n } else {\n // success\n }\n });\n ```\n\n Promise Example;\n\n ```javascript\n findResult().then(function(result){\n // success\n }, function(reason){\n // failure\n });\n ```\n\n Advanced Example\n --------------\n\n Synchronous Example\n\n ```javascript\n var author, books;\n\n try {\n author = findAuthor();\n books = findBooksByAuthor(author);\n // success\n } catch(reason) {\n // failure\n }\n ```\n\n Errback Example\n\n ```js\n\n function foundBooks(books) {\n\n }\n\n function failure(reason) {\n\n }\n\n findAuthor(function(author, err){\n if (err) {\n failure(err);\n // failure\n } else {\n try {\n findBoooksByAuthor(author, function(books, err) {\n if (err) {\n failure(err);\n } else {\n try {\n foundBooks(books);\n } catch(reason) {\n failure(reason);\n }\n }\n });\n } catch(error) {\n failure(err);\n }\n // success\n }\n });\n ```\n\n Promise Example;\n\n ```javascript\n findAuthor().\n then(findBooksByAuthor).\n then(function(books){\n // found books\n }).catch(function(reason){\n // something went wrong\n });\n ```\n\n @method then\n @param {Function} onFulfilled\n @param {Function} onRejected\n Useful for tooling.\n @return {Promise}\n */\n then: function(onFulfillment, onRejection) {\n var parent = this;\n var state = parent._state;\n\n if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) {\n return this;\n }\n\n var child = new this.constructor(lib$es6$promise$$internal$$noop);\n var result = parent._result;\n\n if (state) {\n var callback = arguments[state - 1];\n lib$es6$promise$asap$$default(function(){\n lib$es6$promise$$internal$$invokeCallback(state, child, callback, result);\n });\n } else {\n lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection);\n }\n\n return child;\n },\n\n /**\n `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same\n as the catch block of a try/catch statement.\n\n ```js\n function findAuthor(){\n throw new Error('couldn't find that author');\n }\n\n // synchronous\n try {\n findAuthor();\n } catch(reason) {\n // something went wrong\n }\n\n // async with promises\n findAuthor().catch(function(reason){\n // something went wrong\n });\n ```\n\n @method catch\n @param {Function} onRejection\n Useful for tooling.\n @return {Promise}\n */\n 'catch': function(onRejection) {\n return this.then(null, onRejection);\n }\n };\n function lib$es6$promise$polyfill$$polyfill() {\n var local;\n\n if (typeof global !== 'undefined') {\n local = global;\n } else if (typeof self !== 'undefined') {\n local = self;\n } else {\n try {\n local = Function('return this')();\n } catch (e) {\n throw new Error('polyfill failed because global object is unavailable in this environment');\n }\n }\n\n var P = local.Promise;\n\n if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) {\n return;\n }\n\n local.Promise = lib$es6$promise$promise$$default;\n }\n var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill;\n\n var lib$es6$promise$umd$$ES6Promise = {\n 'Promise': lib$es6$promise$promise$$default,\n 'polyfill': lib$es6$promise$polyfill$$default\n };\n\n /* global define:true module:true window: true */\n if (typeof define === 'function' && define['amd']) {\n define(function() { return lib$es6$promise$umd$$ES6Promise; });\n } else if (typeof module !== 'undefined' && module['exports']) {\n module['exports'] = lib$es6$promise$umd$$ES6Promise;\n } else if (typeof this !== 'undefined') {\n this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise;\n }\n\n lib$es6$promise$polyfill$$default();\n}).call(this);\n\n","\"use strict\";function q(a){throw a;}var s=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return\"CORRUPT: \"+this.message};this.message=a},invalid:function(a){this.toString=function(){return\"INVALID: \"+this.message};this.message=a},bug:function(a){this.toString=function(){return\"BUG: \"+this.message};this.message=a},notReady:function(a){this.toString=function(){return\"NOT READY: \"+this.message};this.message=a}}};\n\"undefined\"!==typeof module&&module.exports&&(module.exports=sjcl);\"function\"===typeof define&&define([],function(){return sjcl});\nsjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid(\"invalid aes key size\"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&\n255]]};\nsjcl.cipher.aes.prototype={encrypt:function(a){return w(this,a,0)},decrypt:function(a){return w(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=\n0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};\nfunction w(a,b,c){4!==b.length&&q(new sjcl.exception.invalid(\"invalid aes block size\"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,t=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],y=h[3],z=h[4];for(m=0;m>>24]^r[f>>16&255]^v[g>>8&255]^y[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^y[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^y[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^y[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>\nm;m++)t[c?3&-m:m]=z[e>>>24]<<24^z[f>>16&255]<<16^z[g>>8&255]<<8^z[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return t}\nsjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===s?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};\nsjcl.codec.utf8String={fromBits:function(a){var b=\"\",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+=\"=\";return d},toBits:function(a,b){a=a.replace(/\\s|=/g,\"\");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+\"-_\");for(d=0;dh&&q(new sjcl.exception.invalid(\"this isn't base64!\")),26>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};\nsjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){\"string\"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)x(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/\n4294967296));for(b.push(this.h|0);b.length;)x(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}};\nfunction x(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],t=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(t^r))+h[c],v=r,r=t,t=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+t|0;g[6]=\ng[6]+r|0;g[7]=g[7]+v|0}\nsjcl.mode.ccm={name:\"ccm\",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid(\"ccm: iv must be at least 7 bytes\"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,\nh-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid(\"ccm: iv must be at least 7 bytes\"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt(\"ccm: tag doesn't match\"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;de.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,\nd(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};\nsjcl.mode.gcm={name:\"gcm\",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt(\"gcm: tag doesn't match\"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0);\nfor(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)&&q(sjcl.exception.invalid(\"invalid params to pbkdf2\"));\"string\"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));\"string\"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.j&&(this.j=f);this.F++;\nthis.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case \"string\":b===s&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug(\"random: addEntropy only supports number, array of numbers or string\"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C(\"seeded\",Math.max(this.j,this.d)),C(\"progress\",this.getProgress()))},isReady:function(a){a=this.I[a!==s?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&&\n(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U),touchCollector:D(this,this.da)},window.addEventListener?(window.addEventListener(\"load\",this.a.loadTimeCollector,u),window.addEventListener(\"mousemove\",this.a.mouseCollector,\nu),window.addEventListener(\"keypress\",this.a.keyboardCollector,u),window.addEventListener(\"devicemotion\",this.a.accelerometerCollector,u),window.addEventListener(\"touchmove\",this.a.touchCollector,u)):document.attachEvent?(document.attachEvent(\"onload\",this.a.loadTimeCollector),document.attachEvent(\"onmousemove\",this.a.mouseCollector),document.attachEvent(\"keypress\",this.a.keyboardCollector)):q(new sjcl.exception.bug(\"can't attach event\")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener?\n(window.removeEventListener(\"load\",this.a.loadTimeCollector,u),window.removeEventListener(\"mousemove\",this.a.mouseCollector,u),window.removeEventListener(\"keypress\",this.a.keyboardCollector,u),window.removeEventListener(\"devicemotion\",this.a.accelerometerCollector,u),window.removeEventListener(\"touchmove\",this.a.touchCollector,u)):document.detachEvent&&(document.detachEvent(\"onload\",this.a.loadTimeCollector),document.detachEvent(\"onmousemove\",this.a.mouseCollector),document.detachEvent(\"keypress\",\nthis.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;cb&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);\na:try{var F,G,H,I;if(I=\"undefined\"!==typeof module){var J;if(J=module.exports){var K;try{K=require(\"crypto\")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,\"crypto['randomBytes']\");else if(\"undefined\"!==typeof window&&\"undefined\"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);\nelse break a;sjcl.random.addEntropy(H,1024,\"crypto['getRandomValues']\")}}catch(M){\"undefined\"!==typeof window&&window.console&&(console.log(\"There was an error collecting entropy from the browser:\"),console.log(M))}\nsjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:\"ccm\",adata:\"\",cipher:\"aes\"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;\"string\"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));\"string\"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||\"string\"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4<\nf.iv.length)&&q(new sjcl.exception.invalid(\"json encrypt: invalid parameters\"));\"string\"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));\"string\"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));\"string\"===typeof c&&(f.adata=c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},\nencrypt:function(a,b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f,g;f=b.adata;\"string\"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));\"string\"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||\"string\"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4