electron-otr
v0.2.16
Published
Off-the-Record Messaging Protocol
Downloads
12
Maintainers
Readme
Off-the Record Messaging Protocol in JavaScript
Warning
This library hasn't been properly vetted by security researchers. Do not use in life and death situations!
Install
Include the build files on the page,
<!-- Load dependencies -->
<script src="build/dep/bigint.js"></script>
<script src="build/dep/crypto.js"></script>
<script src="build/dep/eventemitter.js"></script>
<!-- Load otr.js or otr.min.js -->
<script src="build/otr.min.js"></script>
Here's an example use in the browser.
Although this is a client library, it can be used on the server.
npm install otr
And then,
var DSA = require('otr').DSA
var OTR = require('otr').OTR
Build
The contents of build/
are the result of calling make build
and are only
updated with releases. Please submit patches against lib/
and vendor/
.
Release
The normal flow for making a release is as follows,
make test
// bump the version numbers in package.json / bower.json
make build
git changelog // cleanup the changelog
git commit -m "bump version"
git tag -a vX.X.X -m "version X.X.X"
git push origin master
git push --tags
npm publish
// update github releases and pages
Usage
Initial setup: Compute your long-lived key beforehand. Currently this is expensive and can take several seconds.
// precompute your DSA key
var myKey = new DSA()
For each user you're communicating with, instantiate an OTR object.
// provide options
var options = {
fragment_size: 140
, send_interval: 200
, priv: myKey
}
var buddy = new OTR(options)
buddy.on('ui', function (msg, encrypted, meta) {
console.log("message to display to the user: " + msg)
// encrypted === true, if the received msg was encrypted
console.log("(optional) with receiveMsg attached meta data: " + meta)
})
buddy.on('io', function (msg, meta) {
console.log("message to send to buddy: " + msg)
console.log("(optional) with sendMsg attached meta data: " + meta)
})
buddy.on('error', function (err, severity) {
if (severity === 'error') // either 'error' or 'warn'
console.error("error occurred: " + err)
})
New message from buddy received: Pass the received message to the receiveMsg
method.
var rcvmsg = "Message from buddy."
var meta = "optional some meta data, like delay"
buddy.receiveMsg(rcvmsg, meta)
Send a message to buddy: Pass the message to the sendMsg
method.
var newmsg = "Message to userA."
var meta = "optional some meta data, like message id"
buddy.sendMsg(newmsg, meta)
Going encrypted: Initially, messages are sent in plaintext. To manually initiate the authenticated key exchange.
buddy.sendQueryMsg()
Alternatively, one can set the policy REQUIRE_ENCRYPTION
and send a plaintext
message. This will store the message, initiate the authentication and then,
upon success, send it out.
buddy.REQUIRE_ENCRYPTION = true
buddy.sendMsg('My plaintext message to be encrypted.')
Another policy, SEND_WHITESPACE_TAG
, will append tags to plaintext messages,
indicating a willingness to speak OTR. If the recipient in turn has set the
policy WHITESPACE_START_AKE
, the AKE will be initiated.
Close private connection: To end an encrypted communication session,
buddy.endOtr(function() {
// calls back when the 'disconnect' message has been sent
})
will return the message state to plaintext and notify the correspondent.
Options: A dictionary of the current options accepted by the OTR constructor.
var options = {
// long-lived private key
priv: new DSA(),
// turn on some debuggin logs
debug: false,
// fragment the message in case of char limits
fragment_size: 140,
// ms delay between sending fragmented msgs, avoid rate limits
send_interval: 200
}
Status
A listener can be attached for status changes. These are non-standard codes, specific to this OTR library, indicating various things like the AKE success.
buddy.on('status', function (state) {
switch (state) {
case OTR.CONST.STATUS_AKE_SUCCESS:
// sucessfully ake'd with buddy
// check if buddy.msgstate === OTR.CONST.MSGSTATE_ENCRYPTED
break
case OTR.CONST.STATUS_END_OTR:
// if buddy.msgstate === OTR.CONST.MSGSTATE_FINISHED
// inform the user that his correspondent has closed his end
// of the private connection and the user should do the same
break
}
})
Policies
To be set on a per-correspondent basis. The defaults are as follows:
// Allow version 2 or 3 of the OTR protocol to be used.
ALLOW_V2 = true
ALLOW_V3 = true
// Refuse to send unencrypted messages.
REQUIRE_ENCRYPTION = false
// Advertise your support of OTR using the whitespace tag.
SEND_WHITESPACE_TAG = false
// Start the OTR AKE when you receive a whitespace tag.
WHITESPACE_START_AKE = false
// Start the OTR AKE when you receive an OTR Error Message.
ERROR_START_AKE = false
Instance Tags
These are intended to be persistent and can be precomputed.
var myTag = OTR.makeInstanceTag()
var options = { instance_tag: myTag }
var buddy = new OTR(options)
Fingerprints
OTR public key fingerprints can be obtained as follows:
// assume you've gone through the ake with buddy
var buddy = new OTR({ priv: myKey })
// buddy.msgstate === OTR.CONST.MSGSTATE_ENCRYPTED
// for my key, either one of the following
myKey.fingerprint()
// or,
buddy.priv.fingerprint()
// for their key
buddy.their_priv_pk.fingerprint()
Socialist Millionaire Protocol
At any time after establishing encryption, either party can initiate SMP to detect impersonation or man-in-the-middle attacks. A shared secret, exchanged through an out-of-band channel prior to starting the conversation, is required.
var secret = "ghostbusters"
buddy.smpSecret(secret)
A question can be supplied, as a reminder of the shared secret.
var question = "who are you going to call?"
buddy.smpSecret(secret, question)
If you plan on using SMP, as opposed to just allowing fingerprints for verification, provide on optional callback when initiating OTR, otherwise a no-opt is fired.
var buddy = new OTR()
buddy.on('smp', function (type, data, act) {
switch (type) {
case 'question':
// call(data) some function with question?
// return the user supplied data to
// userA.smpSecret(secret)
break
case 'trust':
// smp completed
// check data (true|false) and update ui accordingly
// act ("asked"|"answered") provides info one who initiated the smp
break
case 'abort':
// smp was aborted. notify the user or update ui
default:
throw new Error('Unknown type.')
}
})
Both users should run the SMP to establish trust. Further, it should be run each time a partner presents a fresh long-lived key.
Private Keys
To export a private, long-lived key:
var myKey = new DSA()
var string = myKey.packPrivate() // returns a Base64 encoded string
It can then be imported as follows,
string = "AAAAAACA4COdKHpU/np9F8EDdnGiJJmc89p ... I9BzTkQduFA7ovXAMY="
myKey = DSA.parsePrivate(string)
Importing the (somewhat) standard libotr s-expression format works as well,
// in node.js
var fs = require('fs')
string = fs.readFileSync("~/.purple/otr.private_key", 'utf8')
// leaving out the terminal backslashes needed for multiline strings in js
string = "(privkeys
(account
(name "[email protected]")
(protocol prpl-jabber)
(private-key
(dsa
(p #00FC07 ... 2AEFD07A2081#)
(q #ASD5FF ... LKJDF898DK12#)
(g #535E3E ... 1E3BC1FC6F26#)
(y #0AC867 ... 8969009B6ECF#)
(x #14D034 ... F72D79043216#)
)
)
)
)"
myKey = DSA.parsePrivate(string, true)
Extra Symmetric Key
In version 3 of the protocol, an extra symmetric key is derived during the AKE. This may be used for secure communication over a different channel (e.g., file transfer, voice chat).
var filename = "test.zip"
var buddy = new OTR()
buddy.sendFile(filename)
buddy.on('file', function (type, key, filename) {
// type === 'send'
// key should be used to encrypt filename
// and sent through a different channel
})
On the other end,
var friend = new OTR()
friend.on('file', function (type, key, filename) {
// type === 'receive'
// decrypt filename with key, once received
})
WebWorkers
Some support exists for calling computationally expensive work off the main thread. However, some feedback on these APIs would be appreciated.
// generate a DSA key in a web worker
DSA.createInWebWorker(null, function (key) {
var buddy = new OTR({
priv: key,
// setting `smw` to a truthy value will perform the socialist
// millionaire protocol in a webworker.
smw: {}
})
})
WebWorkers don't have access to window.crypto.getRandomValues()
, so they will
need to include Salsa20.
<script src="build/dep/salsa20.js"></script>
Links
Spec:
- http://www.cypherpunks.ca/otr/Protocol-v3-4.0.0.html
- http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html
- See:
specs/
Using:
In The Wild
A sampling of projects that use this library:
- Cryptocat
- Yabasta
- Diaspora
- Converse.js
- WebRTCCopy
- OTRon
- ojsxc (owncloud)
- sjsxc (SOGo)
- LoquiIM
- Salut à Toi (Python wrapper for Pyjamas)
- HackTunnel
Donate
Bitcoins: 1BWLnnig89fpn8hCcASd2B1YbfK6j1vtX3
License
MPL v2.0