No Password Management


    0.2.16 • Public • Published

    Build Status

    Off-the Record Messaging Protocol in JavaScript


    This library hasn't been properly vetted by security researchers. Do not use in life and death situations!


    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


    The contents of build/ are the result of calling make build and are only updated with releases. Please submit patches against lib/ and vendor/.


    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


    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.


    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


    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) {
          // sucessfully ake'd with buddy
          // check if buddy.msgstate === OTR.CONST.MSGSTATE_ENCRYPTED
          // 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


    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.
    // Advertise your support of OTR using the whitespace tag.
    // Start the OTR AKE when you receive a whitespace tag.
    // 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)


    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
    // or,
    // for their key

    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"

    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)
        case 'trust':
          // smp completed
          // check data (true|false) and update ui accordingly
          // act ("asked"|"answered") provides info one who initiated the smp
        case 'abort':
          // smp was aborted. notify the user or update ui
          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
        (name "")
        (protocol prpl-jabber)
            (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 = ""
    var buddy = new OTR()
    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


    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>




    In The Wild

    A sampling of projects that use this library:


    Bitcoins: 1BWLnnig89fpn8hCcASd2B1YbfK6j1vtX3


    MPL v2.0


    npm i otr

    DownloadsWeekly Downloads






    Last publish


    • arlolra