Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

namespace

  • Loading branch information...
commit dcbf9d51870a04f5388abe0d513e78af0c66b142 1 parent f6ec569
@arlolra authored
View
1,169 build/otr.js
@@ -1,6 +1,6 @@
/*!
- otr.js v0.0.5-pre - 2012-08-24
+ otr.js v0.0.5-pre - 2012-08-25
(c) 2012 - Arlo Breault <arlolra@gmail.com>
Freely distributed under the LGPL license.
@@ -9,30 +9,20 @@
*/
-;(function () {
-
- var root = this
-
- var DH = {
- "N": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
- , "G": "2"
- }
-
- if (typeof exports !== 'undefined') {
- module.exports = DH
- } else {
- root.DH = DH
- }
+var OTR = {}, DSA = {}
-}).call(this)
;(function () {
var root = this
- var STATES = {
+ var CONST = {
+
+ // diffie-heilman
+ N : 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF'
+ , G : '2'
// otr message states
- MSGSTATE_PLAINTEXT : 0
+ , MSGSTATE_PLAINTEXT : 0
, MSGSTATE_ENCRYPTED : 1
, MSGSTATE_FINISHED : 2
@@ -53,9 +43,9 @@
}
if (typeof exports !== 'undefined') {
- module.exports = STATES
+ module.exports = CONST
} else {
- root.STATES = STATES
+ root.OTR.CONST = CONST
}
}).call(this)
@@ -63,19 +53,17 @@
var root = this
- var HLP
+ var HLP, CryptoJS, BigInt
if (typeof exports !== 'undefined') {
HLP = exports
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.js')
} else {
- HLP = root.HLP = {}
- }
-
- var BigInt = root.BigInt
- , CryptoJS = root.CryptoJS
-
- if (typeof require !== 'undefined') {
- BigInt || (BigInt = require('../vendor/bigint.js'))
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
+ HLP = {}
+ if (root.OTR) root.OTR.HLP = HLP
+ if (root.DSA) root.DSA.HLP = HLP
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
}
// data types (byte lengths)
@@ -427,21 +415,21 @@
var root = this
- var DSA
+ var CryptoJS, BigInt, HLP
if (typeof exports !== 'undefined') {
- DSA = exports
+ module.exports = DSA
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.js')
+ HLP = require('./helpers.js')
} else {
- DSA = root.DSA = {}
- }
-
- var BigInt = root.BigInt
- , CryptoJS = root.CryptoJS
- , HLP = root.HLP
-
- if (typeof require !== 'undefined') {
- BigInt || (BigInt = require('../vendor/bigint.js'))
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- HLP || (HLP = require('./helpers.js'))
+ // 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
+ HLP = DSA.HLP
}
var ZERO = BigInt.str2bigInt('0', 10)
@@ -474,10 +462,8 @@
return k
}
- DSA.Key = Key
-
- function Key() {
- if (!(this instanceof Key)) return new Key()
+ function DSA() {
+ if (!(this instanceof DSA)) return new DSA()
this.N = 160
this.L = 1024
@@ -491,9 +477,9 @@
this.y = BigInt.powMod(this.g, this.x, this.p)
}
- Key.prototype = {
+ DSA.prototype = {
- constructor: Key,
+ constructor: DSA,
makePQ: function() {
var g = this.N
@@ -639,8 +625,8 @@
}
DSA.inherit = function (key) {
- key.__proto__ = DSA.Key.prototype
- key.constructor = DSA.Key
+ key.__proto__ = DSA.prototype
+ key.constructor = DSA
key.type = '\x00\x00'
}
@@ -649,393 +635,181 @@
var root = this
+ var Parse, CryptoJS, CONST, HLP
if (typeof exports !== 'undefined') {
- module.exports = SM
+ Parse = exports
+ CryptoJS = require('../vendor/crypto.js')
+ CONST = require('./const.js')
+ HLP = require('./helpers.js')
} else {
- root.SM = SM
+ Parse = root.OTR.Parse = {}
+ CryptoJS = root.CryptoJS
+ CONST = root.OTR.CONST
+ HLP = root.OTR.HLP
}
- var BigInt = root.BigInt
- , CryptoJS = root.CryptoJS
- , DH = root.DH
- , HLP = root.HLP
- , DSA = root.DSA
-
- if (typeof require !== 'undefined') {
- BigInt || (BigInt = require('../vendor/bigint.js'))
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- DH || (DH = require('./dh.js'))
- HLP || (HLP = require('./helpers.js'))
- DSA || (DSA = require('./dsa.js'))
- }
-
- // smp machine states
- var SMPSTATE_EXPECT1 = 1
- , SMPSTATE_EXPECT2 = 2
- , SMPSTATE_EXPECT3 = 3
- , SMPSTATE_EXPECT4 = 4
-
- // otr message states
- var MSGSTATE_PLAINTEXT = 0
- , MSGSTATE_ENCRYPTED = 1
- , MSGSTATE_FINISHED = 2
-
- // diffie-hellman modulus and generator
- // see group 5, RFC 3526
- var G = BigInt.str2bigInt(DH.G, 10)
- var N = BigInt.str2bigInt(DH.N, 16)
+ Parse.parseMsg = function (otr, msg) {
- // to calculate D's for zero-knowledge proofs
- var Q = BigInt.sub(N, BigInt.str2bigInt('1', 10))
- BigInt.divInt_(Q, 2) // meh
+ // is this otr?
+ var start = msg.indexOf(CONST.OTR_TAG)
+ if (!~start) {
- function SM(otr) {
- if (!(this instanceof SM)) return new SM(otr)
+ // restart fragments
+ this.initFragment(otr)
- this.otr = otr
- this.version = '1'
- this.our_fp = DSA.fingerprint(otr.priv)
- this.their_fp = DSA.fingerprint(otr.their_priv_pk)
+ // whitespace tags
+ var ver = []
+ ind = msg.indexOf(CONST.WHITESPACE_TAG)
- // initial state
- this.init()
- }
+ if (~ind) {
- SM.prototype = {
+ msg = msg.split('')
+ msg.splice(ind, 16)
- // set the constructor
- // because the prototype is being replaced
- constructor: SM,
+ var len = msg.length
+ for (; ind < len;) {
+ if (msg.slice(ind, ind + 8).join('') === CONST.WHITESPACE_TAG_V2) {
+ msg.splice(ind, 8)
+ ver.push(CONST.OTR_VERSION_2)
+ break
+ }
+ ind += 8
+ }
- // set the initial values
- // also used when aborting
- init: function () {
- this.smpstate = SMPSTATE_EXPECT1
- this.secret = null
- },
+ msg = msg.join('')
- makeSecret: function (our) {
- var sha256 = CryptoJS.algo.SHA256.create()
- sha256.update(this.version)
- sha256.update(our ? this.our_fp : this.their_fp)
- sha256.update(our ? this.their_fp : this.our_fp)
- sha256.update(this.otr.ssid) // secure session id
- sha256.update(this.otr.secret) // user input string
- var hash = sha256.finalize()
- this.secret = BigInt.str2bigInt(hash.toString(CryptoJS.enc.Hex), 16)
- },
+ }
- makeG2s: function () {
- this.g2a = BigInt.powMod(G, this.a2, N)
- this.g3a = BigInt.powMod(G, this.a3, N)
- },
+ return { msg: msg, ver: ver }
+ }
- computeGs: function (g2a, g3a) {
- this.g2 = BigInt.powMod(g2a, this.a2, N)
- this.g3 = BigInt.powMod(g3a, this.a3, N)
- },
+ var ind = start + CONST.OTR_TAG.length
+ var com = msg[ind]
- computePQ: function (r) {
- this.p = BigInt.powMod(this.g3, r, N)
- this.q = HLP.multPowMod(G, r, this.g2, this.secret, N)
- },
+ // message fragment
+ if (com === ',') {
+ return this.msgFragment(otr, msg.substring(ind + 1))
+ }
- computeR: function () {
- this.r = BigInt.powMod(this.QoQ, this.a3, N)
- },
+ this.initFragment(otr)
- computeRab: function (r) {
- return BigInt.powMod(r, this.a3, N)
- },
+ // query message
+ if (~['?', 'v'].indexOf(com)) {
- computeC: function (v, r) {
- return HLP.smpHash(v, BigInt.powMod(G, r, N))
- },
+ // version 1
+ if (msg[ind] === '?') {
+ otr.versions['1'] = true
+ ind += 1
+ }
- computeD: function (r, a, c) {
- return HLP.subMod(r, BigInt.multMod(a, c, Q), Q)
- },
+ // other versions
+ var qs = msg.substring(ind + 1)
+ var qi = qs.indexOf('?')
- // the bulk of the work
- handleSM: function (msg) {
- var send, r2, r3, r4, r5, r6, r7, t1, t2, t3, t4
- , rab, tmp, tmp2, cP, cR, d5, d6, d7, ms
+ if (qi < 1) return
+ qs = qs.substring(0, qi).split('')
- var expectStates = {
- 2: SMPSTATE_EXPECT1
- , 3: SMPSTATE_EXPECT2
- , 4: SMPSTATE_EXPECT3
- , 5: SMPSTATE_EXPECT4
+ if (msg[ind] === 'v') {
+ qs.forEach(function (q) {
+ otr.versions[q] = true
+ })
}
- if (msg.type === 6) {
- this.init()
- return
+ // start ake
+ if (otr.ALLOW_V2 && otr.versions['2']) {
+ return { cls: 'query', version: '2' }
}
- // abort! there was an error
- if ( this.smpstate !== expectStates[msg.type] ||
- this.otr.msgstate !== MSGSTATE_ENCRYPTED
- ) return this.abort()
+ return
+ }
- switch (this.smpstate) {
+ // otr message
+ if (com === ':') {
- case SMPSTATE_EXPECT1:
- // 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))
+ ind += 1
- this.makeSecret()
+ var info = msg.substring(ind, ind + 4)
+ if (info.length < 4) return { msg: msg }
+ info = CryptoJS.enc.Base64.parse(info).toString(CryptoJS.enc.Latin1)
- // verify znp's
- if (!HLP.ZKP(1, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N)))
- return this.abort()
+ var version = info.substring(0, 2)
+ var type = info.substring(2)
- if (!HLP.ZKP(2, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N)))
- return this.abort()
+ // only supporting otr version 2
+ if (!otr['ALLOW_V' + HLP.unpackSHORT(version)]) return { msg: msg }
- this.g3ao = msg[3] // save for later
+ ind += 4
- this.a2 = HLP.randomExponent()
- this.a3 = HLP.randomExponent()
+ var end = msg.substring(ind).indexOf('.')
+ if (!~end) return { msg: msg }
- this.makeG2s()
+ msg = CryptoJS.enc.Base64.parse(msg.substring(ind, ind + end))
+ msg = CryptoJS.enc.Latin1.stringify(msg)
- // 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)
+ var cls
+ if (~['\x02', '\x0a', '\x11', '\x12'].indexOf(type)) {
+ cls = 'ake'
+ } else if (type === '\x03') {
+ cls = 'data'
+ }
- this.computeGs(msg[0], msg[3])
+ return {
+ version: version
+ , type: type
+ , msg: msg
+ , cls: cls
+ }
+ }
- r4 = HLP.randomExponent()
+ // error message
+ if (msg.substring(ind, ind + 7) === ' Error:') {
+ if (otr.ERROR_START_AKE) {
+ otr.sendQueryMsg()
+ }
+ return { msg: msg.substring(ind + 7), cls: 'error' }
+ }
- this.computePQ(r4)
+ return { msg: msg }
+ }
- // zero-knowledge proof that P & Q
- // were generated according to the protocol
- r5 = HLP.randomExponent()
- r6 = HLP.randomExponent()
- tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
- cP = HLP.smpHash(5, BigInt.powMod(this.g3, r5, N), tmp)
- d5 = this.computeD(r5, r4, cP)
- d6 = this.computeD(r6, this.secret, cP)
+ Parse.initFragment = function (otr) {
+ otr.fragment = { s: '', j: 0, k: 0 }
+ }
- this.smpstate = SMPSTATE_EXPECT3
+ Parse.msgFragment = function (otr, msg) {
+ msg = msg.split(',')
- 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
- ])
+ if (msg.length < 4 ||
+ isNaN(parseInt(msg[0], 10)) ||
+ isNaN(parseInt(msg[1], 10))
+ ) return
- // TLV
- send = HLP.packTLV(3, send)
- break
+ var k = parseInt(msg[0], 10)
+ var n = parseInt(msg[1], 10)
+ msg = msg[2]
- case SMPSTATE_EXPECT2:
- // 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))
-
- // 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()
-
- r4 = HLP.randomExponent()
-
- this.computePQ(r4)
-
- // zero-knowledge proof that P & Q
- // were generated according to the protocol
- r5 = HLP.randomExponent()
- r6 = HLP.randomExponent()
- tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
- cP = HLP.smpHash(6, BigInt.powMod(this.g3, r5, N), tmp)
- d5 = this.computeD(r5, r4, cP)
- d6 = this.computeD(r6, this.secret, cP)
-
- // store these
- this.QoQ = HLP.divMod(this.q, msg[7], N)
- this.PoP = HLP.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 = 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 SMPSTATE_EXPECT3:
- // 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))
-
- // 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 = HLP.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)
-
- rab = this.computeRab(msg[5])
-
- if (!BigInt.equals(rab, HLP.divMod(msg[0], this.p, N)))
- return this.abort()
-
- send = HLP.packINT(3) + HLP.packMPIs([ this.r, cR, d7 ])
-
- // TLV
- send = HLP.packTLV(5, send)
-
- this.otr.trust = true
- this.init()
- break
-
- case SMPSTATE_EXPECT4:
- // 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))
-
- // 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])
-
- if (!BigInt.equals(rab, this.PoP))
- return this.abort()
-
- this.otr.trust = true
- this.init()
- return
-
- }
-
- this.sendMsg(send)
- },
-
- // send a message
- sendMsg: function (send) {
- this.otr.sendMsg('\x00' + send)
- },
-
- initiate: function () {
-
- if (this.otr.msgstate !== MSGSTATE_ENCRYPTED)
- return this.otr.error('Not ready to send encrypted messages.')
-
- this.makeSecret(true)
-
- if (this.smpstate !== SMPSTATE_EXPECT1)
- this.abort() // abort + restart
-
- this.a2 = HLP.randomValue()
- this.a3 = HLP.randomValue()
- this.makeG2s()
-
- // zero-knowledge proof that the exponents
- // associated with g2a & g3a are known
- var r2 = HLP.randomValue()
- var r3 = HLP.randomValue()
- 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 = SMPSTATE_EXPECT2
-
- var send = HLP.packINT(6) + HLP.packMPIs([
- this.g2a
- , this.c2
- , this.d2
- , this.g3a
- , this.c3
- , this.d3
- ])
-
- // TLV
- send = HLP.packTLV(2, send)
+ if (n < k || n === 0 || k === 0) {
+ this.initFragment(otr)
+ return
+ }
- this.sendMsg(send)
- },
+ 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)
+ }
- abort: function () {
- this.init()
- this.sendMsg(HLP.packTLV(6, ''))
+ if (n === k) {
+ msg = otr.fragment.s
+ this.initFragment(otr)
+ return this.parseMsg(otr, msg)
}
+ return
}
}).call(this)
@@ -1043,32 +817,27 @@
var root = this
+ var CryptoJS, BigInt, CONST, HLP, DSA
if (typeof exports !== 'undefined') {
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.AKE = AKE
- }
-
- var CryptoJS = root.CryptoJS
- , BigInt = root.BigInt
- , DH = root.DH
- , HLP = root.HLP
- , DSA = root.DSA
- , STATES = root.STATES
-
- if (typeof require !== 'undefined') {
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- BigInt || (BigInt = require('../vendor/bigint.js'))
- DH || (DH = require('./dh.js'))
- HLP || (HLP = require('./helpers.js'))
- DSA || (DSA = require('./dsa.js'))
- STATES || (STATES = require('./states.js'))
+ root.OTR.AKE = AKE
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
+ CONST = root.OTR.CONST
+ HLP = root.OTR.HLP
+ DSA = root.DSA
}
// diffie-hellman modulus and generator
// see group 5, RFC 3526
- var G = BigInt.str2bigInt(DH.G, 10)
- var N = BigInt.str2bigInt(DH.N, 16)
+ var G = BigInt.str2bigInt(CONST.G, 10)
+ var N = BigInt.str2bigInt(CONST.N, 16)
var TWO = BigInt.str2bigInt('2', 10)
var N_MINUS_2 = BigInt.sub(N, TWO)
@@ -1207,8 +976,8 @@
this.otr.smInit()
// go encrypted
- this.otr.authstate = STATES.AUTHSTATE_NONE
- this.otr.msgstate = STATES.MSGSTATE_ENCRYPTED
+ this.otr.authstate = CONST.AUTHSTATE_NONE
+ this.otr.msgstate = CONST.MSGSTATE_ENCRYPTED
// send stored msgs
this.otr.sendStored()
@@ -1226,7 +995,7 @@
msg = HLP.splitype(['DATA', 'DATA'], msg.msg)
- if (this.otr.authstate === STATES.AUTHSTATE_AWAITING_DHKEY) {
+ 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)) {
@@ -1235,15 +1004,15 @@
} else {
// forget
this.our_dh = this.otr.dh()
- this.otr.authstate = STATES.AUTHSTATE_NONE
+ this.otr.authstate = CONST.AUTHSTATE_NONE
this.r = null
this.myhashed = null
}
} else if (
- this.otr.authstate === STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG
) this.our_dh = this.otr.dh()
- this.otr.authstate = STATES.AUTHSTATE_AWAITING_REVEALSIG
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_REVEALSIG
this.encrypted = msg[0].substring(4)
this.hashed = msg[1].substring(4)
@@ -1259,15 +1028,15 @@
msg = HLP.splitype(['MPI'], msg.msg)
- if (this.otr.authstate !== STATES.AUTHSTATE_AWAITING_DHKEY) {
- if (this.otr.authstate === STATES.AUTHSTATE_AWAITING_SIG) {
+ 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 = STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_SIG
this.their_y = HLP.readMPI(msg[0])
@@ -1286,7 +1055,7 @@
debug('signature message')
if ( !this.otr.ALLOW_V2 ||
- this.otr.authstate !== STATES.AUTHSTATE_AWAITING_REVEALSIG
+ this.otr.authstate !== CONST.AUTHSTATE_AWAITING_REVEALSIG
) return // ignore
msg = HLP.splitype(['DATA', 'DATA', 'MAC'], msg.msg)
@@ -1344,7 +1113,7 @@
debug('data message')
if ( !this.otr.ALLOW_V2 ||
- this.otr.authstate !== STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate !== CONST.AUTHSTATE_AWAITING_SIG
) return // ignore
msg = HLP.splitype(['DATA', 'MAC'], msg.msg)
@@ -1378,7 +1147,7 @@
},
sendMsg: function (msg) {
- msg = STATES.OTR_VERSION_2 + msg
+ msg = CONST.OTR_VERSION_2 + msg
msg = HLP.wrapMsg(msg, this.otr.fragment_size)
if (msg[0]) return this.otr.error(msg[0])
this.otr.sendMsg(msg[1], true)
@@ -1390,7 +1159,7 @@
// save in case we have to resend
this.dhcommit = '\x02'
- this.otr.authstate = STATES.AUTHSTATE_AWAITING_DHKEY
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_DHKEY
var gxmpi = HLP.packMPI(this.our_dh.publicKey)
@@ -1413,185 +1182,390 @@
var root = this
- var ParseOTR
+ var CryptoJS, BigInt, CONST, HLP, DSA
if (typeof exports !== 'undefined') {
- ParseOTR = exports
+ module.exports = SM
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.js')
+ CONST = require('./const.js')
+ HLP = require('./helpers.js')
+ DSA = require('./dsa.js')
} else {
- ParseOTR = root.ParseOTR = {}
- }
-
- var CryptoJS = root.CryptoJS
- , HLP = root.HLP
- , STATES = root.STATES
-
- if (typeof require !== 'undefined') {
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- HLP || (HLP = require('./helpers.js'))
- STATES || (STATES = require('./states.js'))
+ root.OTR.SM = SM
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
+ CONST = root.OTR.CONST
+ HLP = root.OTR.HLP
+ DSA = root.DSA
}
- ParseOTR.parseMsg = function (otr, msg) {
+ // smp machine states
+ var SMPSTATE_EXPECT1 = 1
+ , SMPSTATE_EXPECT2 = 2
+ , SMPSTATE_EXPECT3 = 3
+ , SMPSTATE_EXPECT4 = 4
- // is this otr?
- var start = msg.indexOf(STATES.OTR_TAG)
- if (!~start) {
+ // otr message states
+ var MSGSTATE_PLAINTEXT = 0
+ , MSGSTATE_ENCRYPTED = 1
+ , MSGSTATE_FINISHED = 2
- // restart fragments
- this.initFragment(otr)
+ // diffie-hellman modulus and generator
+ // see group 5, RFC 3526
+ var G = BigInt.str2bigInt(CONST.G, 10)
+ var N = BigInt.str2bigInt(CONST.N, 16)
- // whitespace tags
- var ver = []
- ind = msg.indexOf(STATES.WHITESPACE_TAG)
+ // to calculate D's for zero-knowledge proofs
+ var Q = BigInt.sub(N, BigInt.str2bigInt('1', 10))
+ BigInt.divInt_(Q, 2) // meh
- if (~ind) {
+ function SM(otr) {
+ if (!(this instanceof SM)) return new SM(otr)
- msg = msg.split('')
- msg.splice(ind, 16)
+ this.otr = otr
+ this.version = '1'
+ this.our_fp = DSA.fingerprint(otr.priv)
+ this.their_fp = DSA.fingerprint(otr.their_priv_pk)
- var len = msg.length
- for (; ind < len;) {
- if (msg.slice(ind, ind + 8).join('') === STATES.WHITESPACE_TAG_V2) {
- msg.splice(ind, 8)
- ver.push(STATES.OTR_VERSION_2)
- break
- }
- ind += 8
- }
+ // initial state
+ this.init()
+ }
- msg = msg.join('')
+ SM.prototype = {
- }
+ // set the constructor
+ // because the prototype is being replaced
+ constructor: SM,
- return { msg: msg, ver: ver }
- }
+ // set the initial values
+ // also used when aborting
+ init: function () {
+ this.smpstate = SMPSTATE_EXPECT1
+ this.secret = null
+ },
- var ind = start + STATES.OTR_TAG.length
- var com = msg[ind]
+ makeSecret: function (our) {
+ var sha256 = CryptoJS.algo.SHA256.create()
+ sha256.update(this.version)
+ sha256.update(our ? this.our_fp : this.their_fp)
+ sha256.update(our ? this.their_fp : this.our_fp)
+ sha256.update(this.otr.ssid) // secure session id
+ sha256.update(this.otr.secret) // user input string
+ var hash = sha256.finalize()
+ this.secret = BigInt.str2bigInt(hash.toString(CryptoJS.enc.Hex), 16)
+ },
- // message fragment
- if (com === ',') {
- return this.msgFragment(otr, msg.substring(ind + 1))
- }
+ makeG2s: function () {
+ this.g2a = BigInt.powMod(G, this.a2, N)
+ this.g3a = BigInt.powMod(G, this.a3, N)
+ },
- this.initFragment(otr)
+ computeGs: function (g2a, g3a) {
+ this.g2 = BigInt.powMod(g2a, this.a2, N)
+ this.g3 = BigInt.powMod(g3a, this.a3, N)
+ },
- // query message
- if (~['?', 'v'].indexOf(com)) {
+ computePQ: function (r) {
+ this.p = BigInt.powMod(this.g3, r, N)
+ this.q = HLP.multPowMod(G, r, this.g2, this.secret, N)
+ },
- // version 1
- if (msg[ind] === '?') {
- otr.versions['1'] = true
- ind += 1
- }
+ computeR: function () {
+ this.r = BigInt.powMod(this.QoQ, this.a3, N)
+ },
- // other versions
- var qs = msg.substring(ind + 1)
- var qi = qs.indexOf('?')
+ computeRab: function (r) {
+ return BigInt.powMod(r, this.a3, N)
+ },
- if (qi < 1) return
- qs = qs.substring(0, qi).split('')
+ computeC: function (v, r) {
+ return HLP.smpHash(v, BigInt.powMod(G, r, N))
+ },
- if (msg[ind] === 'v') {
- qs.forEach(function (q) {
- otr.versions[q] = true
- })
+ computeD: function (r, a, c) {
+ return HLP.subMod(r, BigInt.multMod(a, c, Q), Q)
+ },
+
+ // the bulk of the work
+ handleSM: function (msg) {
+ var send, r2, r3, r4, r5, r6, r7, t1, t2, t3, t4
+ , rab, tmp, tmp2, cP, cR, d5, d6, d7, ms
+
+ var expectStates = {
+ 2: SMPSTATE_EXPECT1
+ , 3: SMPSTATE_EXPECT2
+ , 4: SMPSTATE_EXPECT3
+ , 5: SMPSTATE_EXPECT4
}
- // start ake
- if (otr.ALLOW_V2 && otr.versions['2']) {
- return { cls: 'query', version: '2' }
+ if (msg.type === 6) {
+ this.init()
+ return
}
- return
- }
+ // abort! there was an error
+ if ( this.smpstate !== expectStates[msg.type] ||
+ this.otr.msgstate !== MSGSTATE_ENCRYPTED
+ ) return this.abort()
- // otr message
- if (com === ':') {
+ switch (this.smpstate) {
- ind += 1
+ case SMPSTATE_EXPECT1:
+ // 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))
- var info = msg.substring(ind, ind + 4)
- if (info.length < 4) return { msg: msg }
- info = CryptoJS.enc.Base64.parse(info).toString(CryptoJS.enc.Latin1)
+ this.makeSecret()
- var version = info.substring(0, 2)
- var type = info.substring(2)
+ // verify znp's
+ if (!HLP.ZKP(1, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N)))
+ return this.abort()
- // only supporting otr version 2
- if (!otr['ALLOW_V' + HLP.unpackSHORT(version)]) return { msg: msg }
+ if (!HLP.ZKP(2, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N)))
+ return this.abort()
- ind += 4
+ this.g3ao = msg[3] // save for later
- var end = msg.substring(ind).indexOf('.')
- if (!~end) return { msg: msg }
+ this.a2 = HLP.randomExponent()
+ this.a3 = HLP.randomExponent()
- msg = CryptoJS.enc.Base64.parse(msg.substring(ind, ind + end))
- msg = CryptoJS.enc.Latin1.stringify(msg)
+ this.makeG2s()
- var cls
- if (~['\x02', '\x0a', '\x11', '\x12'].indexOf(type)) {
- cls = 'ake'
- } else if (type === '\x03') {
- cls = 'data'
- }
+ // 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)
- return {
- version: version
- , type: type
- , msg: msg
- , cls: cls
- }
- }
+ this.computeGs(msg[0], msg[3])
+
+ r4 = HLP.randomExponent()
+
+ this.computePQ(r4)
+
+ // zero-knowledge proof that P & Q
+ // were generated according to the protocol
+ r5 = HLP.randomExponent()
+ r6 = HLP.randomExponent()
+ tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
+ cP = HLP.smpHash(5, BigInt.powMod(this.g3, r5, N), tmp)
+ d5 = this.computeD(r5, r4, cP)
+ d6 = this.computeD(r6, this.secret, cP)
+
+ this.smpstate = SMPSTATE_EXPECT3
+
+ 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
+ ])
+
+ // TLV
+ send = HLP.packTLV(3, send)
+ break
+
+ case SMPSTATE_EXPECT2:
+ // 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))
+
+ // 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()
+
+ r4 = HLP.randomExponent()
+
+ this.computePQ(r4)
+
+ // zero-knowledge proof that P & Q
+ // were generated according to the protocol
+ r5 = HLP.randomExponent()
+ r6 = HLP.randomExponent()
+ tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
+ cP = HLP.smpHash(6, BigInt.powMod(this.g3, r5, N), tmp)
+ d5 = this.computeD(r5, r4, cP)
+ d6 = this.computeD(r6, this.secret, cP)
+
+ // store these
+ this.QoQ = HLP.divMod(this.q, msg[7], N)
+ this.PoP = HLP.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 = 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 SMPSTATE_EXPECT3:
+ // 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))
+
+ // 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 = HLP.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)
+
+ rab = this.computeRab(msg[5])
+
+ if (!BigInt.equals(rab, HLP.divMod(msg[0], this.p, N)))
+ return this.abort()
+
+ send = HLP.packINT(3) + HLP.packMPIs([ this.r, cR, d7 ])
+
+ // TLV
+ send = HLP.packTLV(5, send)
+
+ this.otr.trust = true
+ this.init()
+ break
+
+ case SMPSTATE_EXPECT4:
+ // 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))
+
+ // 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])
+
+ if (!BigInt.equals(rab, this.PoP))
+ return this.abort()
+
+ this.otr.trust = true
+ this.init()
+ return
- // 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 }
- }
+ this.sendMsg(send)
+ },
- ParseOTR.initFragment = function (otr) {
- otr.fragment = { s: '', j: 0, k: 0 }
- }
+ // send a message
+ sendMsg: function (send) {
+ this.otr.sendMsg('\x00' + send)
+ },
- ParseOTR.msgFragment = function (otr, msg) {
- msg = msg.split(',')
+ initiate: function () {
- if (msg.length < 4 ||
- isNaN(parseInt(msg[0], 10)) ||
- isNaN(parseInt(msg[1], 10))
- ) return
+ if (this.otr.msgstate !== MSGSTATE_ENCRYPTED)
+ return this.otr.error('Not ready to send encrypted messages.')
- var k = parseInt(msg[0], 10)
- var n = parseInt(msg[1], 10)
- msg = msg[2]
+ this.makeSecret(true)
- if (n < k || n === 0 || k === 0) {
- this.initFragment(otr)
- return
- }
+ if (this.smpstate !== SMPSTATE_EXPECT1)
+ this.abort() // abort + restart
- 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)
- }
+ this.a2 = HLP.randomValue()
+ this.a3 = HLP.randomValue()
+ this.makeG2s()
- if (n === k) {
- msg = otr.fragment.s
- this.initFragment(otr)
- return this.parseMsg(otr, msg)
+ // zero-knowledge proof that the exponents
+ // associated with g2a & g3a are known
+ var r2 = HLP.randomValue()
+ var r3 = HLP.randomValue()
+ 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 = SMPSTATE_EXPECT2
+
+ var send = HLP.packINT(6) + HLP.packMPIs([
+ this.g2a
+ , this.c2
+ , this.d2
+ , this.g3a
+ , this.c3
+ , this.d3
+ ])
+
+ // TLV
+ send = HLP.packTLV(2, send)
+
+ this.sendMsg(send)
+ },
+
+ abort: function () {
+ this.init()
+ this.sendMsg(HLP.packTLV(6, ''))
}
- return
}
}).call(this)
@@ -1599,48 +1573,47 @@
var root = this
+ var CryptoJS, BigInt, CONST, HLP, Parse, AKE, SM, DSA
if (typeof exports !== 'undefined') {
module.exports = OTR
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.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')
} else {
+ // copy over and expose internals
+ Object.keys(root.OTR).forEach(function (k) {
+ OTR[k] = root.OTR[k]
+ })
root.OTR = OTR
- }
-
- var CryptoJS = root.CryptoJS
- , BigInt = root.BigInt
- , DH = root.DH
- , HLP = root.HLP
- , DSA = root.DSA
- , AKE = root.AKE
- , SM = root.SM
- , STATES = root.STATES
- , ParseOTR = root.ParseOTR
-
- if (typeof require !== 'undefined') {
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- BigInt || (BigInt = require('../vendor/bigint.js'))
- DH || (DH = require('./dh.js'))
- HLP || (HLP = require('./helpers.js'))
- DSA || (DSA = require('./dsa.js'))
- AKE || (AKE = require('./ake.js'))
- SM || (SM = require('./sm.js'))
- STATES || (STATES = require('./states.js'))
- ParseOTR || (ParseOTR = require('./parse.js'))
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
+ 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(DH.G, 10)
- var N = BigInt.str2bigInt(DH.N, 16)
+ var G = BigInt.str2bigInt(CONST.G, 10)
+ var N = BigInt.str2bigInt(CONST.N, 16)
// OTR contructor
function OTR(priv, uicb, iocb, options) {
if (!(this instanceof OTR)) return new OTR(priv, uicb, iocb, options)
// private keys
- if (priv && !(priv instanceof DSA.Key))
+ if (priv && !(priv instanceof DSA))
throw new Error('Requires long-lived DSA key.')
- this.priv = priv ? priv : new DSA.Key()
+ this.priv = priv ? priv : new DSA()
// options
options = options || {}
@@ -1678,8 +1651,8 @@
init: function () {
- this.msgstate = STATES.MSGSTATE_PLAINTEXT
- this.authstate = STATES.AUTHSTATE_NONE
+ this.msgstate = CONST.MSGSTATE_PLAINTEXT
+ this.authstate = CONST.AUTHSTATE_NONE
this.ALLOW_V2 = true
@@ -1688,7 +1661,7 @@
this.WHITESPACE_START_AKE = false
this.ERROR_START_AKE = false
- ParseOTR.initFragment(this)
+ Parse.initFragment(this)
this.versions = {}
@@ -1846,7 +1819,7 @@
},
prepareMsg: function (msg) {
- if (this.msgstate !== STATES.MSGSTATE_ENCRYPTED || this.their_keyid === 0)
+ if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || this.their_keyid === 0)
return this.error('Not ready to encrypt.')
var sessKeys = this.sessKeys[1][0]
@@ -1854,7 +1827,7 @@
var ctr = HLP.packCtr(sessKeys.send_counter)
- var send = STATES.OTR_VERSION_2 + '\x03' // version and type
+ var send = CONST.OTR_VERSION_2 + '\x03' // version and type
send += '\x00' // flag
send += HLP.packINT(this.our_keyid - 1)
send += HLP.packINT(this.their_keyid)
@@ -1880,7 +1853,7 @@
// ignore flag
var ign = (msg[0] === '\x01')
- if (this.msgstate !== STATES.MSGSTATE_ENCRYPTED || msg.length !== 8) {
+ if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || msg.length !== 8) {
if (!ign) this.error('Received an unreadable encrypted message.', true)
return
}
@@ -1989,18 +1962,18 @@
if (!internal) { // a user or sm msg
switch (this.msgstate) {
- case STATES.MSGSTATE_PLAINTEXT:
+ case CONST.MSGSTATE_PLAINTEXT:
if (this.REQUIRE_ENCRYPTION) {
this.storedMgs.push(msg)
this.sendQueryMsg()
return
}
if (this.SEND_WHITESPACE_TAG) {
- msg += STATES.WHITESPACE_TAG // 16 byte tag
- if (this.ALLOW_V2) msg += STATES.WHITESPACE_TAG_V2
+ msg += CONST.WHITESPACE_TAG // 16 byte tag
+ if (this.ALLOW_V2) msg += CONST.WHITESPACE_TAG_V2
}
break
- case STATES.MSGSTATE_FINISHED:
+ case CONST.MSGSTATE_FINISHED:
this.storedMgs.push(msg)
this.error('Message cannot be sent at this time.')
return
@@ -2015,7 +1988,7 @@
receiveMsg: function (msg) {
// parse type
- msg = ParseOTR.parseMsg(this, msg)
+ msg = Parse.parseMsg(this, msg)
if (!msg) return
@@ -2031,12 +2004,12 @@
break
case 'query':
if (msg.version !== '2') return
- if (this.msgstate === STATES.MSGSTATE_ENCRYPTED) this.akeInit()
+ if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) this.akeInit()
this.ake.initiateAKE()
break
default:
if ( this.REQUIRE_ENCRYPTION ||
- this.msgstate !== STATES.MSGSTATE_PLAINTEXT
+ this.msgstate !== CONST.MSGSTATE_PLAINTEXT
) this.error('Received an unencrypted message.')
// received a plaintext message
@@ -2045,7 +2018,7 @@
// received a whitespace tag
if ( this.WHITESPACE_START_AKE &&
- ~msg.ver.indexOf(STATES.OTR_VERSION_2)
+ ~msg.ver.indexOf(CONST.OTR_VERSION_2)
) this.ake.initiateAKE()
}
@@ -2071,11 +2044,11 @@
},
endOtr: function () {
- if (this.msgstate === STATES.MSGSTATE_ENCRYPTED) {
+ if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) {
this.sendMsg('\x00\x01\x00\x00')
this.sm = null
}
- this.msgstate = STATES.MSGSTATE_PLAINTEXT
+ this.msgstate = CONST.MSGSTATE_PLAINTEXT
}
}
View
BIN  build/otr.min.js
Binary file not shown
View
25 grunt.js
@@ -19,26 +19,27 @@ module.exports = function (grunt) {
pkg: '<json:package.json>'
, meta: {
banner:
- '/*!\n\n <%= pkg.name %>.js v<%= pkg.version %> - ' +
- '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
- ' (c) <%= grunt.template.today("yyyy") %> - <%= pkg.author %>\n' +
- ' Freely distributed under the <%= pkg.license %> license.\n\n' +
- ' This file is concatenated for the browser.\n' +
- ' Please see: <%= pkg.homepage %>' +
- '\n\n*/',
- cryptojs: 'module.exports = CryptoJS'
+ '/*!\n\n <%= pkg.name %>.js v<%= pkg.version %> - ' +
+ '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+ ' (c) <%= grunt.template.today("yyyy") %> - <%= pkg.author %>\n' +
+ ' Freely distributed under the <%= pkg.license %> license.\n\n' +
+ ' This file is concatenated for the browser.\n' +
+ ' Please see: <%= pkg.homepage %>' +
+ '\n\n*/'
+ , cryptojs: 'module.exports = CryptoJS'
+ , globals: 'var OTR = {}, DSA = {}'
}
, concat: {
otr: {
src: [
'<banner:meta.banner>'
- , 'lib/dh.js'
- , 'lib/states.js'
+ , '<banner:meta.globals>'
+ , 'lib/const.js'
, 'lib/helpers.js'
, 'lib/dsa.js'
- , 'lib/sm.js'
- , 'lib/ake.js'
, 'lib/parse.js'
+ , 'lib/ake.js'
+ , 'lib/sm.js'
, 'lib/otr.js'
]
, dest: 'build/otr.js'
View
4 index.js
@@ -0,0 +1,4 @@
+module.exports = {
+ DSA: require('./lib/dsa.js')
+ , OTR: require('./lib/otr.js')
+}
View
59 lib/ake.js
@@ -2,32 +2,27 @@
var root = this
+ var CryptoJS, BigInt, CONST, HLP, DSA
if (typeof exports !== 'undefined') {
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.AKE = AKE
- }
-
- var CryptoJS = root.CryptoJS
- , BigInt = root.BigInt
- , DH = root.DH
- , HLP = root.HLP
- , DSA = root.DSA
- , STATES = root.STATES
-
- if (typeof require !== 'undefined') {
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- BigInt || (BigInt = require('../vendor/bigint.js'))
- DH || (DH = require('./dh.js'))
- HLP || (HLP = require('./helpers.js'))
- DSA || (DSA = require('./dsa.js'))
- STATES || (STATES = require('./states.js'))
+ root.OTR.AKE = AKE
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
+ CONST = root.OTR.CONST
+ HLP = root.OTR.HLP
+ DSA = root.DSA
}
// diffie-hellman modulus and generator
// see group 5, RFC 3526
- var G = BigInt.str2bigInt(DH.G, 10)
- var N = BigInt.str2bigInt(DH.N, 16)
+ var G = BigInt.str2bigInt(CONST.G, 10)
+ var N = BigInt.str2bigInt(CONST.N, 16)
var TWO = BigInt.str2bigInt('2', 10)
var N_MINUS_2 = BigInt.sub(N, TWO)
@@ -166,8 +161,8 @@
this.otr.smInit()
// go encrypted
- this.otr.authstate = STATES.AUTHSTATE_NONE
- this.otr.msgstate = STATES.MSGSTATE_ENCRYPTED
+ this.otr.authstate = CONST.AUTHSTATE_NONE
+ this.otr.msgstate = CONST.MSGSTATE_ENCRYPTED
// send stored msgs
this.otr.sendStored()
@@ -185,7 +180,7 @@
msg = HLP.splitype(['DATA', 'DATA'], msg.msg)
- if (this.otr.authstate === STATES.AUTHSTATE_AWAITING_DHKEY) {
+ 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)) {
@@ -194,15 +189,15 @@
} else {
// forget
this.our_dh = this.otr.dh()
- this.otr.authstate = STATES.AUTHSTATE_NONE
+ this.otr.authstate = CONST.AUTHSTATE_NONE
this.r = null
this.myhashed = null
}
} else if (
- this.otr.authstate === STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG
) this.our_dh = this.otr.dh()
- this.otr.authstate = STATES.AUTHSTATE_AWAITING_REVEALSIG
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_REVEALSIG
this.encrypted = msg[0].substring(4)
this.hashed = msg[1].substring(4)
@@ -218,15 +213,15 @@
msg = HLP.splitype(['MPI'], msg.msg)
- if (this.otr.authstate !== STATES.AUTHSTATE_AWAITING_DHKEY) {
- if (this.otr.authstate === STATES.AUTHSTATE_AWAITING_SIG) {
+ 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 = STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_SIG
this.their_y = HLP.readMPI(msg[0])
@@ -245,7 +240,7 @@
debug('signature message')
if ( !this.otr.ALLOW_V2 ||
- this.otr.authstate !== STATES.AUTHSTATE_AWAITING_REVEALSIG
+ this.otr.authstate !== CONST.AUTHSTATE_AWAITING_REVEALSIG
) return // ignore
msg = HLP.splitype(['DATA', 'DATA', 'MAC'], msg.msg)
@@ -303,7 +298,7 @@
debug('data message')
if ( !this.otr.ALLOW_V2 ||
- this.otr.authstate !== STATES.AUTHSTATE_AWAITING_SIG
+ this.otr.authstate !== CONST.AUTHSTATE_AWAITING_SIG
) return // ignore
msg = HLP.splitype(['DATA', 'MAC'], msg.msg)
@@ -337,7 +332,7 @@
},
sendMsg: function (msg) {
- msg = STATES.OTR_VERSION_2 + msg
+ msg = CONST.OTR_VERSION_2 + msg
msg = HLP.wrapMsg(msg, this.otr.fragment_size)
if (msg[0]) return this.otr.error(msg[0])
this.otr.sendMsg(msg[1], true)
@@ -349,7 +344,7 @@
// save in case we have to resend
this.dhcommit = '\x02'
- this.otr.authstate = STATES.AUTHSTATE_AWAITING_DHKEY
+ this.otr.authstate = CONST.AUTHSTATE_AWAITING_DHKEY
var gxmpi = HLP.packMPI(this.our_dh.publicKey)
View
12 lib/states.js → lib/const.js
@@ -2,10 +2,14 @@
var root = this
- var STATES = {
+ var CONST = {
+
+ // diffie-heilman
+ N : 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF'
+ , G : '2'
// otr message states
- MSGSTATE_PLAINTEXT : 0
+ , MSGSTATE_PLAINTEXT : 0
, MSGSTATE_ENCRYPTED : 1
, MSGSTATE_FINISHED : 2
@@ -26,9 +30,9 @@
}
if (typeof exports !== 'undefined') {
- module.exports = STATES
+ module.exports = CONST
} else {
- root.STATES = STATES
+ root.OTR.CONST = CONST
}
}).call(this)
View
16 lib/dh.js
@@ -1,16 +0,0 @@
-;(function () {
-
- var root = this
-
- var DH = {
- "N": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
- , "G": "2"
- }
-
- if (typeof exports !== 'undefined') {
- module.exports = DH
- } else {
- root.DH = DH
- }
-
-}).call(this)
View
40 lib/dsa.js
@@ -5,21 +5,21 @@
var root = this
- var DSA
+ var CryptoJS, BigInt, HLP
if (typeof exports !== 'undefined') {
- DSA = exports
+ module.exports = DSA
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.js')
+ HLP = require('./helpers.js')
} else {
- DSA = root.DSA = {}
- }
-
- var BigInt = root.BigInt
- , CryptoJS = root.CryptoJS
- , HLP = root.HLP
-
- if (typeof require !== 'undefined') {
- BigInt || (BigInt = require('../vendor/bigint.js'))
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- HLP || (HLP = require('./helpers.js'))
+ // 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
+ HLP = DSA.HLP
}
var ZERO = BigInt.str2bigInt('0', 10)
@@ -52,10 +52,8 @@
return k
}
- DSA.Key = Key
-
- function Key() {
- if (!(this instanceof Key)) return new Key()
+ function DSA() {
+ if (!(this instanceof DSA)) return new DSA()
this.N = 160
this.L = 1024
@@ -69,9 +67,9 @@
this.y = BigInt.powMod(this.g, this.x, this.p)
}
- Key.prototype = {
+ DSA.prototype = {
- constructor: Key,
+ constructor: DSA,
makePQ: function() {
var g = this.N
@@ -217,8 +215,8 @@
}
DSA.inherit = function (key) {
- key.__proto__ = DSA.Key.prototype
- key.constructor = DSA.Key
+ key.__proto__ = DSA.prototype
+ key.constructor = DSA
key.type = '\x00\x00'
}
View
18 lib/helpers.js
@@ -2,19 +2,17 @@
var root = this
- var HLP
+ var HLP, CryptoJS, BigInt
if (typeof exports !== 'undefined') {
HLP = exports
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.js')
} else {
- HLP = root.HLP = {}
- }
-
- var BigInt = root.BigInt
- , CryptoJS = root.CryptoJS
-
- if (typeof require !== 'undefined') {
- BigInt || (BigInt = require('../vendor/bigint.js'))
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
+ HLP = {}
+ if (root.OTR) root.OTR.HLP = HLP
+ if (root.DSA) root.DSA.HLP = HLP
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
}
// data types (byte lengths)
View
83 lib/otr.js
@@ -2,48 +2,47 @@
var root = this
+ var CryptoJS, BigInt, CONST, HLP, Parse, AKE, SM, DSA
if (typeof exports !== 'undefined') {
module.exports = OTR
+ CryptoJS = require('../vendor/crypto.js')
+ BigInt = require('../vendor/bigint.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')
} else {
+ // copy over and expose internals
+ Object.keys(root.OTR).forEach(function (k) {
+ OTR[k] = root.OTR[k]
+ })
root.OTR = OTR
- }
-
- var CryptoJS = root.CryptoJS
- , BigInt = root.BigInt
- , DH = root.DH
- , HLP = root.HLP
- , DSA = root.DSA
- , AKE = root.AKE
- , SM = root.SM
- , STATES = root.STATES
- , ParseOTR = root.ParseOTR
-
- if (typeof require !== 'undefined') {
- CryptoJS || (CryptoJS = require('../vendor/crypto.js'))
- BigInt || (BigInt = require('../vendor/bigint.js'))
- DH || (DH = require('./dh.js'))
- HLP || (HLP = require('./helpers.js'))
- DSA || (DSA = require('./dsa.js'))
- AKE || (AKE = require('./ake.js'))
- SM || (SM = require('./sm.js'))
- STATES || (STATES = require('./states.js'))
- ParseOTR || (ParseOTR = require('./parse.js'))
+ CryptoJS = root.CryptoJS
+ BigInt = root.BigInt
+ 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(DH.G, 10)
- var N = BigInt.str2bigInt(DH.N, 16)
+ var G = BigInt.str2bigInt(CONST.G, 10)
+ var N = BigInt.str2bigInt(CONST.N, 16)
// OTR contructor
function OTR(priv, uicb, iocb, options) {
if (!(this instanceof OTR)) return new OTR(priv, uicb, iocb, options)
// private keys
- if (priv && !(priv instanceof DSA.Key))
+ if (priv && !(priv instanceof DSA))
throw new Error('Requires long-lived DSA key.')
- this.priv = priv ? priv : new DSA.Key()
+ this.priv = priv ? priv : new DSA()
// options
options = options || {}
@@ -81,8 +80,8 @@
init: function () {
- this.msgstate = STATES.MSGSTATE_PLAINTEXT
- this.authstate = STATES.AUTHSTATE_NONE
+ this.msgstate = CONST.MSGSTATE_PLAINTEXT
+ this.authstate = CONST.AUTHSTATE_NONE
this.ALLOW_V2 = true
@@ -91,7 +90,7 @@
this.WHITESPACE_START_AKE = false
this.ERROR_START_AKE = false
- ParseOTR.initFragment(this)
+ Parse.initFragment(this)
this.versions = {}
@@ -249,7 +248,7 @@
},
prepareMsg: function (msg) {
- if (this.msgstate !== STATES.MSGSTATE_ENCRYPTED || this.their_keyid === 0)
+ if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || this.their_keyid === 0)
return this.error('Not ready to encrypt.')
var sessKeys = this.sessKeys[1][0]
@@ -257,7 +256,7 @@
var ctr = HLP.packCtr(sessKeys.send_counter)
- var send = STATES.OTR_VERSION_2 + '\x03' // version and type
+ var send = CONST.OTR_VERSION_2 + '\x03' // version and type
send += '\x00' // flag
send += HLP.packINT(this.our_keyid - 1)
send += HLP.packINT(this.their_keyid)
@@ -283,7 +282,7 @@
// ignore flag
var ign = (msg[0] === '\x01')
- if (this.msgstate !== STATES.MSGSTATE_ENCRYPTED || msg.length !== 8) {
+ if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || msg.length !== 8) {
if (!ign) this.error('Received an unreadable encrypted message.', true)
return
}
@@ -392,18 +391,18 @@
if (!internal) { // a user or sm msg
switch (this.msgstate) {
- case STATES.MSGSTATE_PLAINTEXT:
+ case CONST.MSGSTATE_PLAINTEXT:
if (this.REQUIRE_ENCRYPTION) {
this.storedMgs.push(msg)
this.sendQueryMsg()
return
}
if (this.SEND_WHITESPACE_TAG) {
- msg += STATES.WHITESPACE_TAG // 16 byte tag
- if (this.ALLOW_V2) msg += STATES.WHITESPACE_TAG_V2
+ msg += CONST.WHITESPACE_TAG // 16 byte tag
+ if (this.ALLOW_V2) msg += CONST.WHITESPACE_TAG_V2
}
break
- case STATES.MSGSTATE_FINISHED:
+ case CONST.MSGSTATE_FINISHED:
this.storedMgs.push(msg)
this.error('Message cannot be sent at this time.')
return
@@ -418,7 +417,7 @@
receiveMsg: function (msg) {
// parse type
- msg = ParseOTR.parseMsg(this, msg)
+ msg = Parse.parseMsg(this, msg)
if (!msg) return
@@ -434,12 +433,12 @@
break
case 'query':
if (msg.version !== '2') return
- if (this.msgstate === STATES.MSGSTATE_ENCRYPTED) this.akeInit()
+ if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) this.akeInit()
this.ake.initiateAKE()
break
default:
if ( this.REQUIRE_ENCRYPTION ||
- this.msgstate !== STATES.MSGSTATE_PLAINTEXT
+ this.msgstate !== CONST.MSGSTATE_PLAINTEXT
) this.error('Received an unencrypted message.')
// received a plaintext message
@@ -448,7 +447,7 @@
// received a whitespace tag
if ( this.WHITESPACE_START_AKE &&
- ~msg.ver.indexOf(STATES.OTR_VERSION_2)
+ ~msg.ver.indexOf(CONST.OTR_VERSION_2)
) this.ake.initiateAKE()
}
@@ -474,11 +473,11 @@
},
endOtr: function () {
- if (this.msgstate === STATES.MSGSTATE_ENCRYPTED) {
+ if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) {
this.sendMsg('\x00\x01\x00\x00')
this.sm = null
}
- this.msgstate = STATES.MSGSTATE_PLAINTEXT
+ this.msgstate = CONST.MSGSTATE_PLAINTEXT
}
}
View
38 lib/parse.js
@@ -2,27 +2,23 @@
var root = this
- var ParseOTR
+ var Parse, CryptoJS,