diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/chat/modules/OTRLib.sys.mjs | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/chat/modules/OTRLib.sys.mjs')
-rw-r--r-- | comm/chat/modules/OTRLib.sys.mjs | 1151 |
1 files changed, 1151 insertions, 0 deletions
diff --git a/comm/chat/modules/OTRLib.sys.mjs b/comm/chat/modules/OTRLib.sys.mjs new file mode 100644 index 0000000000..b9fddbe89e --- /dev/null +++ b/comm/chat/modules/OTRLib.sys.mjs @@ -0,0 +1,1151 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const otrl_version = [4, 1, 1]; + +import { CLib } from "resource:///modules/CLib.sys.mjs"; + +import { ctypes } from "resource://gre/modules/ctypes.sys.mjs"; + +var systemOS = Services.appinfo.OS.toLowerCase(); + +var abi = ctypes.default_abi; + +var libotr, libotrPath; + +function getLibraryFilename(baseName, suffix) { + return ctypes.libraryName(baseName) + suffix; +} + +function getSystemVersionedFilename() { + let baseName; + let suffix; + + switch (systemOS) { + case "winnt": + baseName = "libotr-5"; + suffix = ""; + break; + case "darwin": + baseName = "otr.5"; + suffix = ""; + break; + default: + baseName = "otr"; + suffix = ".5"; + break; + } + + return getLibraryFilename(baseName, suffix); +} + +function getDistributionFilename() { + let baseName; + let suffix; + + if (systemOS === "winnt") { + baseName = "libotr"; + suffix = ""; + } else { + baseName = "otr"; + suffix = ""; + } + + return getLibraryFilename(baseName, suffix); +} + +function getDistributionFullPath() { + let binPath = Services.dirsvc.get("XpcomLib", Ci.nsIFile).path; + let binDir = PathUtils.parent(binPath); + return PathUtils.join(binDir, getDistributionFilename()); +} + +function tryLoadOTR(filename, info) { + libotrPath = filename; + try { + libotr = ctypes.open(filename); + } catch (e) { + return `Tried to load ${filename}${info}`; + } + return ""; +} + +function loadExternalOTRLib() { + const systemInfo = " from system's standard library locations."; + + let info = ""; + // Try to load using an absolute path from our install directory + if (!libotr) { + info += tryLoadOTR(getDistributionFullPath(), ""); + } + + // Try to load using our expected filename from system directories + if (!libotr) { + info += ", " + tryLoadOTR(getDistributionFilename(), systemInfo); + } + + // Try to load using a versioned library name + if (!libotr) { + info += ", " + tryLoadOTR(getSystemVersionedFilename(), systemInfo); + } + + // Try other filenames + + if (!libotr && systemOS == "winnt") { + info += ", " + tryLoadOTR(getLibraryFilename("otr.5", ""), systemInfo); + } + + if (!libotr && systemOS == "winnt") { + info += ", " + tryLoadOTR(getLibraryFilename("otr-5", ""), systemInfo); + } + + if (!libotr) { + info += ", " + tryLoadOTR(getLibraryFilename("otr", ""), systemInfo); + } + + if (!libotr) { + throw new Error("Cannot load required OTR library; " + info); + } +} + +export var OTRLibLoader = { + init() { + loadExternalOTRLib(); + if (libotr) { + enableOTRLibJS(); + } + return OTRLib; + }, +}; + +// Helper function to open files with the path properly encoded. +var callWithFILEp = function () { + // Windows filenames are in UTF-16. + let charType = systemOS === "winnt" ? "jschar" : "char"; + + let args = Array.from(arguments); + let func = args.shift() + "_FILEp"; + let mode = ctypes[charType].array()(args.shift()); + let ind = args.shift(); + let filename = ctypes[charType].array()(args[ind]); + + let file = CLib.fopen(filename, mode); + if (file.isNull()) { + return 1; + } + + // Swap filename with file. + args[ind] = file; + + let ret = OTRLib[func].apply(OTRLib, args); + CLib.fclose(file); + return ret; +}; + +// type defs + +const FILE = CLib.FILE; + +const time_t = ctypes.long; +const gcry_error_t = ctypes.unsigned_int; +const gcry_cipher_hd_t = ctypes.StructType("gcry_cipher_handle").ptr; +const gcry_md_hd_t = ctypes.StructType("gcry_md_handle").ptr; +const gcry_mpi_t = ctypes.StructType("gcry_mpi").ptr; + +const otrl_instag_t = ctypes.unsigned_int; +const OtrlPolicy = ctypes.unsigned_int; +const OtrlTLV = ctypes.StructType("s_OtrlTLV"); +const ConnContext = ctypes.StructType("context"); +const ConnContextPriv = ctypes.StructType("context_priv"); +const OtrlMessageAppOps = ctypes.StructType("s_OtrlMessageAppOps"); +const OtrlAuthInfo = ctypes.StructType("OtrlAuthInfo"); +const Fingerprint = ctypes.StructType("s_fingerprint"); +const s_OtrlUserState = ctypes.StructType("s_OtrlUserState"); +const OtrlUserState = s_OtrlUserState.ptr; +const OtrlSMState = ctypes.StructType("OtrlSMState"); +const DH_keypair = ctypes.StructType("DH_keypair"); +const OtrlPrivKey = ctypes.StructType("s_OtrlPrivKey"); +const OtrlInsTag = ctypes.StructType("s_OtrlInsTag"); +const OtrlPendingPrivKey = ctypes.StructType("s_OtrlPendingPrivKey"); + +const OTRL_PRIVKEY_FPRINT_HUMAN_LEN = 45; +const fingerprint_t = ctypes.char.array(OTRL_PRIVKEY_FPRINT_HUMAN_LEN); +const hash_t = ctypes.unsigned_char.array(20); + +const app_data_free_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, +]).ptr; + +// enums + +const OtrlErrorCode = ctypes.int; +const OtrlSMPEvent = ctypes.int; +const OtrlMessageEvent = ctypes.int; +const OtrlFragmentPolicy = ctypes.int; +const OtrlConvertType = ctypes.int; +const OtrlMessageState = ctypes.int; +const OtrlAuthState = ctypes.int; +const OtrlSessionIdHalf = ctypes.int; +const OtrlSMProgState = ctypes.int; +const NextExpectedSMP = ctypes.int; + +// callback signatures + +const policy_cb_t = ctypes.FunctionType(abi, OtrlPolicy, [ + ctypes.void_t.ptr, + ConnContext.ptr, +]).ptr; + +const create_privkey_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, +]).ptr; + +const is_logged_in_cb_t = ctypes.FunctionType(abi, ctypes.int, [ + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, +]).ptr; + +const inject_message_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, +]).ptr; + +const update_context_list_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, +]).ptr; + +const new_fingerprint_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + OtrlUserState, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.unsigned_char.array(20), +]).ptr; + +const write_fingerprint_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, +]).ptr; + +const gone_secure_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, +]).ptr; + +const gone_insecure_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, +]).ptr; + +const still_secure_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.int, +]).ptr; + +const max_message_size_cb_t = ctypes.FunctionType(abi, ctypes.int, [ + ctypes.void_t.ptr, + ConnContext.ptr, +]).ptr; + +const account_name_cb_t = ctypes.FunctionType(abi, ctypes.char.ptr, [ + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, +]).ptr; + +const account_name_free_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, +]).ptr; + +const received_symkey_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.unsigned_int, + ctypes.unsigned_char.ptr, + ctypes.size_t, + ctypes.unsigned_char.ptr, +]).ptr; + +const otr_error_message_cb_t = ctypes.FunctionType(abi, ctypes.char.ptr, [ + ctypes.void_t.ptr, + ConnContext.ptr, + OtrlErrorCode, +]).ptr; + +const otr_error_message_free_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, +]).ptr; + +const resent_msg_prefix_cb_t = ctypes.FunctionType(abi, ctypes.char.ptr, [ + ctypes.void_t.ptr, + ConnContext.ptr, +]).ptr; + +const resent_msg_prefix_free_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, +]).ptr; + +const handle_smp_event_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + OtrlSMPEvent, + ConnContext.ptr, + ctypes.unsigned_short, + ctypes.char.ptr, +]).ptr; + +const handle_msg_event_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + OtrlMessageEvent, + ConnContext.ptr, + ctypes.char.ptr, + gcry_error_t, +]).ptr; + +const create_instag_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, +]).ptr; + +const convert_msg_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, + OtrlConvertType, + ctypes.char.ptr.ptr, + ctypes.char.ptr, +]).ptr; + +const convert_free_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.char.ptr, +]).ptr; + +const timer_control_cb_t = ctypes.FunctionType(abi, ctypes.void_t, [ + ctypes.void_t.ptr, + ctypes.unsigned_int, +]).ptr; + +// defines + +s_OtrlUserState.define([ + { context_root: ConnContext.ptr }, + { privkey_root: OtrlPrivKey.ptr }, + { instag_root: OtrlInsTag.ptr }, + { pending_root: OtrlPendingPrivKey.ptr }, + { timer_running: ctypes.int }, +]); + +Fingerprint.define([ + { next: Fingerprint.ptr }, + { tous: Fingerprint.ptr.ptr }, + { fingerprint: ctypes.unsigned_char.ptr }, + { context: ConnContext.ptr }, + { trust: ctypes.char.ptr }, +]); + +DH_keypair.define([ + { groupid: ctypes.unsigned_int }, + { priv: gcry_mpi_t }, + { pub: gcry_mpi_t }, +]); + +OtrlSMState.define([ + { secret: gcry_mpi_t }, + { x2: gcry_mpi_t }, + { x3: gcry_mpi_t }, + { g1: gcry_mpi_t }, + { g2: gcry_mpi_t }, + { g3: gcry_mpi_t }, + { g3o: gcry_mpi_t }, + { p: gcry_mpi_t }, + { q: gcry_mpi_t }, + { pab: gcry_mpi_t }, + { qab: gcry_mpi_t }, + { nextExpected: NextExpectedSMP }, + { received_question: ctypes.int }, + { sm_prog_state: OtrlSMProgState }, +]); + +OtrlAuthInfo.define([ + { authstate: OtrlAuthState }, + { context: ConnContext.ptr }, + { our_dh: DH_keypair }, + { our_keyid: ctypes.unsigned_int }, + { encgx: ctypes.unsigned_char.ptr }, + { encgx_len: ctypes.size_t }, + { r: ctypes.unsigned_char.array(16) }, + { hashgx: ctypes.unsigned_char.array(32) }, + { their_pub: gcry_mpi_t }, + { their_keyid: ctypes.unsigned_int }, + { enc_c: gcry_cipher_hd_t }, + { enc_cp: gcry_cipher_hd_t }, + { mac_m1: gcry_md_hd_t }, + { mac_m1p: gcry_md_hd_t }, + { mac_m2: gcry_md_hd_t }, + { mac_m2p: gcry_md_hd_t }, + { their_fingerprint: ctypes.unsigned_char.array(20) }, + { initiated: ctypes.int }, + { protocol_version: ctypes.unsigned_int }, + { secure_session_id: ctypes.unsigned_char.array(20) }, + { secure_session_id_len: ctypes.size_t }, + { session_id_half: OtrlSessionIdHalf }, + { lastauthmsg: ctypes.char.ptr }, + { commit_sent_time: time_t }, +]); + +ConnContext.define([ + { next: ConnContext.ptr }, + { tous: ConnContext.ptr.ptr }, + { context_priv: ConnContextPriv.ptr }, + { username: ctypes.char.ptr }, + { accountname: ctypes.char.ptr }, + { protocol: ctypes.char.ptr }, + { m_context: ConnContext.ptr }, + { recent_rcvd_child: ConnContext.ptr }, + { recent_sent_child: ConnContext.ptr }, + { recent_child: ConnContext.ptr }, + { our_instance: otrl_instag_t }, + { their_instance: otrl_instag_t }, + { msgstate: OtrlMessageState }, + { auth: OtrlAuthInfo }, + { fingerprint_root: Fingerprint }, + { active_fingerprint: Fingerprint.ptr }, + { sessionid: ctypes.unsigned_char.array(20) }, + { sessionid_len: ctypes.size_t }, + { sessionid_half: OtrlSessionIdHalf }, + { protocol_version: ctypes.unsigned_int }, + { otr_offer: ctypes.int }, + { app_data: ctypes.void_t.ptr }, + { app_data_free: app_data_free_t }, + { smstate: OtrlSMState.ptr }, +]); + +OtrlMessageAppOps.define([ + { policy: policy_cb_t }, + { create_privkey: create_privkey_cb_t }, + { is_logged_in: is_logged_in_cb_t }, + { inject_message: inject_message_cb_t }, + { update_context_list: update_context_list_cb_t }, + { new_fingerprint: new_fingerprint_cb_t }, + { write_fingerprint: write_fingerprint_cb_t }, + { gone_secure: gone_secure_cb_t }, + { gone_insecure: gone_insecure_cb_t }, + { still_secure: still_secure_cb_t }, + { max_message_size: max_message_size_cb_t }, + { account_name: account_name_cb_t }, + { account_name_free: account_name_free_cb_t }, + { received_symkey: received_symkey_cb_t }, + { otr_error_message: otr_error_message_cb_t }, + { otr_error_message_free: otr_error_message_free_cb_t }, + { resent_msg_prefix: resent_msg_prefix_cb_t }, + { resent_msg_prefix_free: resent_msg_prefix_free_cb_t }, + { handle_smp_event: handle_smp_event_cb_t }, + { handle_msg_event: handle_msg_event_cb_t }, + { create_instag: create_instag_cb_t }, + { convert_msg: convert_msg_cb_t }, + { convert_free: convert_free_cb_t }, + { timer_control: timer_control_cb_t }, +]); + +OtrlTLV.define([ + { type: ctypes.unsigned_short }, + { len: ctypes.unsigned_short }, + { data: ctypes.unsigned_char.ptr }, + { next: OtrlTLV.ptr }, +]); + +// policies + +// const OTRL_POLICY_ALLOW_V1 = 0x01; +const OTRL_POLICY_ALLOW_V2 = 0x02; + +// const OTRL_POLICY_ALLOW_V3 = 0x04; +// See https://bugzilla.mozilla.org/show_bug.cgi?id=1550474 re v3. + +const OTRL_POLICY_REQUIRE_ENCRYPTION = 0x08; +const OTRL_POLICY_SEND_WHITESPACE_TAG = 0x10; +const OTRL_POLICY_WHITESPACE_START_AKE = 0x20; + +// const OTRL_POLICY_ERROR_START_AKE = 0x40; +// Disabled to avoid automatic resend and MITM, as explained in +// https://github.com/arlolra/ctypes-otr/issues/55 + +var OTRLib; + +function enableOTRLibJS() { + // this must be delayed until after "libotr" is initialized + + OTRLib = { + path: libotrPath, + + // libotr API version + otrl_version, + + init() { + // apply version array as arguments to the init function + if (this.otrl_init.apply(this, this.otrl_version)) { + throw new Error("Couldn't initialize libotr."); + } + return true; + }, + + // proto.h + + // If we ever see this sequence in a plaintext message, we'll assume the + // other side speaks OTR, and try to establish a connection. + OTRL_MESSAGE_TAG_BASE: " \t \t\t\t\t \t \t \t ", + + OTRL_POLICY_OPPORTUNISTIC: new ctypes.unsigned_int( + OTRL_POLICY_ALLOW_V2 | + // OTRL_POLICY_ALLOW_V3 | + OTRL_POLICY_SEND_WHITESPACE_TAG | + OTRL_POLICY_WHITESPACE_START_AKE | + // OTRL_POLICY_ERROR_START_AKE | + 0 + ), + + OTRL_POLICY_ALWAYS: new ctypes.unsigned_int( + OTRL_POLICY_ALLOW_V2 | + // OTRL_POLICY_ALLOW_V3 | + OTRL_POLICY_REQUIRE_ENCRYPTION | + OTRL_POLICY_WHITESPACE_START_AKE | + // OTRL_POLICY_ERROR_START_AKE | + 0 + ), + + fragPolicy: { + OTRL_FRAGMENT_SEND_SKIP: 0, + OTRL_FRAGMENT_SEND_ALL: 1, + OTRL_FRAGMENT_SEND_ALL_BUT_FIRST: 2, + OTRL_FRAGMENT_SEND_ALL_BUT_LAST: 3, + }, + + // Return a pointer to a newly-allocated OTR query message, customized + // with our name. The caller should free() the result when he's done + // with it. + otrl_proto_default_query_msg: libotr.declare( + "otrl_proto_default_query_msg", + abi, + ctypes.char.ptr, + ctypes.char.ptr, + OtrlPolicy + ), + + // Initialize the OTR library. Pass the version of the API you are using. + otrl_init: libotr.declare( + "otrl_init", + abi, + gcry_error_t, + ctypes.unsigned_int, + ctypes.unsigned_int, + ctypes.unsigned_int + ), + + // instag.h + + instag: { + OTRL_INSTAG_MASTER: new ctypes.unsigned_int(0), + OTRL_INSTAG_BEST: new ctypes.unsigned_int(1), + OTRL_INSTAG_RECENT: new ctypes.unsigned_int(2), + OTRL_INSTAG_RECENT_RECEIVED: new ctypes.unsigned_int(3), + OTRL_INSTAG_RECENT_SENT: new ctypes.unsigned_int(4), + OTRL_MIN_VALID_INSTAG: new ctypes.unsigned_int(0x100), + }, + + // Get a new instance tag for the given account and write to file. The FILE* + // must be open for writing. + otrl_instag_generate: callWithFILEp.bind( + null, + "otrl_instag_generate", + "wb", + 1 + ), + otrl_instag_generate_FILEp: libotr.declare( + "otrl_instag_generate_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr, + ctypes.char.ptr, + ctypes.char.ptr + ), + + // Read our instance tag from a file on disk into the given OtrlUserState. + // The FILE* must be open for reading. + otrl_instag_read: callWithFILEp.bind(null, "otrl_instag_read", "rb", 1), + otrl_instag_read_FILEp: libotr.declare( + "otrl_instag_read_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr + ), + + // Write our instance tags to a file on disk. The FILE* must be open for + // writing. + otrl_instag_write: callWithFILEp.bind(null, "otrl_instag_write", "wb", 1), + otrl_instag_write_FILEp: libotr.declare( + "otrl_instag_write_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr + ), + + // auth.h + + authState: { + OTRL_AUTHSTATE_NONE: 0, + OTRL_AUTHSTATE_AWAITING_DHKEY: 1, + OTRL_AUTHSTATE_AWAITING_REVEALSIG: 2, + OTRL_AUTHSTATE_AWAITING_SIG: 3, + OTRL_AUTHSTATE_V1_SETUP: 4, + }, + + // b64.h + + // base64 encode data. Insert no linebreaks or whitespace. + // The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of + // space. This function will return the number of bytes actually used. + otrl_base64_encode: libotr.declare( + "otrl_base64_encode", + abi, + ctypes.size_t, + ctypes.char.ptr, + ctypes.unsigned_char.ptr, + ctypes.size_t + ), + + // base64 decode data. Skip non-base64 chars, and terminate at the + // first '=', or the end of the buffer. + // The buffer data must contain at least ((base64len+3) / 4) * 3 bytes + // of space. This function will return the number of bytes actually + // used. + otrl_base64_decode: libotr.declare( + "otrl_base64_decode", + abi, + ctypes.size_t, + ctypes.unsigned_char.ptr, + ctypes.char.ptr, + ctypes.size_t + ), + + // context.h + + otr_offer: { + OFFER_NOT: 0, + OFFER_SENT: 1, + OFFER_REJECTED: 2, + OFFER_ACCEPTED: 3, + }, + + messageState: { + OTRL_MSGSTATE_PLAINTEXT: 0, + OTRL_MSGSTATE_ENCRYPTED: 1, + OTRL_MSGSTATE_FINISHED: 2, + }, + + // Look up a connection context by name/account/protocol/instance from the + // given OtrlUserState. + otrl_context_find: libotr.declare( + "otrl_context_find", + abi, + ConnContext.ptr, + OtrlUserState, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + otrl_instag_t, + ctypes.int, + ctypes.int.ptr, + ctypes.void_t.ptr, + ctypes.void_t.ptr + ), + + // Set the trust level for a given fingerprint. + otrl_context_set_trust: libotr.declare( + "otrl_context_set_trust", + abi, + ctypes.void_t, + Fingerprint.ptr, + ctypes.char.ptr + ), + + // Find a fingerprint in a given context, perhaps adding it if not present. + otrl_context_find_fingerprint: libotr.declare( + "otrl_context_find_fingerprint", + abi, + Fingerprint.ptr, + ConnContext.ptr, + hash_t, + ctypes.int, + ctypes.int.ptr + ), + + // Forget a fingerprint (and maybe the whole context). + otrl_context_forget_fingerprint: libotr.declare( + "otrl_context_forget_fingerprint", + abi, + ctypes.void_t, + Fingerprint.ptr, + ctypes.int + ), + + // Return true iff the given fingerprint is marked as trusted. + otrl_context_is_fingerprint_trusted: libotr.declare( + "otrl_context_is_fingerprint_trusted", + abi, + ctypes.int, + Fingerprint.ptr + ), + + // dh.h + + sessionIdHalf: { + OTRL_SESSIONID_FIRST_HALF_BOLD: 0, + OTRL_SESSIONID_SECOND_HALF_BOLD: 1, + }, + + // sm.h + + nextExpectedSMP: { + OTRL_SMP_EXPECT1: 0, + OTRL_SMP_EXPECT2: 1, + OTRL_SMP_EXPECT3: 2, + OTRL_SMP_EXPECT4: 3, + OTRL_SMP_EXPECT5: 4, + }, + + smProgState: { + OTRL_SMP_PROG_OK: 0, + OTRL_SMP_PROG_CHEATED: -2, + OTRL_SMP_PROG_FAILED: -1, + OTRL_SMP_PROG_SUCCEEDED: 1, + }, + + // userstate.h + + // Create a new OtrlUserState. + otrl_userstate_create: libotr.declare( + "otrl_userstate_create", + abi, + OtrlUserState + ), + + // privkey.h + + // Generate a private DSA key for a given account, storing it into a file on + // disk, and loading it into the given OtrlUserState. Overwrite any + // previously generated keys for that account in that OtrlUserState. + otrl_privkey_generate: callWithFILEp.bind( + null, + "otrl_privkey_generate", + "w+b", + 1 + ), + otrl_privkey_generate_FILEp: libotr.declare( + "otrl_privkey_generate_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr, + ctypes.char.ptr, + ctypes.char.ptr + ), + + // Begin a private key generation that will potentially take place in + // a background thread. This routine must be called from the main + // thread. It will set *newkeyp, which you can pass to + // otrl_privkey_generate_calculate in a background thread. If it + // returns gcry_error(GPG_ERR_EEXIST), then a privkey creation for + // this accountname/protocol is already in progress, and *newkeyp will + // be set to NULL. + otrl_privkey_generate_start: libotr.declare( + "otrl_privkey_generate_start", + abi, + gcry_error_t, + OtrlUserState, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.void_t.ptr.ptr + ), + + // Do the private key generation calculation. You may call this from a + // background thread. When it completes, call + // otrl_privkey_generate_finish from the _main_ thread. + otrl_privkey_generate_calculate: libotr.declare( + "otrl_privkey_generate_calculate", + abi, + gcry_error_t, + ctypes.void_t.ptr + ), + + // Call this from the main thread only. It will write the newly created + // private key into the given file and store it in the OtrlUserState. + otrl_privkey_generate_finish: callWithFILEp.bind( + null, + "otrl_privkey_generate_finish", + "w+b", + 2 + ), + otrl_privkey_generate_finish_FILEp: libotr.declare( + "otrl_privkey_generate_finish_FILEp", + abi, + gcry_error_t, + OtrlUserState, + ctypes.void_t.ptr, + FILE.ptr + ), + + // Call this from the main thread only, in the event that the background + // thread generating the key is cancelled. The newkey is deallocated, + // and must not be used further. + otrl_privkey_generate_cancelled: libotr.declare( + "otrl_privkey_generate_cancelled", + abi, + gcry_error_t, + OtrlUserState, + ctypes.void_t.ptr + ), + + // Read a sets of private DSA keys from a file on disk into the given + // OtrlUserState. + otrl_privkey_read: callWithFILEp.bind(null, "otrl_privkey_read", "rb", 1), + otrl_privkey_read_FILEp: libotr.declare( + "otrl_privkey_read_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr + ), + + // Read the fingerprint store from a file on disk into the given + // OtrlUserState. + otrl_privkey_read_fingerprints: callWithFILEp.bind( + null, + "otrl_privkey_read_fingerprints", + "rb", + 1 + ), + otrl_privkey_read_fingerprints_FILEp: libotr.declare( + "otrl_privkey_read_fingerprints_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr, + ctypes.void_t.ptr, + ctypes.void_t.ptr + ), + + // Write the fingerprint store from a given OtrlUserState to a file on disk. + otrl_privkey_write_fingerprints: callWithFILEp.bind( + null, + "otrl_privkey_write_fingerprints", + "wb", + 1 + ), + otrl_privkey_write_fingerprints_FILEp: libotr.declare( + "otrl_privkey_write_fingerprints_FILEp", + abi, + gcry_error_t, + OtrlUserState, + FILE.ptr + ), + + // The length of a string representing a human-readable version of a + // fingerprint (including the trailing NUL). + OTRL_PRIVKEY_FPRINT_HUMAN_LEN, + + // Human readable fingerprint type + fingerprint_t, + + // fingerprint value + hash_t, + + // Calculate a human-readable hash of our DSA public key. Return it in the + // passed fingerprint buffer. Return NULL on error, or a pointer to the given + // buffer on success. + otrl_privkey_fingerprint: libotr.declare( + "otrl_privkey_fingerprint", + abi, + ctypes.char.ptr, + OtrlUserState, + fingerprint_t, + ctypes.char.ptr, + ctypes.char.ptr + ), + + // Convert a 20-byte hash value to a 45-byte human-readable value. + otrl_privkey_hash_to_human: libotr.declare( + "otrl_privkey_hash_to_human", + abi, + ctypes.void_t, + fingerprint_t, + hash_t + ), + + // Calculate a raw hash of our DSA public key. Return it in the passed + // fingerprint buffer. Return NULL on error, or a pointer to the given + // buffer on success. + otrl_privkey_fingerprint_raw: libotr.declare( + "otrl_privkey_fingerprint_raw", + abi, + ctypes.unsigned_char.ptr, + OtrlUserState, + hash_t, + ctypes.char.ptr, + ctypes.char.ptr + ), + + // uiOps callbacks + policy_cb_t, + create_privkey_cb_t, + is_logged_in_cb_t, + inject_message_cb_t, + update_context_list_cb_t, + new_fingerprint_cb_t, + write_fingerprint_cb_t, + gone_secure_cb_t, + gone_insecure_cb_t, + still_secure_cb_t, + max_message_size_cb_t, + account_name_cb_t, + account_name_free_cb_t, + received_symkey_cb_t, + otr_error_message_cb_t, + otr_error_message_free_cb_t, + resent_msg_prefix_cb_t, + resent_msg_prefix_free_cb_t, + handle_smp_event_cb_t, + handle_msg_event_cb_t, + create_instag_cb_t, + convert_msg_cb_t, + convert_free_cb_t, + timer_control_cb_t, + + // message.h + + OtrlMessageAppOps, + + errorCode: { + OTRL_ERRCODE_NONE: 0, + OTRL_ERRCODE_ENCRYPTION_ERROR: 1, + OTRL_ERRCODE_MSG_NOT_IN_PRIVATE: 2, + OTRL_ERRCODE_MSG_UNREADABLE: 3, + OTRL_ERRCODE_MSG_MALFORMED: 4, + }, + + smpEvent: { + OTRL_SMPEVENT_NONE: 0, + OTRL_SMPEVENT_ERROR: 1, + OTRL_SMPEVENT_ABORT: 2, + OTRL_SMPEVENT_CHEATED: 3, + OTRL_SMPEVENT_ASK_FOR_ANSWER: 4, + OTRL_SMPEVENT_ASK_FOR_SECRET: 5, + OTRL_SMPEVENT_IN_PROGRESS: 6, + OTRL_SMPEVENT_SUCCESS: 7, + OTRL_SMPEVENT_FAILURE: 8, + }, + + messageEvent: { + OTRL_MSGEVENT_NONE: 0, + OTRL_MSGEVENT_ENCRYPTION_REQUIRED: 1, + OTRL_MSGEVENT_ENCRYPTION_ERROR: 2, + OTRL_MSGEVENT_CONNECTION_ENDED: 3, + OTRL_MSGEVENT_SETUP_ERROR: 4, + OTRL_MSGEVENT_MSG_REFLECTED: 5, + OTRL_MSGEVENT_MSG_RESENT: 6, + OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE: 7, + OTRL_MSGEVENT_RCVDMSG_UNREADABLE: 8, + OTRL_MSGEVENT_RCVDMSG_MALFORMED: 9, + OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD: 10, + OTRL_MSGEVENT_LOG_HEARTBEAT_SENT: 11, + OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR: 12, + OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED: 13, + OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED: 14, + OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE: 15, + }, + + convertType: { + OTRL_CONVERT_SENDING: 0, + OTRL_CONVERT_RECEIVING: 1, + }, + + // Deallocate a message allocated by other otrl_message_* routines. + otrl_message_free: libotr.declare( + "otrl_message_free", + abi, + ctypes.void_t, + ctypes.char.ptr + ), + + // Handle a message about to be sent to the network. + otrl_message_sending: libotr.declare( + "otrl_message_sending", + abi, + gcry_error_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + otrl_instag_t, + ctypes.char.ptr, + OtrlTLV.ptr, + ctypes.char.ptr.ptr, + OtrlFragmentPolicy, + ConnContext.ptr.ptr, + ctypes.void_t.ptr, + ctypes.void_t.ptr + ), + + // Handle a message just received from the network. + otrl_message_receiving: libotr.declare( + "otrl_message_receiving", + abi, + ctypes.int, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr.ptr, + OtrlTLV.ptr.ptr, + ConnContext.ptr.ptr, + ctypes.void_t.ptr, + ctypes.void_t.ptr + ), + + // Put a connection into the PLAINTEXT state, first sending the + // other side a notice that we're doing so if we're currently ENCRYPTED, + // and we think he's logged in. Affects only the specified instance. + otrl_message_disconnect: libotr.declare( + "otrl_message_disconnect", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + otrl_instag_t + ), + + // Call this function every so often, to clean up stale private state that + // may otherwise stick around in memory. + otrl_message_poll: libotr.declare( + "otrl_message_poll", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr + ), + + // Initiate the Socialist Millionaires' Protocol. + otrl_message_initiate_smp: libotr.declare( + "otrl_message_initiate_smp", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.char.ptr, + ctypes.size_t + ), + + // Initiate the Socialist Millionaires' Protocol and send a prompt + // question to the buddy. + otrl_message_initiate_smp_q: libotr.declare( + "otrl_message_initiate_smp_q", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.size_t + ), + + // Respond to a buddy initiating the Socialist Millionaires' Protocol. + otrl_message_respond_smp: libotr.declare( + "otrl_message_respond_smp", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ConnContext.ptr, + ctypes.char.ptr, + ctypes.size_t + ), + + // Abort the SMP. Called when an unexpected SMP message breaks the + // normal flow. + otrl_message_abort_smp: libotr.declare( + "otrl_message_abort_smp", + abi, + ctypes.void_t, + OtrlUserState, + OtrlMessageAppOps.ptr, + ctypes.void_t.ptr, + ConnContext.ptr + ), + + // tlv.h + + tlvs: { + OTRL_TLV_PADDING: new ctypes.unsigned_short(0x0000), + OTRL_TLV_DISCONNECTED: new ctypes.unsigned_short(0x0001), + OTRL_TLV_SMP1: new ctypes.unsigned_short(0x0002), + OTRL_TLV_SMP2: new ctypes.unsigned_short(0x0003), + OTRL_TLV_SMP3: new ctypes.unsigned_short(0x0004), + OTRL_TLV_SMP4: new ctypes.unsigned_short(0x0005), + OTRL_TLV_SMP_ABORT: new ctypes.unsigned_short(0x0006), + OTRL_TLV_SMP1Q: new ctypes.unsigned_short(0x0007), + OTRL_TLV_SYMKEY: new ctypes.unsigned_short(0x0008), + }, + + OtrlTLV, + + // Return the first TLV with the given type in the chain, or NULL if one + // isn't found. + otrl_tlv_find: libotr.declare( + "otrl_tlv_find", + abi, + OtrlTLV.ptr, + OtrlTLV.ptr, + ctypes.unsigned_short + ), + + // Deallocate a chain of TLVs. + otrl_tlv_free: libotr.declare( + "otrl_tlv_free", + abi, + ctypes.void_t, + OtrlTLV.ptr + ), + }; +} + +// exports |