UNPKG

otr3

Version:

OTR Messaging Protocol [native libotr3.2.x]

228 lines (205 loc) 8.33 kB
/* * Off-the-Record Messaging bindings for nodejs * Copyright (C) 2012 Mokhtar Naamani, * <mokhtar.naamani@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ var debug_mode = false; exports.debugOn = function(){ debug_mode = true; }; exports.debugOff = function(){ debug_mode = false; }; function debug(msg){ if(debug_mode) console.error(msg); } //load node C++ native module var otr=require("./build/Release/otrnat"); if(otr.version()!="3.2.1"){ console.error("Warning. You are not using the latest version of libotr on your system."); } var util = require('util'); var events = require('events'); //low level - wrappers to C API exports.version = otr.version; exports.UserState = otr.UserState; exports.ConnContext = otr.ConnContext; //discourage direct use of otr.MessageAppOps - keep it internal //exports.MessageAppOps = otr.MessageAppOps; //high level - javascript API exports.User = User; exports.Session = OTRChannel; exports.OTRChannel = OTRChannel; exports.POLICY = POLICY; util.inherits(OTRChannel, events.EventEmitter); function User( config ){ this.state = new otr.UserState(); this.keys = config.keys; this.fingerprints = config.fingerprints; try{ this.state.readKeysSync(this.keys); }catch(e){ console.error("Warning Reading Keys:",e);} try{ this.state.readFingerprintsSync(this.fingerprints); }catch(e){ console.error("Warning Reading Fingerprints:",e);} } User.prototype.generateKey = function(accountname,protocol,callback){ this.state.generateKey(this.keys,accountname,protocol,callback); } User.prototype.ConnContext = function(accountname, protocol, recipient){ return new otr.ConnContext(this.state,accountname,protocol,recipient); } User.prototype.writeFingerprints = function(){ this.state.writeFingerprintsSync(this.fingerprints); } User.prototype.writeTrustedFingerprints = function(){ this.state.writeTrustedFingerprintsSync(this.fingerprints); } User.prototype.accounts = function(){ return this.state.accounts(); }; User.prototype.fingerprint = function(accountname,protocol){ return this.state.fingerprint(accountname,protocol); }; function OTRChannel(user, context, parameters){ events.EventEmitter.call(this); this.user = user; this.context = context; this.parameters = parameters; this.ops = new otr.MessageAppOps( OtrEventHandler(this) ); } OTRChannel.prototype.connect = function(){ return this.send("?OTR?"); }; OTRChannel.prototype.send = function(message){ //message can be any object that can be serialsed to a string using it's .toString() method. var msgout, err; msgout = this.ops.messageSending(this.user.state, this.context.accountname, this.context.protocol, this.context.username, message.toString()); if(msgout){ err=this.ops.fragmentAndSend(this.context,msgout); return err; } }; OTRChannel.prototype.recv = function(message){ //message can be any object that can be serialsed to a string using it's .toString() method. var msg = this.ops.messageReceiving(this.user.state, this.context.accountname, this.context.protocol, this.context.username, message.toString()); if(msg) this.emit("message",msg, this.isEncrypted()); }; OTRChannel.prototype.close = function(){ this.ops.disconnect(this.user.state,this.context.accountname,this.context.protocol,this.context.username); this.emit("shutdown"); }; OTRChannel.prototype.start_smp = function(secret){ var sec = secret; sec = sec || (this.parameters? this.parameters.secret:undefined); if(sec){ this.ops.initSMP(this.user.state, this.context, sec); }else{ throw( new Error("No Secret Provided")); } }; OTRChannel.prototype.start_smp_question = function(question,secret){ if(!question){ throw(new Error("No Question Provided")); } var sec = secret; if(!sec){ sec = this.parameters ? this.parameters.secrets : undefined; if(!sec) throw(new Error("No Secrets Provided")); sec = sec[question]; } if(!sec) throw(new Error("No Secret Matched for Question")); this.ops.initSMP(this.user.state, this.context, sec,question); }; OTRChannel.prototype.respond_smp = function(secret){ var sec = secret ? secret : undefined; if(!sec){ sec = this.parameters ? this.parameters.secret : undefined; } if(!sec) throw( new Error("No Secret Provided")); this.ops.respondSMP(this.user.state, this.context, sec); }; OTRChannel.prototype.isEncrypted = function(){ return (this.context.msgstate===1); }; OTRChannel.prototype.isAuthenticated = function(){ return (this.context.trust==="smp"); }; function OtrEventHandler( otrChannel ){ function emit(){ otrChannel.emit.apply(otrChannel,arguments); } return (function(o){ debug(o.EVENT); switch(o.EVENT){ //custom events - raised by ops.messageReceiving() case "smp_request": emit(o.EVENT,o.question);break; case "smp_complete": emit(o.EVENT);break; case "smp_failed": emit(o.EVENT);break; case "smp_aborted": emit(o.EVENT);break; case "remote_disconnected": emit(o.EVENT);break; //message operations case "gone_secure": emit(o.EVENT);break; case "gone_insecure": emit(o.EVENT);break; case "still_secure": emit(o.EVENT);break; case "update_context_list": emit(o.EVENT);break; case "inject_message": emit(o.EVENT,o.message);break; case "create_privkey": emit(o.EVENT,o.accountname,o.protocol);break; case "display_otr_message": emit(o.EVENT,o.message);break; case "notify": emit(o.EVENT,o.title,o.primary,o.secondary);break; case "log_message": emit(o.EVENT,o.message);break; case "new_fingerprint": emit(o.EVENT,o.fingerprint);break; case "write_fingerprints": //otrChannel.user.writeFingerprints(); emit(o.EVENT);//app will decide to write fringerprints or not. break; case "is_logged_in": return 1;break;//assume remote buddy is logged in. case "policy": //if no policy specified, default to DEFAULT policy if(!otrChannel.parameters) return POLICY("DEFAULT"); if(typeof otrChannel.parameters.policy == 'number' ){ return otrChannel.parameters.policy; } return POLICY("DEFAULT"); case "max_message_size": //fragment size : default no fragmentation if(!otrChannel.parameters) return 0; return otrChannel.parameters.MTU || 0; default: //test that we handled all the operations debug("Unhandled Message Operation:",o.EVENT); return; } }); } /* --- libotr-3.2.1/src/proto.h */ var _policy = { 'NEVER':0x00, 'ALLOW_V1': 0x01, 'ALLOW_V2': 0x02, 'REQUIRE_ENCRYPTION': 0x04, 'SEND_WHITESPACE_TAG': 0x08, 'WHITESPACE_START_AKE': 0x10, 'ERROR_START_AKE': 0x20 }; _policy['VERSION_MASK'] = _policy['ALLOW_V1']|_policy['ALLOW_V2']; _policy['OPPORTUNISTIC'] = _policy['ALLOW_V1']|_policy['ALLOW_V2']|_policy['SEND_WHITESPACE_TAG']|_policy['WHITESPACE_START_AKE']|_policy['ERROR_START_AKE']; _policy['MANUAL'] = _policy['ALLOW_V1']|_policy['ALLOW_V2']; _policy['ALWAYS'] = _policy['ALLOW_V1']|_policy['ALLOW_V2']|_policy['REQUIRE_ENCRYPTION']|_policy['WHITESPACE_START_AKE']|_policy['ERROR_START_AKE']; _policy['DEFAULT'] = _policy['OPPORTUNISTIC'] function POLICY(p){ return _policy[p]; };