Mercurial > sat_docs
diff scripts/minifier/otr/otr.js @ 12:1596660ddf72
Add minifier script for otr.js and its dependencies
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 03 Sep 2014 19:38:05 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/minifier/otr/otr.js Wed Sep 03 19:38:05 2014 +0200 @@ -0,0 +1,2601 @@ +/*! + + otr.js v0.2.12 - 2014-04-15 + (c) 2014 - Arlo Breault <arlolra@gmail.com> + Freely distributed under the MPL v2.0 license. + + This file is concatenated for the browser. + Please see: https://github.com/arlolra/otr + +*/ + +;(function (root, factory) { + + if (typeof define === 'function' && define.amd) { + define([ + "bigint" + , "crypto" + , "eventemitter" + ], function (BigInt, CryptoJS, EventEmitter) { + var root = { + BigInt: BigInt + , CryptoJS: CryptoJS + , EventEmitter: EventEmitter + , OTR: {} + , DSA: {} + } + return factory.call(root) + }) + } else { + root.OTR = {} + root.DSA = {} + factory.call(root) + } + +}(this, function () { + +;(function () { + "use strict"; + + var root = this + + var CONST = { + + // diffie-heilman + N : 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF' + , G : '2' + + // otr message states + , MSGSTATE_PLAINTEXT : 0 + , MSGSTATE_ENCRYPTED : 1 + , MSGSTATE_FINISHED : 2 + + // otr auth states + , AUTHSTATE_NONE : 0 + , AUTHSTATE_AWAITING_DHKEY : 1 + , AUTHSTATE_AWAITING_REVEALSIG : 2 + , AUTHSTATE_AWAITING_SIG : 3 + + // whitespace tags + , WHITESPACE_TAG : '\x20\x09\x20\x20\x09\x09\x09\x09\x20\x09\x20\x09\x20\x09\x20\x20' + , WHITESPACE_TAG_V2 : '\x20\x20\x09\x09\x20\x20\x09\x20' + , WHITESPACE_TAG_V3 : '\x20\x20\x09\x09\x20\x20\x09\x09' + + // otr tags + , OTR_TAG : '?OTR' + , OTR_VERSION_1 : '\x00\x01' + , OTR_VERSION_2 : '\x00\x02' + , OTR_VERSION_3 : '\x00\x03' + + // smp machine states + , SMPSTATE_EXPECT0 : 0 + , SMPSTATE_EXPECT1 : 1 + , SMPSTATE_EXPECT2 : 2 + , SMPSTATE_EXPECT3 : 3 + , SMPSTATE_EXPECT4 : 4 + + // unstandard status codes + , STATUS_SEND_QUERY : 0 + , STATUS_AKE_INIT : 1 + , STATUS_AKE_SUCCESS : 2 + , STATUS_END_OTR : 3 + + } + + if (typeof module !== 'undefined' && module.exports) { + module.exports = CONST + } else { + root.OTR.CONST = CONST + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var HLP = {}, CryptoJS, BigInt + if (typeof module !== 'undefined' && module.exports) { + module.exports = HLP = {} + CryptoJS = require('../vendor/crypto.js') + BigInt = require('../vendor/bigint.js') + } else { + if (root.OTR) root.OTR.HLP = HLP + if (root.DSA) root.DSA.HLP = HLP + CryptoJS = root.CryptoJS + BigInt = root.BigInt + } + + // data types (byte lengths) + var DTS = { + BYTE : 1 + , SHORT : 2 + , INT : 4 + , CTR : 8 + , MAC : 20 + , SIG : 40 + } + + // otr message wrapper begin and end + var WRAPPER_BEGIN = "?OTR" + , WRAPPER_END = "." + + var TWO = BigInt.str2bigInt('2', 10) + + HLP.debug = function (msg) { + // used as HLP.debug.call(ctx, msg) + if ( this.debug && + typeof this.debug !== 'function' && + typeof console !== 'undefined' + ) console.log(msg) + } + + HLP.extend = function (child, parent) { + for (var key in parent) { + if (Object.hasOwnProperty.call(parent, key)) + child[key] = parent[key] + } + function Ctor() { this.constructor = child } + Ctor.prototype = parent.prototype + child.prototype = new Ctor() + child.__super__ = parent.prototype + } + + // constant-time string comparison + HLP.compare = function (str1, str2) { + if (str1.length !== str2.length) + return false + var i = 0, result = 0 + for (; i < str1.length; i++) + result |= str1[i].charCodeAt(0) ^ str2[i].charCodeAt(0) + return result === 0 + } + + HLP.randomExponent = function () { + return BigInt.randBigInt(1536) + } + + HLP.smpHash = function (version, fmpi, smpi) { + var sha256 = CryptoJS.algo.SHA256.create() + sha256.update(CryptoJS.enc.Latin1.parse(HLP.packBytes(version, DTS.BYTE))) + sha256.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(fmpi))) + if (smpi) sha256.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(smpi))) + var hash = sha256.finalize() + return HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1)) + } + + HLP.makeMac = function (aesctr, m) { + var pass = CryptoJS.enc.Latin1.parse(m) + var mac = CryptoJS.HmacSHA256(CryptoJS.enc.Latin1.parse(aesctr), pass) + return HLP.mask(mac.toString(CryptoJS.enc.Latin1), 0, 160) + } + + HLP.make1Mac = function (aesctr, m) { + var pass = CryptoJS.enc.Latin1.parse(m) + var mac = CryptoJS.HmacSHA1(CryptoJS.enc.Latin1.parse(aesctr), pass) + return mac.toString(CryptoJS.enc.Latin1) + } + + HLP.encryptAes = function (msg, c, iv) { + var opts = { + mode: CryptoJS.mode.CTR + , iv: CryptoJS.enc.Latin1.parse(iv) + , padding: CryptoJS.pad.NoPadding + } + var aesctr = CryptoJS.AES.encrypt( + msg + , CryptoJS.enc.Latin1.parse(c) + , opts + ) + var aesctr_decoded = CryptoJS.enc.Base64.parse(aesctr.toString()) + return CryptoJS.enc.Latin1.stringify(aesctr_decoded) + } + + HLP.decryptAes = function (msg, c, iv) { + msg = CryptoJS.enc.Latin1.parse(msg) + var opts = { + mode: CryptoJS.mode.CTR + , iv: CryptoJS.enc.Latin1.parse(iv) + , padding: CryptoJS.pad.NoPadding + } + return CryptoJS.AES.decrypt( + CryptoJS.enc.Base64.stringify(msg) + , CryptoJS.enc.Latin1.parse(c) + , opts + ) + } + + HLP.multPowMod = function (a, b, c, d, e) { + return BigInt.multMod(BigInt.powMod(a, b, e), BigInt.powMod(c, d, e), e) + } + + HLP.ZKP = function (v, c, d, e) { + return BigInt.equals(c, HLP.smpHash(v, d, e)) + } + + // greater than, or equal + HLP.GTOE = function (a, b) { + return (BigInt.equals(a, b) || BigInt.greater(a, b)) + } + + HLP.between = function (x, a, b) { + return (BigInt.greater(x, a) && BigInt.greater(b, x)) + } + + HLP.checkGroup = function (g, N_MINUS_2) { + return HLP.GTOE(g, TWO) && HLP.GTOE(N_MINUS_2, g) + } + + HLP.h1 = function (b, secbytes) { + var sha1 = CryptoJS.algo.SHA1.create() + sha1.update(CryptoJS.enc.Latin1.parse(b)) + sha1.update(CryptoJS.enc.Latin1.parse(secbytes)) + return (sha1.finalize()).toString(CryptoJS.enc.Latin1) + } + + HLP.h2 = function (b, secbytes) { + var sha256 = CryptoJS.algo.SHA256.create() + sha256.update(CryptoJS.enc.Latin1.parse(b)) + sha256.update(CryptoJS.enc.Latin1.parse(secbytes)) + return (sha256.finalize()).toString(CryptoJS.enc.Latin1) + } + + HLP.mask = function (bytes, start, n) { + return bytes.substr(start / 8, n / 8) + } + + var _toString = String.fromCharCode; + HLP.packBytes = function (val, bytes) { + val = val.toString(16) + var nex, res = '' // big-endian, unsigned long + for (; bytes > 0; bytes--) { + nex = val.length ? val.substr(-2, 2) : '0' + val = val.substr(0, val.length - 2) + res = _toString(parseInt(nex, 16)) + res + } + return res + } + + HLP.packINT = function (d) { + return HLP.packBytes(d, DTS.INT) + } + + HLP.packCtr = function (d) { + return HLP.padCtr(HLP.packBytes(d, DTS.CTR)) + } + + HLP.padCtr = function (ctr) { + return ctr + '\x00\x00\x00\x00\x00\x00\x00\x00' + } + + HLP.unpackCtr = function (d) { + d = HLP.toByteArray(d.substring(0, 8)) + return HLP.unpack(d) + } + + HLP.unpack = function (arr) { + var val = 0, i = 0, len = arr.length + for (; i < len; i++) { + val = (val * 256) + arr[i] + } + return val + } + + HLP.packData = function (d) { + return HLP.packINT(d.length) + d + } + + HLP.bits2bigInt = function (bits) { + bits = HLP.toByteArray(bits) + return BigInt.ba2bigInt(bits) + } + + HLP.packMPI = function (mpi) { + return HLP.packData(BigInt.bigInt2bits(BigInt.trim(mpi, 0))) + } + + HLP.packSHORT = function (short) { + return HLP.packBytes(short, DTS.SHORT) + } + + HLP.unpackSHORT = function (short) { + short = HLP.toByteArray(short) + return HLP.unpack(short) + } + + HLP.packTLV = function (type, value) { + return HLP.packSHORT(type) + HLP.packSHORT(value.length) + value + } + + HLP.readLen = function (msg) { + msg = HLP.toByteArray(msg.substring(0, 4)) + return HLP.unpack(msg) + } + + HLP.readData = function (data) { + var n = HLP.unpack(data.splice(0, 4)) + return [n, data] + } + + HLP.readMPI = function (data) { + data = HLP.toByteArray(data) + data = HLP.readData(data) + return BigInt.ba2bigInt(data[1]) + } + + HLP.packMPIs = function (arr) { + return arr.reduce(function (prv, cur) { + return prv + HLP.packMPI(cur) + }, '') + } + + HLP.unpackMPIs = function (num, mpis) { + var i = 0, arr = [] + for (; i < num; i++) arr.push('MPI') + return (HLP.splitype(arr, mpis)).map(function (m) { + return HLP.readMPI(m) + }) + } + + HLP.wrapMsg = function (msg, fs, v3, our_it, their_it) { + msg = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Latin1.parse(msg)) + msg = WRAPPER_BEGIN + ":" + msg + WRAPPER_END + + var its + if (v3) { + its = '|' + its += (HLP.readLen(our_it)).toString(16) + its += '|' + its += (HLP.readLen(their_it)).toString(16) + } + + if (!fs) return [null, msg] + + var n = Math.ceil(msg.length / fs) + if (n > 65535) return ['Too many fragments'] + if (n == 1) return [null, msg] + + var k, bi, ei, frag, mf, mfs = [] + for (k = 1; k <= n; k++) { + bi = (k - 1) * fs + ei = k * fs + frag = msg.slice(bi, ei) + mf = WRAPPER_BEGIN + if (v3) mf += its + mf += ',' + k + ',' + mf += n + ',' + mf += frag + ',' + mfs.push(mf) + } + + return [null, mfs] + } + + HLP.splitype = function splitype(arr, msg) { + var data = [] + arr.forEach(function (a) { + var str + switch (a) { + case 'PUBKEY': + str = splitype(['SHORT', 'MPI', 'MPI', 'MPI', 'MPI'], msg).join('') + break + case 'DATA': // falls through + case 'MPI': + str = msg.substring(0, HLP.readLen(msg) + 4) + break + default: + str = msg.substring(0, DTS[a]) + } + data.push(str) + msg = msg.substring(str.length) + }) + return data + } + + // https://github.com/msgpack/msgpack-javascript/blob/master/msgpack.js + + var _bin2num = (function () { + var i = 0, _bin2num = {} + for (; i < 0x100; ++i) { + _bin2num[String.fromCharCode(i)] = i // "\00" -> 0x00 + } + for (i = 0x80; i < 0x100; ++i) { // [Webkit][Gecko] + _bin2num[String.fromCharCode(0xf700 + i)] = i // "\f780" -> 0x80 + } + return _bin2num + }()) + + HLP.toByteArray = function (data) { + var rv = [] + , ary = data.split("") + , i = -1 + , iz = ary.length + , remain = iz % 8 + + while (remain--) { + ++i + rv[i] = _bin2num[ary[i]] + } + remain = iz >> 3 + while (remain--) { + rv.push(_bin2num[ary[++i]], _bin2num[ary[++i]], + _bin2num[ary[++i]], _bin2num[ary[++i]], + _bin2num[ary[++i]], _bin2num[ary[++i]], + _bin2num[ary[++i]], _bin2num[ary[++i]]) + } + return rv + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var CryptoJS, BigInt, Worker, WWPath, HLP + if (typeof module !== 'undefined' && module.exports) { + module.exports = DSA + CryptoJS = require('../vendor/crypto.js') + BigInt = require('../vendor/bigint.js') + WWPath = require('path').join(__dirname, '/dsa-webworker.js') + HLP = require('./helpers.js') + } else { + // copy over and expose internals + Object.keys(root.DSA).forEach(function (k) { + DSA[k] = root.DSA[k] + }) + root.DSA = DSA + CryptoJS = root.CryptoJS + BigInt = root.BigInt + Worker = root.Worker + WWPath = 'dsa-webworker.js' + HLP = DSA.HLP + } + + var ZERO = BigInt.str2bigInt('0', 10) + , ONE = BigInt.str2bigInt('1', 10) + , TWO = BigInt.str2bigInt('2', 10) + , KEY_TYPE = '\x00\x00' + + var DEBUG = false + function timer() { + var start = (new Date()).getTime() + return function (s) { + if (!DEBUG || typeof console === 'undefined') return + var t = (new Date()).getTime() + console.log(s + ': ' + (t - start)) + start = t + } + } + + function makeRandom(min, max) { + var c = BigInt.randBigInt(BigInt.bitSize(max)) + if (!HLP.between(c, min, max)) return makeRandom(min, max) + return c + } + + // altered BigInt.randProbPrime() + // n rounds of Miller Rabin (after trial division with small primes) + var rpprb = [] + function isProbPrime(k, n) { + var i, B = 30000, l = BigInt.bitSize(k) + var primes = BigInt.primes + + if (primes.length === 0) + primes = BigInt.findPrimes(B) + + if (rpprb.length != k.length) + rpprb = BigInt.dup(k) + + // check ans for divisibility by small primes up to B + for (i = 0; (i < primes.length) && (primes[i] <= B); i++) + if (BigInt.modInt(k, primes[i]) === 0 && !BigInt.equalsInt(k, primes[i])) + return 0 + + // do n rounds of Miller Rabin, with random bases less than k + for (i = 0; i < n; i++) { + BigInt.randBigInt_(rpprb, l, 0) + while(!BigInt.greater(k, rpprb)) // pick a random rpprb that's < k + BigInt.randBigInt_(rpprb, l, 0) + if (!BigInt.millerRabin(k, rpprb)) + return 0 + } + + return 1 + } + + var bit_lengths = { + '1024': { N: 160, repeat: 40 } // 40x should give 2^-80 confidence + , '2048': { N: 224, repeat: 56 } + } + + var primes = {} + + // follows go lang http://golang.org/src/pkg/crypto/dsa/dsa.go + // fips version was removed in 0c99af0df3e7 + function generatePrimes(bit_length) { + + var t = timer() // for debugging + + // number of MR tests to perform + var repeat = bit_lengths[bit_length].repeat + + var N = bit_lengths[bit_length].N + + var LM1 = BigInt.twoToThe(bit_length - 1) + var bl4 = 4 * bit_length + var brk = false + + var q, p, rem, counter + for (;;) { + + q = BigInt.randBigInt(N, 1) + q[0] |= 1 + + if (!isProbPrime(q, repeat)) continue + t('q') + + for (counter = 0; counter < bl4; counter++) { + p = BigInt.randBigInt(bit_length, 1) + p[0] |= 1 + + rem = BigInt.mod(p, q) + rem = BigInt.sub(rem, ONE) + p = BigInt.sub(p, rem) + + if (BigInt.greater(LM1, p)) continue + if (!isProbPrime(p, repeat)) continue + + t('p') + primes[bit_length] = { p: p, q: q } + brk = true + break + } + + if (brk) break + } + + var h = BigInt.dup(TWO) + var pm1 = BigInt.sub(p, ONE) + var e = BigInt.multMod(pm1, BigInt.inverseMod(q, p), p) + + var g + for (;;) { + g = BigInt.powMod(h, e, p) + if (BigInt.equals(g, ONE)) { + h = BigInt.add(h, ONE) + continue + } + primes[bit_length].g = g + t('g') + return + } + + throw new Error('Unreachable!') + } + + function DSA(obj, opts) { + if (!(this instanceof DSA)) return new DSA(obj, opts) + + // options + opts = opts || {} + + // inherit + if (obj) { + var self = this + ;['p', 'q', 'g', 'y', 'x'].forEach(function (prop) { + self[prop] = obj[prop] + }) + this.type = obj.type || KEY_TYPE + return + } + + // default to 1024 + var bit_length = parseInt(opts.bit_length ? opts.bit_length : 1024, 10) + + if (!bit_lengths[bit_length]) + throw new Error('Unsupported bit length.') + + // set primes + if (!primes[bit_length]) + generatePrimes(bit_length) + + this.p = primes[bit_length].p + this.q = primes[bit_length].q + this.g = primes[bit_length].g + + // key type + this.type = KEY_TYPE + + // private key + this.x = makeRandom(ZERO, this.q) + + // public keys (p, q, g, y) + this.y = BigInt.powMod(this.g, this.x, this.p) + + // nocache? + if (opts.nocache) primes[bit_length] = null + } + + DSA.prototype = { + + constructor: DSA, + + packPublic: function () { + var str = this.type + str += HLP.packMPI(this.p) + str += HLP.packMPI(this.q) + str += HLP.packMPI(this.g) + str += HLP.packMPI(this.y) + return str + }, + + packPrivate: function () { + var str = this.packPublic() + HLP.packMPI(this.x) + str = CryptoJS.enc.Latin1.parse(str) + return str.toString(CryptoJS.enc.Base64) + }, + + // http://www.imperialviolet.org/2013/06/15/suddendeathentropy.html + generateNonce: function (m) { + var priv = BigInt.bigInt2bits(BigInt.trim(this.x, 0)) + var rand = BigInt.bigInt2bits(BigInt.randBigInt(256)) + + var sha256 = CryptoJS.algo.SHA256.create() + sha256.update(CryptoJS.enc.Latin1.parse(priv)) + sha256.update(m) + sha256.update(CryptoJS.enc.Latin1.parse(rand)) + + var hash = sha256.finalize() + hash = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1)) + BigInt.rightShift_(hash, 256 - BigInt.bitSize(this.q)) + + return HLP.between(hash, ZERO, this.q) ? hash : this.generateNonce(m) + }, + + sign: function (m) { + m = CryptoJS.enc.Latin1.parse(m) + var b = BigInt.str2bigInt(m.toString(CryptoJS.enc.Hex), 16) + var k, r = ZERO, s = ZERO + while (BigInt.isZero(s) || BigInt.isZero(r)) { + k = this.generateNonce(m) + r = BigInt.mod(BigInt.powMod(this.g, k, this.p), this.q) + if (BigInt.isZero(r)) continue + s = BigInt.inverseMod(k, this.q) + s = BigInt.mult(s, BigInt.add(b, BigInt.mult(this.x, r))) + s = BigInt.mod(s, this.q) + } + return [r, s] + }, + + fingerprint: function () { + var pk = this.packPublic() + if (this.type === KEY_TYPE) pk = pk.substring(2) + pk = CryptoJS.enc.Latin1.parse(pk) + return CryptoJS.SHA1(pk).toString(CryptoJS.enc.Hex) + } + + } + + DSA.parsePublic = function (str, priv) { + var fields = ['SHORT', 'MPI', 'MPI', 'MPI', 'MPI'] + if (priv) fields.push('MPI') + str = HLP.splitype(fields, str) + var obj = { + type: str[0] + , p: HLP.readMPI(str[1]) + , q: HLP.readMPI(str[2]) + , g: HLP.readMPI(str[3]) + , y: HLP.readMPI(str[4]) + } + if (priv) obj.x = HLP.readMPI(str[5]) + return new DSA(obj) + } + + function tokenizeStr(str) { + var start, end + + start = str.indexOf("(") + end = str.lastIndexOf(")") + + if (start < 0 || end < 0) + throw new Error("Malformed S-Expression") + + str = str.substring(start + 1, end) + + var splt = str.search(/\s/) + var obj = { + type: str.substring(0, splt) + , val: [] + } + + str = str.substring(splt + 1, end) + start = str.indexOf("(") + + if (start < 0) obj.val.push(str) + else { + + var i, len, ss, es + while (start > -1) { + i = start + 1 + len = str.length + for (ss = 1, es = 0; i < len && es < ss; i++) { + if (str[i] === "(") ss++ + if (str[i] === ")") es++ + } + obj.val.push(tokenizeStr(str.substring(start, ++i))) + str = str.substring(++i) + start = str.indexOf("(") + } + + } + return obj + } + + function parseLibotr(obj) { + if (!obj.type) throw new Error("Parse error.") + + var o, val + if (obj.type === "privkeys") { + o = [] + obj.val.forEach(function (i) { + o.push(parseLibotr(i)) + }) + return o + } + + o = {} + obj.val.forEach(function (i) { + + val = i.val[0] + if (typeof val === "string") { + + if (val.indexOf("#") === 0) { + val = val.substring(1, val.lastIndexOf("#")) + val = BigInt.str2bigInt(val, 16) + } + + } else { + val = parseLibotr(i) + } + + o[i.type] = val + }) + + return o + } + + DSA.parsePrivate = function (str, libotr) { + if (!libotr) { + str = CryptoJS.enc.Base64.parse(str) + str = str.toString(CryptoJS.enc.Latin1) + return DSA.parsePublic(str, true) + } + // only returning the first key found + return parseLibotr(tokenizeStr(str))[0]["private-key"].dsa + } + + DSA.verify = function (key, m, r, s) { + if (!HLP.between(r, ZERO, key.q) || !HLP.between(s, ZERO, key.q)) + return false + + var hm = CryptoJS.enc.Latin1.parse(m) // CryptoJS.SHA1(m) + hm = BigInt.str2bigInt(hm.toString(CryptoJS.enc.Hex), 16) + + var w = BigInt.inverseMod(s, key.q) + var u1 = BigInt.multMod(hm, w, key.q) + var u2 = BigInt.multMod(r, w, key.q) + + u1 = BigInt.powMod(key.g, u1, key.p) + u2 = BigInt.powMod(key.y, u2, key.p) + + var v = BigInt.mod(BigInt.multMod(u1, u2, key.p), key.q) + + return BigInt.equals(v, r) + } + + DSA.createInWebWorker = function (options, cb) { + var opts = { + path: WWPath + , seed: BigInt.getSeed + } + if (options && typeof options === 'object') + Object.keys(options).forEach(function (k) { + opts[k] = options[k] + }) + + // load optional dep. in node + if (typeof module !== 'undefined' && module.exports) + Worker = require('webworker-threads').Worker + + var worker = new Worker(opts.path) + worker.onmessage = function (e) { + var data = e.data + switch (data.type) { + case "debug": + if (!DEBUG || typeof console === 'undefined') return + console.log(data.val) + break; + case "data": + worker.terminate() + cb(DSA.parsePrivate(data.val)) + break; + default: + throw new Error("Unrecognized type.") + } + } + worker.postMessage({ + seed: opts.seed() + , imports: opts.imports + , debug: DEBUG + }) + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var Parse = {}, CryptoJS, CONST, HLP + if (typeof module !== 'undefined' && module.exports) { + module.exports = Parse + CryptoJS = require('../vendor/crypto.js') + CONST = require('./const.js') + HLP = require('./helpers.js') + } else { + root.OTR.Parse = Parse + CryptoJS = root.CryptoJS + CONST = root.OTR.CONST + HLP = root.OTR.HLP + } + + // whitespace tags + var tags = {} + tags[CONST.WHITESPACE_TAG_V2] = CONST.OTR_VERSION_2 + tags[CONST.WHITESPACE_TAG_V3] = CONST.OTR_VERSION_3 + + Parse.parseMsg = function (otr, msg) { + + var ver = [] + + // is this otr? + var start = msg.indexOf(CONST.OTR_TAG) + if (!~start) { + + // restart fragments + this.initFragment(otr) + + // whitespace tags + ind = msg.indexOf(CONST.WHITESPACE_TAG) + + if (~ind) { + + msg = msg.split('') + msg.splice(ind, 16) + + var tag, len = msg.length + for (; ind < len;) { + tag = msg.slice(ind, ind + 8).join('') + if (Object.hasOwnProperty.call(tags, tag)) { + msg.splice(ind, 8) + ver.push(tags[tag]) + continue + } + ind += 8 + } + + msg = msg.join('') + + } + + return { msg: msg, ver: ver } + } + + var ind = start + CONST.OTR_TAG.length + var com = msg[ind] + + // message fragment + if (com === ',' || com === '|') { + return this.msgFragment(otr, msg.substring(ind + 1), (com === '|')) + } + + this.initFragment(otr) + + // query message + if (~['?', 'v'].indexOf(com)) { + + // version 1 + if (msg[ind] === '?') { + ver.push(CONST.OTR_VERSION_1) + ind += 1 + } + + // other versions + var vers = { + '2': CONST.OTR_VERSION_2 + , '3': CONST.OTR_VERSION_3 + } + var qs = msg.substring(ind + 1) + var qi = qs.indexOf('?') + + if (qi >= 1) { + qs = qs.substring(0, qi).split('') + if (msg[ind] === 'v') { + qs.forEach(function (q) { + if (Object.hasOwnProperty.call(vers, q)) ver.push(vers[q]) + }) + } + } + + return { cls: 'query', ver: ver } + } + + // otr message + if (com === ':') { + + ind += 1 + + var info = msg.substring(ind, ind + 4) + if (info.length < 4) return { msg: msg } + info = CryptoJS.enc.Base64.parse(info).toString(CryptoJS.enc.Latin1) + + var version = info.substring(0, 2) + var type = info.substring(2) + + // supporting otr versions 2 and 3 + if (!otr['ALLOW_V' + HLP.unpackSHORT(version)]) return { msg: msg } + + ind += 4 + + var end = msg.substring(ind).indexOf('.') + if (!~end) return { msg: msg } + + msg = CryptoJS.enc.Base64.parse(msg.substring(ind, ind + end)) + msg = CryptoJS.enc.Latin1.stringify(msg) + + // instance tags + var instance_tags + if (version === CONST.OTR_VERSION_3) { + instance_tags = msg.substring(0, 8) + msg = msg.substring(8) + } + + var cls + if (~['\x02', '\x0a', '\x11', '\x12'].indexOf(type)) { + cls = 'ake' + } else if (type === '\x03') { + cls = 'data' + } + + return { + version: version + , type: type + , msg: msg + , cls: cls + , instance_tags: instance_tags + } + } + + // error message + if (msg.substring(ind, ind + 7) === ' Error:') { + if (otr.ERROR_START_AKE) { + otr.sendQueryMsg() + } + return { msg: msg.substring(ind + 7), cls: 'error' } + } + + return { msg: msg } + } + + Parse.initFragment = function (otr) { + otr.fragment = { s: '', j: 0, k: 0 } + } + + Parse.msgFragment = function (otr, msg, v3) { + + msg = msg.split(',') + + // instance tags + if (v3) { + var its = msg.shift().split('|') + var their_it = HLP.packINT(parseInt(its[0], 16)) + var our_it = HLP.packINT(parseInt(its[1], 16)) + if (otr.checkInstanceTags(their_it + our_it)) return // ignore + } + + if (msg.length < 4 || + isNaN(parseInt(msg[0], 10)) || + isNaN(parseInt(msg[1], 10)) + ) return + + var k = parseInt(msg[0], 10) + var n = parseInt(msg[1], 10) + msg = msg[2] + + if (n < k || n === 0 || k === 0) { + this.initFragment(otr) + return + } + + if (k === 1) { + this.initFragment(otr) + otr.fragment = { k: 1, n: n, s: msg } + } else if (n === otr.fragment.n && k === (otr.fragment.k + 1)) { + otr.fragment.s += msg + otr.fragment.k += 1 + } else { + this.initFragment(otr) + } + + if (n === k) { + msg = otr.fragment.s + this.initFragment(otr) + return this.parseMsg(otr, msg) + } + + return + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var CryptoJS, BigInt, CONST, HLP, DSA + if (typeof module !== 'undefined' && module.exports) { + module.exports = AKE + CryptoJS = require('../vendor/crypto.js') + BigInt = require('../vendor/bigint.js') + CONST = require('./const.js') + HLP = require('./helpers.js') + DSA = require('./dsa.js') + } else { + root.OTR.AKE = AKE + CryptoJS = root.CryptoJS + BigInt = root.BigInt + CONST = root.OTR.CONST + HLP = root.OTR.HLP + DSA = root.DSA + } + + // diffie-hellman modulus + // see group 5, RFC 3526 + var N = BigInt.str2bigInt(CONST.N, 16) + var N_MINUS_2 = BigInt.sub(N, BigInt.str2bigInt('2', 10)) + + function hMac(gx, gy, pk, kid, m) { + var pass = CryptoJS.enc.Latin1.parse(m) + var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, pass) + hmac.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(gx))) + hmac.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(gy))) + hmac.update(CryptoJS.enc.Latin1.parse(pk)) + hmac.update(CryptoJS.enc.Latin1.parse(kid)) + return (hmac.finalize()).toString(CryptoJS.enc.Latin1) + } + + // AKE constructor + function AKE(otr) { + if (!(this instanceof AKE)) return new AKE(otr) + + // otr instance + this.otr = otr + + // our keys + this.our_dh = otr.our_old_dh + this.our_keyid = otr.our_keyid - 1 + + // their keys + this.their_y = null + this.their_keyid = null + this.their_priv_pk = null + + // state + this.ssid = null + this.transmittedRS = false + this.r = null + + // bind methods + var self = this + ;['sendMsg'].forEach(function (meth) { + self[meth] = self[meth].bind(self) + }) + } + + AKE.prototype = { + + constructor: AKE, + + createKeys: function(g) { + var s = BigInt.powMod(g, this.our_dh.privateKey, N) + var secbytes = HLP.packMPI(s) + this.ssid = HLP.mask(HLP.h2('\x00', secbytes), 0, 64) // first 64-bits + var tmp = HLP.h2('\x01', secbytes) + this.c = HLP.mask(tmp, 0, 128) // first 128-bits + this.c_prime = HLP.mask(tmp, 128, 128) // second 128-bits + this.m1 = HLP.h2('\x02', secbytes) + this.m2 = HLP.h2('\x03', secbytes) + this.m1_prime = HLP.h2('\x04', secbytes) + this.m2_prime = HLP.h2('\x05', secbytes) + }, + + verifySignMac: function (mac, aesctr, m2, c, their_y, our_dh_pk, m1, ctr) { + // verify mac + var vmac = HLP.makeMac(aesctr, m2) + if (!HLP.compare(mac, vmac)) + return ['MACs do not match.'] + + // decrypt x + var x = HLP.decryptAes(aesctr.substring(4), c, ctr) + x = HLP.splitype(['PUBKEY', 'INT', 'SIG'], x.toString(CryptoJS.enc.Latin1)) + + var m = hMac(their_y, our_dh_pk, x[0], x[1], m1) + var pub = DSA.parsePublic(x[0]) + + var r = HLP.bits2bigInt(x[2].substring(0, 20)) + var s = HLP.bits2bigInt(x[2].substring(20)) + + // verify sign m + if (!DSA.verify(pub, m, r, s)) return ['Cannot verify signature of m.'] + + return [null, HLP.readLen(x[1]), pub] + }, + + makeM: function (their_y, m1, c, m2) { + var pk = this.otr.priv.packPublic() + var kid = HLP.packINT(this.our_keyid) + var m = hMac(this.our_dh.publicKey, their_y, pk, kid, m1) + m = this.otr.priv.sign(m) + var msg = pk + kid + msg += BigInt.bigInt2bits(m[0], 20) // pad to 20 bytes + msg += BigInt.bigInt2bits(m[1], 20) + msg = CryptoJS.enc.Latin1.parse(msg) + var aesctr = HLP.packData(HLP.encryptAes(msg, c, HLP.packCtr(0))) + var mac = HLP.makeMac(aesctr, m2) + return aesctr + mac + }, + + akeSuccess: function (version) { + HLP.debug.call(this.otr, 'success') + + if (BigInt.equals(this.their_y, this.our_dh.publicKey)) + return this.otr.error('equal keys - we have a problem.', true) + + this.otr.our_old_dh = this.our_dh + this.otr.their_priv_pk = this.their_priv_pk + + if (!( + (this.their_keyid === this.otr.their_keyid && + BigInt.equals(this.their_y, this.otr.their_y)) || + (this.their_keyid === (this.otr.their_keyid - 1) && + BigInt.equals(this.their_y, this.otr.their_old_y)) + )) { + + this.otr.their_y = this.their_y + this.otr.their_old_y = null + this.otr.their_keyid = this.their_keyid + + // rotate keys + this.otr.sessKeys[0] = [ new this.otr.DHSession( + this.otr.our_dh + , this.otr.their_y + ), null ] + this.otr.sessKeys[1] = [ new this.otr.DHSession( + this.otr.our_old_dh + , this.otr.their_y + ), null ] + + } + + // ake info + this.otr.ssid = this.ssid + this.otr.transmittedRS = this.transmittedRS + this.otr_version = version + + // go encrypted + this.otr.authstate = CONST.AUTHSTATE_NONE + this.otr.msgstate = CONST.MSGSTATE_ENCRYPTED + + // null out values + this.r = null + this.myhashed = null + this.dhcommit = null + this.encrypted = null + this.hashed = null + + this.otr.trigger('status', [CONST.STATUS_AKE_SUCCESS]) + + // send stored msgs + this.otr.sendStored() + }, + + handleAKE: function (msg) { + var send, vsm, type + var version = msg.version + + switch (msg.type) { + + case '\x02': + HLP.debug.call(this.otr, 'd-h key message') + + msg = HLP.splitype(['DATA', 'DATA'], msg.msg) + + if (this.otr.authstate === CONST.AUTHSTATE_AWAITING_DHKEY) { + var ourHash = HLP.readMPI(this.myhashed) + var theirHash = HLP.readMPI(msg[1]) + if (BigInt.greater(ourHash, theirHash)) { + type = '\x02' + send = this.dhcommit + break // ignore + } else { + // forget + this.our_dh = this.otr.dh() + this.otr.authstate = CONST.AUTHSTATE_NONE + this.r = null + this.myhashed = null + } + } else if ( + this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG + ) this.our_dh = this.otr.dh() + + this.otr.authstate = CONST.AUTHSTATE_AWAITING_REVEALSIG + + this.encrypted = msg[0].substring(4) + this.hashed = msg[1].substring(4) + + type = '\x0a' + send = HLP.packMPI(this.our_dh.publicKey) + break + + case '\x0a': + HLP.debug.call(this.otr, 'reveal signature message') + + msg = HLP.splitype(['MPI'], msg.msg) + + if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_DHKEY) { + if (this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG) { + if (!BigInt.equals(this.their_y, HLP.readMPI(msg[0]))) return + } else { + return // ignore + } + } + + this.otr.authstate = CONST.AUTHSTATE_AWAITING_SIG + + this.their_y = HLP.readMPI(msg[0]) + + // verify gy is legal 2 <= gy <= N-2 + if (!HLP.checkGroup(this.their_y, N_MINUS_2)) + return this.otr.error('Illegal g^y.', true) + + this.createKeys(this.their_y) + + type = '\x11' + send = HLP.packMPI(this.r) + send += this.makeM(this.their_y, this.m1, this.c, this.m2) + + this.m1 = null + this.m2 = null + this.c = null + break + + case '\x11': + HLP.debug.call(this.otr, 'signature message') + + if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_REVEALSIG) + return // ignore + + msg = HLP.splitype(['DATA', 'DATA', 'MAC'], msg.msg) + + this.r = HLP.readMPI(msg[0]) + + // decrypt their_y + var key = CryptoJS.enc.Hex.parse(BigInt.bigInt2str(this.r, 16)) + key = CryptoJS.enc.Latin1.stringify(key) + + var gxmpi = HLP.decryptAes(this.encrypted, key, HLP.packCtr(0)) + gxmpi = gxmpi.toString(CryptoJS.enc.Latin1) + + this.their_y = HLP.readMPI(gxmpi) + + // verify hash + var hash = CryptoJS.SHA256(CryptoJS.enc.Latin1.parse(gxmpi)) + + if (!HLP.compare(this.hashed, hash.toString(CryptoJS.enc.Latin1))) + return this.otr.error('Hashed g^x does not match.', true) + + // verify gx is legal 2 <= g^x <= N-2 + if (!HLP.checkGroup(this.their_y, N_MINUS_2)) + return this.otr.error('Illegal g^x.', true) + + this.createKeys(this.their_y) + + vsm = this.verifySignMac( + msg[2] + , msg[1] + , this.m2 + , this.c + , this.their_y + , this.our_dh.publicKey + , this.m1 + , HLP.packCtr(0) + ) + if (vsm[0]) return this.otr.error(vsm[0], true) + + // store their key + this.their_keyid = vsm[1] + this.their_priv_pk = vsm[2] + + send = this.makeM( + this.their_y + , this.m1_prime + , this.c_prime + , this.m2_prime + ) + + this.m1 = null + this.m2 = null + this.m1_prime = null + this.m2_prime = null + this.c = null + this.c_prime = null + + this.sendMsg(version, '\x12', send) + this.akeSuccess(version) + return + + case '\x12': + HLP.debug.call(this.otr, 'data message') + + if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_SIG) + return // ignore + + msg = HLP.splitype(['DATA', 'MAC'], msg.msg) + + vsm = this.verifySignMac( + msg[1] + , msg[0] + , this.m2_prime + , this.c_prime + , this.their_y + , this.our_dh.publicKey + , this.m1_prime + , HLP.packCtr(0) + ) + if (vsm[0]) return this.otr.error(vsm[0], true) + + // store their key + this.their_keyid = vsm[1] + this.their_priv_pk = vsm[2] + + this.m1_prime = null + this.m2_prime = null + this.c_prime = null + + this.transmittedRS = true + this.akeSuccess(version) + return + + default: + return // ignore + + } + + this.sendMsg(version, type, send) + }, + + sendMsg: function (version, type, msg) { + var send = version + type + var v3 = (version === CONST.OTR_VERSION_3) + + // instance tags for v3 + if (v3) { + HLP.debug.call(this.otr, 'instance tags') + send += this.otr.our_instance_tag + send += this.otr.their_instance_tag + } + + send += msg + + // fragment message if necessary + send = HLP.wrapMsg( + send + , this.otr.fragment_size + , v3 + , this.otr.our_instance_tag + , this.otr.their_instance_tag + ) + if (send[0]) return this.otr.error(send[0]) + + this.otr.io(send[1]) + }, + + initiateAKE: function (version) { + HLP.debug.call(this.otr, 'd-h commit message') + + this.otr.trigger('status', [CONST.STATUS_AKE_INIT]) + + this.otr.authstate = CONST.AUTHSTATE_AWAITING_DHKEY + + var gxmpi = HLP.packMPI(this.our_dh.publicKey) + gxmpi = CryptoJS.enc.Latin1.parse(gxmpi) + + this.r = BigInt.randBigInt(128) + var key = CryptoJS.enc.Hex.parse(BigInt.bigInt2str(this.r, 16)) + key = CryptoJS.enc.Latin1.stringify(key) + + this.myhashed = CryptoJS.SHA256(gxmpi) + this.myhashed = HLP.packData(this.myhashed.toString(CryptoJS.enc.Latin1)) + + this.dhcommit = HLP.packData(HLP.encryptAes(gxmpi, key, HLP.packCtr(0))) + this.dhcommit += this.myhashed + + this.sendMsg(version, '\x02', this.dhcommit) + } + + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var CryptoJS, BigInt, EventEmitter, CONST, HLP + if (typeof module !== 'undefined' && module.exports) { + module.exports = SM + CryptoJS = require('../vendor/crypto.js') + BigInt = require('../vendor/bigint.js') + EventEmitter = require('../vendor/eventemitter.js') + CONST = require('./const.js') + HLP = require('./helpers.js') + } else { + root.OTR.SM = SM + CryptoJS = root.CryptoJS + BigInt = root.BigInt + EventEmitter = root.EventEmitter + CONST = root.OTR.CONST + HLP = root.OTR.HLP + } + + // diffie-hellman modulus and generator + // see group 5, RFC 3526 + var G = BigInt.str2bigInt(CONST.G, 10) + var N = BigInt.str2bigInt(CONST.N, 16) + var N_MINUS_2 = BigInt.sub(N, BigInt.str2bigInt('2', 10)) + + // to calculate D's for zero-knowledge proofs + var Q = BigInt.sub(N, BigInt.str2bigInt('1', 10)) + BigInt.divInt_(Q, 2) // meh + + function SM(reqs) { + if (!(this instanceof SM)) return new SM(reqs) + + this.version = 1 + + this.our_fp = reqs.our_fp + this.their_fp = reqs.their_fp + this.ssid = reqs.ssid + + this.debug = !!reqs.debug + + // initial state + this.init() + } + + // inherit from EE + HLP.extend(SM, EventEmitter) + + // set the initial values + // also used when aborting + SM.prototype.init = function () { + this.smpstate = CONST.SMPSTATE_EXPECT1 + this.secret = null + } + + SM.prototype.makeSecret = function (our, secret) { + var sha256 = CryptoJS.algo.SHA256.create() + sha256.update(CryptoJS.enc.Latin1.parse(HLP.packBytes(this.version, 1))) + sha256.update(CryptoJS.enc.Hex.parse(our ? this.our_fp : this.their_fp)) + sha256.update(CryptoJS.enc.Hex.parse(our ? this.their_fp : this.our_fp)) + sha256.update(CryptoJS.enc.Latin1.parse(this.ssid)) + sha256.update(CryptoJS.enc.Latin1.parse(secret)) + var hash = sha256.finalize() + this.secret = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1)) + } + + SM.prototype.makeG2s = function () { + this.a2 = HLP.randomExponent() + this.a3 = HLP.randomExponent() + this.g2a = BigInt.powMod(G, this.a2, N) + this.g3a = BigInt.powMod(G, this.a3, N) + if ( !HLP.checkGroup(this.g2a, N_MINUS_2) || + !HLP.checkGroup(this.g3a, N_MINUS_2) + ) this.makeG2s() + } + + SM.prototype.computeGs = function (g2a, g3a) { + this.g2 = BigInt.powMod(g2a, this.a2, N) + this.g3 = BigInt.powMod(g3a, this.a3, N) + } + + SM.prototype.computePQ = function (r) { + this.p = BigInt.powMod(this.g3, r, N) + this.q = HLP.multPowMod(G, r, this.g2, this.secret, N) + } + + SM.prototype.computeR = function () { + this.r = BigInt.powMod(this.QoQ, this.a3, N) + } + + SM.prototype.computeRab = function (r) { + return BigInt.powMod(r, this.a3, N) + } + + SM.prototype.computeC = function (v, r) { + return HLP.smpHash(v, BigInt.powMod(G, r, N)) + } + + SM.prototype.computeD = function (r, a, c) { + return BigInt.subMod(r, BigInt.multMod(a, c, Q), Q) + } + + // the bulk of the work + SM.prototype.handleSM = function (msg) { + var send, r2, r3, r7, t1, t2, t3, t4, rab, tmp2, cR, d7, ms, trust + + var expectStates = { + 2: CONST.SMPSTATE_EXPECT1 + , 3: CONST.SMPSTATE_EXPECT2 + , 4: CONST.SMPSTATE_EXPECT3 + , 5: CONST.SMPSTATE_EXPECT4 + , 7: CONST.SMPSTATE_EXPECT1 + } + + if (msg.type === 6) { + this.init() + this.trigger('abort') + return + } + + // abort! there was an error + if (this.smpstate !== expectStates[msg.type]) + return this.abort() + + switch (this.smpstate) { + + case CONST.SMPSTATE_EXPECT1: + HLP.debug.call(this, 'smp tlv 2') + + // user specified question + var ind, question + if (msg.type === 7) { + ind = msg.msg.indexOf('\x00') + question = msg.msg.substring(0, ind) + msg.msg = msg.msg.substring(ind + 1) + } + + // 0:g2a, 1:c2, 2:d2, 3:g3a, 4:c3, 5:d3 + ms = HLP.readLen(msg.msg.substr(0, 4)) + if (ms !== 6) return this.abort() + msg = HLP.unpackMPIs(6, msg.msg.substring(4)) + + if ( !HLP.checkGroup(msg[0], N_MINUS_2) || + !HLP.checkGroup(msg[3], N_MINUS_2) + ) return this.abort() + + // verify znp's + if (!HLP.ZKP(1, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N))) + return this.abort() + + if (!HLP.ZKP(2, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N))) + return this.abort() + + this.g3ao = msg[3] // save for later + + this.makeG2s() + + // zero-knowledge proof that the exponents + // associated with g2a & g3a are known + r2 = HLP.randomExponent() + r3 = HLP.randomExponent() + this.c2 = this.computeC(3, r2) + this.c3 = this.computeC(4, r3) + this.d2 = this.computeD(r2, this.a2, this.c2) + this.d3 = this.computeD(r3, this.a3, this.c3) + + this.computeGs(msg[0], msg[3]) + + this.smpstate = CONST.SMPSTATE_EXPECT0 + + // assume utf8 question + question = CryptoJS.enc.Latin1 + .parse(question) + .toString(CryptoJS.enc.Utf8) + + // invoke question + this.trigger('question', [question]) + return + + case CONST.SMPSTATE_EXPECT2: + HLP.debug.call(this, 'smp tlv 3') + + // 0:g2a, 1:c2, 2:d2, 3:g3a, 4:c3, 5:d3, 6:p, 7:q, 8:cP, 9:d5, 10:d6 + ms = HLP.readLen(msg.msg.substr(0, 4)) + if (ms !== 11) return this.abort() + msg = HLP.unpackMPIs(11, msg.msg.substring(4)) + + if ( !HLP.checkGroup(msg[0], N_MINUS_2) || + !HLP.checkGroup(msg[3], N_MINUS_2) || + !HLP.checkGroup(msg[6], N_MINUS_2) || + !HLP.checkGroup(msg[7], N_MINUS_2) + ) return this.abort() + + // verify znp of c3 / c3 + if (!HLP.ZKP(3, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N))) + return this.abort() + + if (!HLP.ZKP(4, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N))) + return this.abort() + + this.g3ao = msg[3] // save for later + + this.computeGs(msg[0], msg[3]) + + // verify znp of cP + t1 = HLP.multPowMod(this.g3, msg[9], msg[6], msg[8], N) + t2 = HLP.multPowMod(G, msg[9], this.g2, msg[10], N) + t2 = BigInt.multMod(t2, BigInt.powMod(msg[7], msg[8], N), N) + + if (!HLP.ZKP(5, msg[8], t1, t2)) + return this.abort() + + var r4 = HLP.randomExponent() + this.computePQ(r4) + + // zero-knowledge proof that P & Q + // were generated according to the protocol + var r5 = HLP.randomExponent() + var r6 = HLP.randomExponent() + var tmp = HLP.multPowMod(G, r5, this.g2, r6, N) + var cP = HLP.smpHash(6, BigInt.powMod(this.g3, r5, N), tmp) + var d5 = this.computeD(r5, r4, cP) + var d6 = this.computeD(r6, this.secret, cP) + + // store these + this.QoQ = BigInt.divMod(this.q, msg[7], N) + this.PoP = BigInt.divMod(this.p, msg[6], N) + + this.computeR() + + // zero-knowledge proof that R + // was generated according to the protocol + r7 = HLP.randomExponent() + tmp2 = BigInt.powMod(this.QoQ, r7, N) + cR = HLP.smpHash(7, BigInt.powMod(G, r7, N), tmp2) + d7 = this.computeD(r7, this.a3, cR) + + this.smpstate = CONST.SMPSTATE_EXPECT4 + + send = HLP.packINT(8) + HLP.packMPIs([ + this.p + , this.q + , cP + , d5 + , d6 + , this.r + , cR + , d7 + ]) + + // TLV + send = HLP.packTLV(4, send) + break + + case CONST.SMPSTATE_EXPECT3: + HLP.debug.call(this, 'smp tlv 4') + + // 0:p, 1:q, 2:cP, 3:d5, 4:d6, 5:r, 6:cR, 7:d7 + ms = HLP.readLen(msg.msg.substr(0, 4)) + if (ms !== 8) return this.abort() + msg = HLP.unpackMPIs(8, msg.msg.substring(4)) + + if ( !HLP.checkGroup(msg[0], N_MINUS_2) || + !HLP.checkGroup(msg[1], N_MINUS_2) || + !HLP.checkGroup(msg[5], N_MINUS_2) + ) return this.abort() + + // verify znp of cP + t1 = HLP.multPowMod(this.g3, msg[3], msg[0], msg[2], N) + t2 = HLP.multPowMod(G, msg[3], this.g2, msg[4], N) + t2 = BigInt.multMod(t2, BigInt.powMod(msg[1], msg[2], N), N) + + if (!HLP.ZKP(6, msg[2], t1, t2)) + return this.abort() + + // verify znp of cR + t3 = HLP.multPowMod(G, msg[7], this.g3ao, msg[6], N) + this.QoQ = BigInt.divMod(msg[1], this.q, N) // save Q over Q + t4 = HLP.multPowMod(this.QoQ, msg[7], msg[5], msg[6], N) + + if (!HLP.ZKP(7, msg[6], t3, t4)) + return this.abort() + + this.computeR() + + // zero-knowledge proof that R + // was generated according to the protocol + r7 = HLP.randomExponent() + tmp2 = BigInt.powMod(this.QoQ, r7, N) + cR = HLP.smpHash(8, BigInt.powMod(G, r7, N), tmp2) + d7 = this.computeD(r7, this.a3, cR) + + send = HLP.packINT(3) + HLP.packMPIs([ this.r, cR, d7 ]) + send = HLP.packTLV(5, send) + + rab = this.computeRab(msg[5]) + trust = !!BigInt.equals(rab, BigInt.divMod(msg[0], this.p, N)) + + this.trigger('trust', [trust, 'answered']) + this.init() + break + + case CONST.SMPSTATE_EXPECT4: + HLP.debug.call(this, 'smp tlv 5') + + // 0:r, 1:cR, 2:d7 + ms = HLP.readLen(msg.msg.substr(0, 4)) + if (ms !== 3) return this.abort() + msg = HLP.unpackMPIs(3, msg.msg.substring(4)) + + if (!HLP.checkGroup(msg[0], N_MINUS_2)) return this.abort() + + // verify znp of cR + t3 = HLP.multPowMod(G, msg[2], this.g3ao, msg[1], N) + t4 = HLP.multPowMod(this.QoQ, msg[2], msg[0], msg[1], N) + if (!HLP.ZKP(8, msg[1], t3, t4)) + return this.abort() + + rab = this.computeRab(msg[0]) + trust = !!BigInt.equals(rab, this.PoP) + + this.trigger('trust', [trust, 'asked']) + this.init() + return + + } + + this.sendMsg(send) + } + + // send a message + SM.prototype.sendMsg = function (send) { + this.trigger('send', [this.ssid, '\x00' + send]) + } + + SM.prototype.rcvSecret = function (secret, question) { + HLP.debug.call(this, 'receive secret') + + var fn, our = false + if (this.smpstate === CONST.SMPSTATE_EXPECT0) { + fn = this.answer + } else { + fn = this.initiate + our = true + } + + this.makeSecret(our, secret) + fn.call(this, question) + } + + SM.prototype.answer = function () { + HLP.debug.call(this, 'smp answer') + + var r4 = HLP.randomExponent() + this.computePQ(r4) + + // zero-knowledge proof that P & Q + // were generated according to the protocol + var r5 = HLP.randomExponent() + var r6 = HLP.randomExponent() + var tmp = HLP.multPowMod(G, r5, this.g2, r6, N) + var cP = HLP.smpHash(5, BigInt.powMod(this.g3, r5, N), tmp) + var d5 = this.computeD(r5, r4, cP) + var d6 = this.computeD(r6, this.secret, cP) + + this.smpstate = CONST.SMPSTATE_EXPECT3 + + var send = HLP.packINT(11) + HLP.packMPIs([ + this.g2a + , this.c2 + , this.d2 + , this.g3a + , this.c3 + , this.d3 + , this.p + , this.q + , cP + , d5 + , d6 + ]) + + this.sendMsg(HLP.packTLV(3, send)) + } + + SM.prototype.initiate = function (question) { + HLP.debug.call(this, 'smp initiate') + + if (this.smpstate !== CONST.SMPSTATE_EXPECT1) + this.abort() // abort + restart + + this.makeG2s() + + // zero-knowledge proof that the exponents + // associated with g2a & g3a are known + var r2 = HLP.randomExponent() + var r3 = HLP.randomExponent() + this.c2 = this.computeC(1, r2) + this.c3 = this.computeC(2, r3) + this.d2 = this.computeD(r2, this.a2, this.c2) + this.d3 = this.computeD(r3, this.a3, this.c3) + + // set the next expected state + this.smpstate = CONST.SMPSTATE_EXPECT2 + + var send = '' + var type = 2 + + if (question) { + send += question + send += '\x00' + type = 7 + } + + send += HLP.packINT(6) + HLP.packMPIs([ + this.g2a + , this.c2 + , this.d2 + , this.g3a + , this.c3 + , this.d3 + ]) + + this.sendMsg(HLP.packTLV(type, send)) + } + + SM.prototype.abort = function () { + this.init() + this.sendMsg(HLP.packTLV(6, '')) + this.trigger('abort') + } + +}).call(this) +;(function () { + "use strict"; + + var root = this + + var CryptoJS, BigInt, EventEmitter, Worker, SMWPath + , CONST, HLP, Parse, AKE, SM, DSA + if (typeof module !== 'undefined' && module.exports) { + module.exports = OTR + CryptoJS = require('../vendor/crypto.js') + BigInt = require('../vendor/bigint.js') + EventEmitter = require('../vendor/eventemitter.js') + SMWPath = require('path').join(__dirname, '/sm-webworker.js') + CONST = require('./const.js') + HLP = require('./helpers.js') + Parse = require('./parse.js') + AKE = require('./ake.js') + SM = require('./sm.js') + DSA = require('./dsa.js') + // expose CONST for consistency with docs + OTR.CONST = CONST + } else { + // copy over and expose internals + Object.keys(root.OTR).forEach(function (k) { + OTR[k] = root.OTR[k] + }) + root.OTR = OTR + CryptoJS = root.CryptoJS + BigInt = root.BigInt + EventEmitter = root.EventEmitter + Worker = root.Worker + SMWPath = 'sm-webworker.js' + CONST = OTR.CONST + HLP = OTR.HLP + Parse = OTR.Parse + AKE = OTR.AKE + SM = OTR.SM + DSA = root.DSA + } + + // diffie-hellman modulus and generator + // see group 5, RFC 3526 + var G = BigInt.str2bigInt(CONST.G, 10) + var N = BigInt.str2bigInt(CONST.N, 16) + + // JavaScript integers + var MAX_INT = Math.pow(2, 53) - 1 // doubles + var MAX_UINT = Math.pow(2, 31) - 1 // bitwise operators + + // OTR contructor + function OTR(options) { + if (!(this instanceof OTR)) return new OTR(options) + + // options + options = options || {} + + // private keys + if (options.priv && !(options.priv instanceof DSA)) + throw new Error('Requires long-lived DSA key.') + + this.priv = options.priv ? options.priv : new DSA() + + this.fragment_size = options.fragment_size || 0 + if (this.fragment_size < 0) + throw new Error('Fragment size must be a positive integer.') + + this.send_interval = options.send_interval || 0 + if (this.send_interval < 0) + throw new Error('Send interval must be a positive integer.') + + this.outgoing = [] + + // instance tag + this.our_instance_tag = options.instance_tag || OTR.makeInstanceTag() + + // debug + this.debug = !!options.debug + + // smp in webworker options + // this is still experimental and undocumented + this.smw = options.smw + + // init vals + this.init() + + // bind methods + var self = this + ;['sendMsg', 'receiveMsg'].forEach(function (meth) { + self[meth] = self[meth].bind(self) + }) + + EventEmitter.call(this) + } + + // inherit from EE + HLP.extend(OTR, EventEmitter) + + // add to prototype + OTR.prototype.init = function () { + + this.msgstate = CONST.MSGSTATE_PLAINTEXT + this.authstate = CONST.AUTHSTATE_NONE + + this.ALLOW_V2 = true + this.ALLOW_V3 = true + + this.REQUIRE_ENCRYPTION = false + this.SEND_WHITESPACE_TAG = false + this.WHITESPACE_START_AKE = false + this.ERROR_START_AKE = false + + Parse.initFragment(this) + + // their keys + this.their_y = null + this.their_old_y = null + this.their_keyid = 0 + this.their_priv_pk = null + this.their_instance_tag = '\x00\x00\x00\x00' + + // our keys + this.our_dh = this.dh() + this.our_old_dh = this.dh() + this.our_keyid = 2 + + // session keys + this.sessKeys = [ new Array(2), new Array(2) ] + + // saved + this.storedMgs = [] + this.oldMacKeys = [] + + // smp + this.sm = null // initialized after AKE + + // when ake is complete + // save their keys and the session + this._akeInit() + + // receive plaintext message since switching to plaintext + // used to decide when to stop sending pt tags when SEND_WHITESPACE_TAG + this.receivedPlaintext = false + + } + + OTR.prototype._akeInit = function () { + this.ake = new AKE(this) + this.transmittedRS = false + this.ssid = null + } + + // smp over webworker + OTR.prototype._SMW = function (otr, reqs) { + this.otr = otr + var opts = { + path: SMWPath + , seed: BigInt.getSeed + } + if (typeof otr.smw === 'object') + Object.keys(otr.smw).forEach(function (k) { + opts[k] = otr.smw[k] + }) + + // load optional dep. in node + if (typeof module !== 'undefined' && module.exports) + Worker = require('webworker-threads').Worker + + this.worker = new Worker(opts.path) + var self = this + this.worker.onmessage = function (e) { + var d = e.data + if (!d) return + self.trigger(d.method, d.args) + } + this.worker.postMessage({ + type: 'seed' + , seed: opts.seed() + , imports: opts.imports + }) + this.worker.postMessage({ + type: 'init' + , reqs: reqs + }) + } + + // inherit from EE + HLP.extend(OTR.prototype._SMW, EventEmitter) + + // shim sm methods + ;['handleSM', 'rcvSecret', 'abort'].forEach(function (m) { + OTR.prototype._SMW.prototype[m] = function () { + this.worker.postMessage({ + type: 'method' + , method: m + , args: Array.prototype.slice.call(arguments, 0) + }) + } + }) + + OTR.prototype._smInit = function () { + var reqs = { + ssid: this.ssid + , our_fp: this.priv.fingerprint() + , their_fp: this.their_priv_pk.fingerprint() + , debug: this.debug + } + if (this.smw) { + if (this.sm) this.sm.worker.terminate() // destroy prev webworker + this.sm = new this._SMW(this, reqs) + } else { + this.sm = new SM(reqs) + } + var self = this + ;['trust', 'abort', 'question'].forEach(function (e) { + self.sm.on(e, function () { + self.trigger('smp', [e].concat(Array.prototype.slice.call(arguments))) + }) + }) + this.sm.on('send', function (ssid, send) { + if (self.ssid === ssid) { + send = self.prepareMsg(send) + self.io(send) + } + }) + } + + OTR.prototype.io = function (msg, meta) { + + // buffer + msg = ([].concat(msg)).map(function(m){ + return { msg: m, meta: meta } + }) + this.outgoing = this.outgoing.concat(msg) + + var self = this + ;(function send(first) { + if (!first) { + if (!self.outgoing.length) return + var elem = self.outgoing.shift() + self.trigger('io', [elem.msg, elem.meta]) + } + setTimeout(send, first ? 0 : self.send_interval) + }(true)) + + } + + OTR.prototype.dh = function dh() { + var keys = { privateKey: BigInt.randBigInt(320) } + keys.publicKey = BigInt.powMod(G, keys.privateKey, N) + return keys + } + + // session constructor + OTR.prototype.DHSession = function DHSession(our_dh, their_y) { + if (!(this instanceof DHSession)) return new DHSession(our_dh, their_y) + + // shared secret + var s = BigInt.powMod(their_y, our_dh.privateKey, N) + var secbytes = HLP.packMPI(s) + + // session id + this.id = HLP.mask(HLP.h2('\x00', secbytes), 0, 64) // first 64-bits + + // are we the high or low end of the connection? + var sq = BigInt.greater(our_dh.publicKey, their_y) + var sendbyte = sq ? '\x01' : '\x02' + var rcvbyte = sq ? '\x02' : '\x01' + + // sending and receiving keys + this.sendenc = HLP.mask(HLP.h1(sendbyte, secbytes), 0, 128) // f16 bytes + this.sendmac = CryptoJS.SHA1(CryptoJS.enc.Latin1.parse(this.sendenc)) + this.sendmac = this.sendmac.toString(CryptoJS.enc.Latin1) + + this.rcvenc = HLP.mask(HLP.h1(rcvbyte, secbytes), 0, 128) + this.rcvmac = CryptoJS.SHA1(CryptoJS.enc.Latin1.parse(this.rcvenc)) + this.rcvmac = this.rcvmac.toString(CryptoJS.enc.Latin1) + this.rcvmacused = false + + // extra symmetric key + this.extra_symkey = HLP.h2('\xff', secbytes) + + // counters + this.send_counter = 0 + this.rcv_counter = 0 + } + + OTR.prototype.rotateOurKeys = function () { + + // reveal old mac keys + var self = this + this.sessKeys[1].forEach(function (sk) { + if (sk && sk.rcvmacused) self.oldMacKeys.push(sk.rcvmac) + }) + + // rotate our keys + this.our_old_dh = this.our_dh + this.our_dh = this.dh() + this.our_keyid += 1 + + this.sessKeys[1][0] = this.sessKeys[0][0] + this.sessKeys[1][1] = this.sessKeys[0][1] + this.sessKeys[0] = [ + this.their_y ? + new this.DHSession(this.our_dh, this.their_y) : null + , this.their_old_y ? + new this.DHSession(this.our_dh, this.their_old_y) : null + ] + + } + + OTR.prototype.rotateTheirKeys = function (their_y) { + + // increment their keyid + this.their_keyid += 1 + + // reveal old mac keys + var self = this + this.sessKeys.forEach(function (sk) { + if (sk[1] && sk[1].rcvmacused) self.oldMacKeys.push(sk[1].rcvmac) + }) + + // rotate their keys / session + this.their_old_y = this.their_y + this.sessKeys[0][1] = this.sessKeys[0][0] + this.sessKeys[1][1] = this.sessKeys[1][0] + + // new keys / sessions + this.their_y = their_y + this.sessKeys[0][0] = new this.DHSession(this.our_dh, this.their_y) + this.sessKeys[1][0] = new this.DHSession(this.our_old_dh, this.their_y) + + } + + OTR.prototype.prepareMsg = function (msg, esk) { + if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || this.their_keyid === 0) + return this.error('Not ready to encrypt.') + + var sessKeys = this.sessKeys[1][0] + + if (sessKeys.send_counter >= MAX_INT) + return this.error('Should have rekeyed by now.') + + sessKeys.send_counter += 1 + + var ctr = HLP.packCtr(sessKeys.send_counter) + + var send = this.ake.otr_version + '\x03' // version and type + var v3 = (this.ake.otr_version === CONST.OTR_VERSION_3) + + if (v3) { + send += this.our_instance_tag + send += this.their_instance_tag + } + + send += '\x00' // flag + send += HLP.packINT(this.our_keyid - 1) + send += HLP.packINT(this.their_keyid) + send += HLP.packMPI(this.our_dh.publicKey) + send += ctr.substring(0, 8) + + if (Math.ceil(msg.length / 8) >= MAX_UINT) // * 16 / 128 + return this.error('Message is too long.') + + var aes = HLP.encryptAes( + CryptoJS.enc.Latin1.parse(msg) + , sessKeys.sendenc + , ctr + ) + + send += HLP.packData(aes) + send += HLP.make1Mac(send, sessKeys.sendmac) + send += HLP.packData(this.oldMacKeys.splice(0).join('')) + + send = HLP.wrapMsg( + send + , this.fragment_size + , v3 + , this.our_instance_tag + , this.their_instance_tag + ) + if (send[0]) return this.error(send[0]) + + // emit extra symmetric key + if (esk) this.trigger('file', ['send', sessKeys.extra_symkey, esk]) + + return send[1] + } + + OTR.prototype.handleDataMsg = function (msg) { + var vt = msg.version + msg.type + + if (this.ake.otr_version === CONST.OTR_VERSION_3) + vt += msg.instance_tags + + var types = ['BYTE', 'INT', 'INT', 'MPI', 'CTR', 'DATA', 'MAC', 'DATA'] + msg = HLP.splitype(types, msg.msg) + + // ignore flag + var ign = (msg[0] === '\x01') + + if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || msg.length !== 8) { + if (!ign) this.error('Received an unreadable encrypted message.', true) + return + } + + var our_keyid = this.our_keyid - HLP.readLen(msg[2]) + var their_keyid = this.their_keyid - HLP.readLen(msg[1]) + + if (our_keyid < 0 || our_keyid > 1) { + if (!ign) this.error('Not of our latest keys.', true) + return + } + + if (their_keyid < 0 || their_keyid > 1) { + if (!ign) this.error('Not of your latest keys.', true) + return + } + + var their_y = their_keyid ? this.their_old_y : this.their_y + + if (their_keyid === 1 && !their_y) { + if (!ign) this.error('Do not have that key.') + return + } + + var sessKeys = this.sessKeys[our_keyid][their_keyid] + + var ctr = HLP.unpackCtr(msg[4]) + if (ctr <= sessKeys.rcv_counter) { + if (!ign) this.error('Counter in message is not larger.') + return + } + sessKeys.rcv_counter = ctr + + // verify mac + vt += msg.slice(0, 6).join('') + var vmac = HLP.make1Mac(vt, sessKeys.rcvmac) + + if (!HLP.compare(msg[6], vmac)) { + if (!ign) this.error('MACs do not match.') + return + } + sessKeys.rcvmacused = true + + var out = HLP.decryptAes( + msg[5].substring(4) + , sessKeys.rcvenc + , HLP.padCtr(msg[4]) + ) + out = out.toString(CryptoJS.enc.Latin1) + + if (!our_keyid) this.rotateOurKeys() + if (!their_keyid) this.rotateTheirKeys(HLP.readMPI(msg[3])) + + // parse TLVs + var ind = out.indexOf('\x00') + if (~ind) { + this.handleTLVs(out.substring(ind + 1), sessKeys) + out = out.substring(0, ind) + } + + out = CryptoJS.enc.Latin1.parse(out) + return out.toString(CryptoJS.enc.Utf8) + } + + OTR.prototype.handleTLVs = function (tlvs, sessKeys) { + var type, len, msg + for (; tlvs.length; ) { + type = HLP.unpackSHORT(tlvs.substr(0, 2)) + len = HLP.unpackSHORT(tlvs.substr(2, 2)) + + msg = tlvs.substr(4, len) + + // TODO: handle pathological cases better + if (msg.length < len) break + + switch (type) { + case 1: + // Disconnected + this.msgstate = CONST.MSGSTATE_FINISHED + this.trigger('status', [CONST.STATUS_END_OTR]) + break + case 2: case 3: case 4: + case 5: case 6: case 7: + // SMP + if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED) { + if (this.sm) this.sm.abort() + return + } + if (!this.sm) this._smInit() + this.sm.handleSM({ msg: msg, type: type }) + break + case 8: + // utf8 filenames + msg = msg.substring(4) // remove 4-byte indication + msg = CryptoJS.enc.Latin1.parse(msg) + msg = msg.toString(CryptoJS.enc.Utf8) + + // Extra Symkey + this.trigger('file', ['receive', sessKeys.extra_symkey, msg]) + break + } + + tlvs = tlvs.substring(4 + len) + } + } + + OTR.prototype.smpSecret = function (secret, question) { + if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED) + return this.error('Must be encrypted for SMP.') + + if (typeof secret !== 'string' || secret.length < 1) + return this.error('Secret is required.') + + if (!this.sm) this._smInit() + + // utf8 inputs + secret = CryptoJS.enc.Utf8.parse(secret).toString(CryptoJS.enc.Latin1) + question = CryptoJS.enc.Utf8.parse(question).toString(CryptoJS.enc.Latin1) + + this.sm.rcvSecret(secret, question) + } + + OTR.prototype.sendQueryMsg = function () { + var versions = {} + , msg = CONST.OTR_TAG + + if (this.ALLOW_V2) versions['2'] = true + if (this.ALLOW_V3) versions['3'] = true + + // but we don't allow v1 + // if (versions['1']) msg += '?' + + var vs = Object.keys(versions) + if (vs.length) { + msg += 'v' + vs.forEach(function (v) { + if (v !== '1') msg += v + }) + msg += '?' + } + + this.io(msg) + this.trigger('status', [CONST.STATUS_SEND_QUERY]) + } + + OTR.prototype.sendMsg = function (msg, meta) { + if ( this.REQUIRE_ENCRYPTION || + this.msgstate !== CONST.MSGSTATE_PLAINTEXT + ) { + msg = CryptoJS.enc.Utf8.parse(msg) + msg = msg.toString(CryptoJS.enc.Latin1) + } + + switch (this.msgstate) { + case CONST.MSGSTATE_PLAINTEXT: + if (this.REQUIRE_ENCRYPTION) { + this.storedMgs.push({msg: msg, meta: meta}) + this.sendQueryMsg() + return + } + if (this.SEND_WHITESPACE_TAG && !this.receivedPlaintext) { + msg += CONST.WHITESPACE_TAG // 16 byte tag + if (this.ALLOW_V3) msg += CONST.WHITESPACE_TAG_V3 + if (this.ALLOW_V2) msg += CONST.WHITESPACE_TAG_V2 + } + break + case CONST.MSGSTATE_FINISHED: + this.storedMgs.push({msg: msg, meta: meta}) + this.error('Message cannot be sent at this time.') + return + case CONST.MSGSTATE_ENCRYPTED: + msg = this.prepareMsg(msg) + break + default: + throw new Error('Unknown message state.') + } + + if (msg) this.io(msg, meta) + } + + OTR.prototype.receiveMsg = function (msg) { + + // parse type + msg = Parse.parseMsg(this, msg) + + if (!msg) return + + switch (msg.cls) { + case 'error': + this.error(msg.msg) + return + case 'ake': + if ( msg.version === CONST.OTR_VERSION_3 && + this.checkInstanceTags(msg.instance_tags) + ) return // ignore + this.ake.handleAKE(msg) + return + case 'data': + if ( msg.version === CONST.OTR_VERSION_3 && + this.checkInstanceTags(msg.instance_tags) + ) return // ignore + msg.msg = this.handleDataMsg(msg) + msg.encrypted = true + break + case 'query': + if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) this._akeInit() + this.doAKE(msg) + break + default: + // check for encrypted + if ( this.REQUIRE_ENCRYPTION || + this.msgstate !== CONST.MSGSTATE_PLAINTEXT + ) this.error('Received an unencrypted message.') + + // received a plaintext message + // stop sending the whitespace tag + this.receivedPlaintext = true + + // received a whitespace tag + if (this.WHITESPACE_START_AKE && msg.ver.length > 0) + this.doAKE(msg) + } + + if (msg.msg) this.trigger('ui', [msg.msg, !!msg.encrypted]) + } + + OTR.prototype.checkInstanceTags = function (it) { + var their_it = HLP.readLen(it.substr(0, 4)) + var our_it = HLP.readLen(it.substr(4, 4)) + + if (our_it && our_it !== HLP.readLen(this.our_instance_tag)) + return true + + if (HLP.readLen(this.their_instance_tag)) { + if (HLP.readLen(this.their_instance_tag) !== their_it) return true + } else { + if (their_it < 100) return true + this.their_instance_tag = HLP.packINT(their_it) + } + } + + OTR.prototype.doAKE = function (msg) { + if (this.ALLOW_V3 && ~msg.ver.indexOf(CONST.OTR_VERSION_3)) { + this.ake.initiateAKE(CONST.OTR_VERSION_3) + } else if (this.ALLOW_V2 && ~msg.ver.indexOf(CONST.OTR_VERSION_2)) { + this.ake.initiateAKE(CONST.OTR_VERSION_2) + } else { + // is this an error? + this.error('OTR conversation requested, ' + + 'but no compatible protocol version found.') + } + } + + OTR.prototype.error = function (err, send) { + if (send) { + if (!this.debug) err = "An OTR error has occurred." + err = '?OTR Error:' + err + this.io(err) + return + } + this.trigger('error', [err]) + } + + OTR.prototype.sendStored = function () { + var self = this + ;(this.storedMgs.splice(0)).forEach(function (elem) { + var msg = self.prepareMsg(elem.msg) + self.io(msg, elem.meta) + }) + } + + OTR.prototype.sendFile = function (filename) { + if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED) + return this.error('Not ready to encrypt.') + + if (this.ake.otr_version !== CONST.OTR_VERSION_3) + return this.error('Protocol v3 required.') + + if (!filename) return this.error('Please specify a filename.') + + // utf8 filenames + var l1name = CryptoJS.enc.Utf8.parse(filename) + l1name = l1name.toString(CryptoJS.enc.Latin1) + + if (l1name.length >= 65532) return this.error('filename is too long.') + + var msg = '\x00' // null byte + msg += '\x00\x08' // type 8 tlv + msg += HLP.packSHORT(4 + l1name.length) // length of value + msg += '\x00\x00\x00\x01' // four bytes indicating file + msg += l1name + + msg = this.prepareMsg(msg, filename) + this.io(msg) + } + + OTR.prototype.endOtr = function () { + if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) { + this.sendMsg('\x00\x00\x01\x00\x00') + if (this.sm) { + if (this.smw) this.sm.worker.terminate() // destroy webworker + this.sm = null + } + } + this.msgstate = CONST.MSGSTATE_PLAINTEXT + this.receivedPlaintext = false + this.trigger('status', [CONST.STATUS_END_OTR]) + } + + // attach methods + + OTR.makeInstanceTag = function () { + var num = BigInt.randBigInt(32) + if (BigInt.greater(BigInt.str2bigInt('100', 16), num)) + return OTR.makeInstanceTag() + return HLP.packINT(parseInt(BigInt.bigInt2str(num, 10), 10)) + } + +}).call(this) + + + return { + OTR: this.OTR + , DSA: this.DSA + } + +})) \ No newline at end of file