Mercurial > sat_docs
view scripts/minifier/otr/otr.js @ 103:e69883c1ec30
docker (libervia_cont): added a "status" command:
- if libervia container is not running, it exits with error code 1
- if libervia container is running but no server is launched, it exits with error code 2
- if libervia container is running and server is launcher, it exits with error code 0 (success)
server detection is done by doing a simple grep on logs, that's not perfectly reliable (ports can be changed in configuration, even if that doesn't really make sense in Docker context) but should be good enough for this purpose.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 27 Feb 2016 00:45:40 +0100 |
parents | 1596660ddf72 |
children |
line wrap: on
line source
/*! 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 } }))