[Pkg-privacy-commits] [libotr] 61/225: 2012-04-30:
Ximin Luo
infinity0 at moszumanska.debian.org
Sat Aug 22 12:44:54 UTC 2015
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository libotr.
commit c87b3fe6531df4abf0add40f43e7a1cb8b70e3f7
Author: Rob Smits <rdfsmits at cs.uwaterloo.ca>
Date: Wed May 2 16:44:14 2012 -0400
2012-04-30:
* AUTHORS:
* README:
* test_suite/
* toolkit/otr_parse.c:
* toolkit/otr_remac.c:
* toolkit/parse.c:
* toolkit/parse.h:
* src/auth.c:
* src/auth.h:
* src/context.c:
* src/context.h:
* src/message.c:
* src/message.h:
* src/privkey.c:
* src/privkey.h:
* src/proto.c:
* src/proto.h:
* src/serial.h:
* src/tests.c:
* src/userstate.c:
* src/userstate.h: More changes for instance tags (Rob Smits).
2009-06-11:
* src/auth.c:
* src/auth.h:
* src/context.c:
* src/context.h:
* src/context_priv.h:
* src/message.c:
* src/message.h:
* src/privkey.c:
* src/privkey.h:
* src/proto.c:
* src/proto.h:
* src/serial.h:
* src/tests.c:
* src/userstate.c:
* src/userstate.h: Core instance tag functionality (Lisa Du).
---
AUTHORS | 2 +-
ChangeLog | 41 ++
README | 66 +-
libotr.pc.in | 2 +-
src/Makefile.am | 4 +-
src/auth.c | 227 +++++--
src/auth.h | 34 +-
src/b64.c | 56 +-
src/b64.h | 2 +-
src/context.c | 234 ++++++-
src/context.h | 69 +-
src/context_priv.c | 3 +-
src/context_priv.h | 7 +-
src/dh.c | 5 +-
src/dh.h | 4 +-
src/instag.c | 258 ++++++++
src/instag.h | 89 +++
src/mem.c | 2 +-
src/mem.h | 2 +-
src/message.c | 864 +++++++++++++++++---------
src/message.h | 81 ++-
src/privkey.c | 28 +-
src/privkey.h | 6 +-
src/proto.c | 427 ++++++++-----
src/proto.h | 45 +-
src/serial.h | 25 +-
src/sm.c | 139 +++--
src/sm.h | 2 +-
src/tests.c | 47 +-
src/tlv.c | 2 +-
src/tlv.h | 4 +-
src/userstate.c | 7 +-
src/userstate.h | 6 +-
src/version.h | 2 +-
test_suite/README | 22 +
test_suite/dummy_im.py | 211 +++++++
test_suite/dummy_im/dummy_client.py | 122 ++++
test_suite/instance_tags0.txt | 3 +
test_suite/instance_tags1.txt | 3 +
test_suite/instance_tags2.txt | 3 +
test_suite/instance_tags3.txt | 3 +
test_suite/instance_tags4.txt | 3 +
test_suite/otr.private_key | 41 ++
test_suite/otr_c_client/Makefile | 8 +
test_suite/otr_c_client/README | 10 +
test_suite/otr_c_client/dummy_client.c | 1071 ++++++++++++++++++++++++++++++++
test_suite/otr_subprocess.py | 482 ++++++++++++++
test_suite/otr_test.py | 250 ++++++++
test_suite/otr_test_general.py | 162 +++++
test_suite/otr_test_mixed.py | 153 +++++
toolkit/aes.c | 482 +++++++-------
toolkit/ctrmode.c | 2 +-
toolkit/ctrmode.h | 2 +-
toolkit/otr_mackey.c | 4 +-
toolkit/otr_modify.c | 6 +-
toolkit/otr_parse.c | 38 +-
toolkit/otr_readforge.c | 8 +-
toolkit/otr_remac.c | 48 +-
toolkit/otr_sesskeys.c | 2 +-
toolkit/otr_toolkit.1 | 11 +-
toolkit/parse.c | 122 +++-
toolkit/parse.h | 24 +-
toolkit/readotr.c | 2 +-
toolkit/readotr.h | 2 +-
toolkit/sesskeys.c | 2 +-
toolkit/sesskeys.h | 2 +-
toolkit/sha1hmac.c | 2 +-
toolkit/sha1hmac.h | 2 +-
68 files changed, 5070 insertions(+), 1030 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index ef73f85..515238c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,7 +2,7 @@ Off-the-Record Messaging Library and Toolkit
Authors:
- Ian Goldberg, Chris Alexander, Willy Lew, Nikita Borisov
+ Ian Goldberg, Rob Smits, Chris Alexander, Willy Lew, Lisa Du, Nikita Borisov
<otr at cypherpunks.ca>
See the README file for mailing list information
diff --git a/ChangeLog b/ChangeLog
index 0e210f2..c1e81dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,44 @@
+2012-04-30:
+
+ * AUTHORS:
+ * README:
+ * toolkit/otr_parse.c:
+ * toolkit/otr_remac.c:
+ * toolkit/parse.c:
+ * toolkit/parse.h:
+ * src/auth.c:
+ * src/auth.h:
+ * src/context.c:
+ * src/context.h:
+ * src/message.c:
+ * src/message.h:
+ * src/privkey.c:
+ * src/privkey.h:
+ * src/proto.c:
+ * src/proto.h:
+ * src/serial.h:
+ * src/tests.c:
+ * src/userstate.c:
+ * src/userstate.h: More changes for instance tags (Rob Smits).
+
+2009-06-11:
+
+ * src/auth.c:
+ * src/auth.h:
+ * src/context.c:
+ * src/context.h:
+ * src/context_priv.h:
+ * src/message.c:
+ * src/message.h:
+ * src/privkey.c:
+ * src/privkey.h:
+ * src/proto.c:
+ * src/proto.h:
+ * src/serial.h:
+ * src/tests.c:
+ * src/userstate.c:
+ * src/userstate.h: Core instance tag functionality (Lisa Du).
+
2009-09-30:
* Protocol-v2.html: Edits from Göran Weinholt
diff --git a/README b/README
index 04e5092..ba112d0 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
Off-the-Record Messaging Library and Toolkit
- v3.2.0, 15 Jun 2008
+ v4.0.0, 2012
This is a library and toolkit which implements Off-the-Record (OTR) Messaging.
@@ -49,6 +49,10 @@ To read stored private keys:
otrl_privkey_read(userstate, privkeyfilename);
+To read stored instance tags:
+
+ otrl_instag_read(userstate, instagfilename);
+
To read stored fingerprints:
otrl_privkey_read_fingerprints(userstate, fingerprintfilename,
@@ -80,8 +84,14 @@ of the UI functions in message.h.
3. Sending messages
When you have a message you're about to send, you'll need to know four
-things: you account name, the protocol id, the name of the recipient, and
-the message.
+things: you account name, the protocol id, the name of the recipient,
+their instance tag, and the message.
+
+OTR protocol version 3 introduces the notion of "instance tags." A
+client may be logged into the same account multiple times from different
+locations. An instance tag is intended to differentiate these clients.
+When sending a message, you may also specify a particular instance tag,
+or use meta instance tags like OTRL_INSTAG_MOST_SECURE.
The protocol id is just a unique string that is used to distinguish
the user foo on AIM from the user foo on MSN, etc. It can be anything
@@ -108,8 +118,9 @@ next function), simply do this:
char *newmessage = NULL;
err = otrl_message_sending(userstate, &ui_ops, opdata, accountname,
- protocolid, recipient_name, message, tlvs, &newmessage,
- add_app_info, add_app_info_data);
+ protocolid, recipient_name, instag, message, tlvs,
+ &newmessage, fragPolicy, contextp, add_app_info,
+ add_app_info_data);
add_app_info and add_app_info_data are as above, and may be NULL.
@@ -117,6 +128,9 @@ tlvs should usually be NULL. If it's not, then it points to a chain of
OtrlTLVs which represent machine-readable data to send along with this
message.
+If contextp is not NULL, it will be set to the context that was used
+for sending the message.
+
If err is non-zero, then the library tried to encrypt the message,
but for some reason failed. DO NOT send the message in the clear in
that case.
@@ -160,10 +174,13 @@ and the message.
ignore_message = otrl_message_receiving(userstate, &ui_ops, opdata,
accountname, protocolid, sender_name, message, &newmessage,
- &tlvs, add_app_info, add_app_info_data);
+ &tlvs, contextp, add_app_info, add_app_info_data);
add_app_info and add_app_info_data are as above, and may be NULL.
+If contextp is not NULL, it will be set to the context that was used
+for receiving the message.
+
If otrl_message_receiving returns 1, then the message you received was
an internal protocol message, and no message should be delivered to the
user.
@@ -235,7 +252,8 @@ Here are the six programs in the toolkit:
you can still forge messages of your choice using the otr_readforge
command, above.
- - otr_remac mackey flags keyid keyid pubkey counter encdata revealed_mackeys
+ - otr_remac mackey sender_instance receiver_instance flags keyid keyid
+ pubkey counter encdata revealed_mackeys
- Make a new OTR Data Message, with the given pieces (note that the
data part is already encrypted). MAC it with the given mackey.
@@ -244,6 +262,30 @@ NOTES
Please send your bug reports, comments, suggestions, patches, etc. to us
at the contact address below.
+In otrl_message_sending, specifying an instance tag allows you to send a
+message to a particular session of a buddy who is logged in multiple times
+with an otr-enabled client. The OTRL_INSTAG_RECENT_RECEIVED meta-instance
+relies on the time that libotr processed the most recent message. Meta-
+instance tags resolve to actual instance tags before a message is sent. An
+instant messaging network may not agree on which session of the remote party is
+the most recent, e.g., due to underlying network race conditions. If the
+behaviour of an instant messaging network is to only deliver to the most recent,
+and libotr and the network disagree on which session is the most recent, the
+other party will not process the given message. That is, the instant messaging
+network will deliver the message to the session whose actual instance tag does
+not match the addressed instance tag. Also note that OTRL_INSTAG_BEST also
+prefers more recent instance tags in the case of multiple instances with the
+same "best" status (most secure). In this case, the most recent has a
+resolution of one second.
+
+If otrl_message_sending is called with an original_msg that contains the text
+"?OTR?", this is a signal to initiate or refresh an OTR session. There is
+currently no way to indicate if this text was actually typed in by a user and
+part of a conversation (e.g., someone communicating instructions on how to
+refresh OTR). In the future, we may allow a policy to specify whether "?OTR?"
+is a signal to start OTR, or just an ordinary message for encrypted and
+unencrypted conversations.
+
MAILING LISTS
There are three mailing lists pertaining to Off-the-Record Messaging:
@@ -267,8 +309,8 @@ The Off-the-Record Messaging library (in the src directory) is
covered by the following (LGPL) license:
Off-the-Record Messaging library
- Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- Nikita Borisov
+ Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ Willy Lew, Lisa Du, Nikita Borisov
<otr at cypherpunks.ca>
This library is free software; you can redistribute it and/or
@@ -289,7 +331,8 @@ The Off-the-Record Messaging Toolkit (in the toolkit directory) is covered
by the following (GPL) license:
Off-the-Record Messaging Toolkit
- Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ Nikita Borisov
<otr at cypherpunks.ca>
This program is free software; you can redistribute it and/or modify
@@ -311,7 +354,8 @@ CONTACT
To report problems, comments, suggestions, patches, etc., you can email
the authors:
-Ian Goldberg, Chris Alexander, and Nikita Borisov <otr at cypherpunks.ca>
+Ian Goldberg, Rob Smits, Chris Alexander, Lisa Du, Nikita Borisov
+<otr at cypherpunks.ca>
For more information on Off-the-Record Messaging, visit
http://otr.cypherpunks.ca/
diff --git a/libotr.pc.in b/libotr.pc.in
index 9538f8e..05b7d43 100644
--- a/libotr.pc.in
+++ b/libotr.pc.in
@@ -5,7 +5,7 @@ includedir=@includedir@
Name: libotr
Description: Off-the-Record Messaging Library
-Version: 3.1.0
+Version: @VERSION@
URL: http://otr.cypherpunks.ca/
Libs: -L${libdir} -lotr
Cflags: -I${includedir}
diff --git a/src/Makefile.am b/src/Makefile.am
index cc4d26f..96e7ce6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,7 +3,7 @@ INCLUDES = @LIBGCRYPT_CFLAGS@
lib_LTLIBRARIES = libotr.la
libotr_la_SOURCES = privkey.c context.c proto.c b64.c dh.c mem.c message.c \
- userstate.c tlv.c auth.c sm.c context_priv.c
+ userstate.c tlv.c auth.c sm.c context_priv.c instag.c
libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@
@@ -11,4 +11,4 @@ otrincdir = $(includedir)/libotr
otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \
version.h userstate.h tlv.h serial.h auth.h sm.h privkey-t.h \
- context_priv.h
+ context_priv.h instag.h
diff --git a/src/auth.c b/src/auth.c
index 36267b5..e3fad2e 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -28,12 +28,15 @@
#include "privkey.h"
#include "auth.h"
#include "serial.h"
+#include "proto.h"
+#include "context.h"
/*
* Initialize the fields of an OtrlAuthInfo (already allocated).
*/
-void otrl_auth_new(OtrlAuthInfo *auth)
+void otrl_auth_new(struct context *context)
{
+ OtrlAuthInfo *auth = &(context->auth);
auth->authstate = OTRL_AUTHSTATE_NONE;
otrl_dh_keypair_init(&(auth->our_dh));
auth->our_keyid = 0;
@@ -55,6 +58,7 @@ void otrl_auth_new(OtrlAuthInfo *auth)
memset(auth->secure_session_id, 0, 20);
auth->secure_session_id_len = 0;
auth->lastauthmsg = NULL;
+ auth->context = context;
}
/*
@@ -95,11 +99,11 @@ void otrl_auth_clear(OtrlAuthInfo *auth)
}
/*
- * Start a fresh AKE (version 2) using the given OtrlAuthInfo. Generate
+ * Start a fresh AKE (version 2 or 3) using the given OtrlAuthInfo. Generate
* a fresh DH keypair to use. If no error is returned, the message to
* transmit will be contained in auth->lastauthmsg.
*/
-gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth)
+gcry_error_t otrl_auth_start_v23(OtrlAuthInfo *auth, int version)
{
gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
@@ -112,6 +116,8 @@ gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth)
/* Clear out this OtrlAuthInfo and start over */
otrl_auth_clear(auth);
auth->initiated = 1;
+ auth->protocol_version = version;
+ auth->context->protocol_version = version;
otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
auth->our_keyid = 1;
@@ -152,15 +158,22 @@ gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth)
enc = NULL;
/* Now serialize the message */
- lenp = 3 + 4 + auth->encgx_len + 4 + 32;
+ lenp = OTRL_HEADER_LEN + (auth->protocol_version == 3 ? 8 : 0) + 4
+ + auth->encgx_len + 4 + 32;
bufp = malloc(lenp);
if (bufp == NULL) goto memerr;
buf = bufp;
buflen = lenp;
- memmove(bufp, "\x00\x02\x02", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
+ /* Header */
+ write_header(auth->protocol_version, '\x02');
+ if (auth->protocol_version == 3) {
+ /* instance tags */
+ write_int(auth->context->our_instance);
+ debug_int("Sender instag", bufp-4);
+ write_int(auth->context->their_instance);
+ debug_int("Recipient instag", bufp-4);
+ }
/* Encrypted g^x */
write_int(auth->encgx_len);
@@ -206,15 +219,21 @@ static gcry_error_t create_key_message(OtrlAuthInfo *auth)
size_t npub;
gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
- buflen = 3 + 4 + npub;
+ buflen = OTRL_HEADER_LEN + (auth->protocol_version == 3 ? 8 : 0) + 4 + npub;
buf = malloc(buflen);
if (buf == NULL) goto memerr;
bufp = buf;
lenp = buflen;
- memmove(bufp, "\x00\x02\x0a", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
+ /* header */
+ write_header(auth->protocol_version, '\x0a');
+ if (auth->protocol_version == 3) {
+ /* instance tags */
+ write_int(auth->context->our_instance);
+ debug_int("Sender instag", bufp-4);
+ write_int(auth->context->their_instance);
+ debug_int("Recipient instag", bufp-4);
+ }
/* g^y */
write_mpi(auth->our_dh.pub, npub, "g^y");
@@ -239,7 +258,7 @@ memerr:
* keypair to use.
*/
gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
- const char *commitmsg)
+ const char *commitmsg, int version)
{
gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
unsigned char *buf = NULL, *bufp = NULL, *encbuf = NULL;
@@ -255,9 +274,14 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
lenp = buflen;
/* Header */
- require_len(3);
- if (memcmp(bufp, "\x00\x02\x02", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ auth->protocol_version = version;
+ auth->context->protocol_version = version;
+ skip_header('\x02');
+
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
/* Encrypted g^x */
read_int(enclen);
@@ -282,10 +306,12 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
case OTRL_AUTHSTATE_NONE:
case OTRL_AUTHSTATE_AWAITING_SIG:
case OTRL_AUTHSTATE_V1_SETUP:
-
/* Store the incoming information */
otrl_auth_clear(auth);
+ auth->protocol_version = version;
+
otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+
auth->our_keyid = 1;
auth->encgx = encbuf;
encbuf = NULL;
@@ -296,7 +322,6 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
err = create_key_message(auth);
if (err) goto err;
auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
-
break;
case OTRL_AUTHSTATE_AWAITING_DHKEY:
@@ -310,6 +335,7 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
} else {
/* Ours loses. Use the incoming parameters instead. */
otrl_auth_clear(auth);
+ auth->protocol_version = version;
otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
auth->our_keyid = 1;
auth->encgx = encbuf;
@@ -373,7 +399,7 @@ static gcry_error_t calculate_pubkey_auth(unsigned char **authbufp,
/* How big is the total structure to be MAC'd? */
totallen = 4 + ourpublen + 4 + theirpublen + 2 + privkey->pubkey_datalen
- + 4;
+ + 4;
buf = malloc(totallen);
if (buf == NULL) goto memerr;
@@ -582,16 +608,23 @@ static gcry_error_t create_revealsig_message(OtrlAuthInfo *auth,
auth->our_dh.pub, auth->their_pub, privkey, auth->our_keyid);
if (err) goto err;
- buflen = 3 + 4 + 16 + 4 + authlen + 20;
+ buflen = OTRL_HEADER_LEN + (auth->protocol_version == 3 ? 8 : 0) + 4 + 16
+ + 4 + authlen + 20;
buf = malloc(buflen);
if (buf == NULL) goto memerr;
bufp = buf;
lenp = buflen;
- memmove(bufp, "\x00\x02\x11", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
+ /* header */
+ write_header(auth->protocol_version, '\x11');
+ if (auth->protocol_version == 3) {
+ /* instance tags */
+ write_int(auth->context->our_instance);
+ debug_int("Sender instag", bufp-4);
+ write_int(auth->context->their_instance);
+ debug_int("Recipient instag", bufp-4);
+ }
/* r */
write_int(16);
@@ -654,16 +687,23 @@ static gcry_error_t create_signature_message(OtrlAuthInfo *auth,
auth->our_keyid);
if (err) goto err;
- buflen = 3 + 4 + authlen + 20;
+ buflen = OTRL_HEADER_LEN + (auth->protocol_version == 3 ? 8 : 0) + 4
+ + authlen + 20;
buf = malloc(buflen);
if (buf == NULL) goto memerr;
bufp = buf;
lenp = buflen;
- memmove(bufp, "\x00\x02\x12", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
+ /* header */
+ write_header(auth->protocol_version, '\x12');
+ if (auth->protocol_version == 3) {
+ /* instance tags */
+ write_int(auth->context->our_instance);
+ debug_int("Sender instag", bufp-4);
+ write_int(auth->context->their_instance);
+ debug_int("Recipient instag", bufp-4);
+ }
/* Encrypted authenticator */
startmac = bufp;
@@ -711,10 +751,12 @@ gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
unsigned char *buf = NULL, *bufp = NULL;
size_t buflen, lenp;
gcry_mpi_t incoming_pub = NULL;
- int res;
+ int res, msg_version;
*havemsgp = 0;
+ msg_version = otrl_proto_message_version(keymsg);
+
res = otrl_base64_otr_decode(keymsg, &buf, &buflen);
if (res == -1) goto memerr;
if (res == -2) goto invval;
@@ -723,8 +765,12 @@ gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
lenp = buflen;
/* Header */
- if (memcmp(bufp, "\x00\x02\x0a", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ skip_header('\x0a');
+
+ if (msg_version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
/* g^y */
read_mpi(incoming_pub);
@@ -735,6 +781,13 @@ gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
switch(auth->authstate) {
case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ /* The other party may also be establishing a session with
+ another instance running a different version. Ignore any
+ DHKEY messages we aren't expecting. */
+ if (msg_version != auth->protocol_version) {
+ goto err;
+ }
+
/* Store the incoming public key */
gcry_mpi_release(auth->their_pub);
auth->their_pub = incoming_pub;
@@ -808,6 +861,7 @@ gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
gcry_mpi_t incoming_pub = NULL;
unsigned char ctr[16], hashbuf[32];
int res;
+ unsigned char version;
*havemsgp = 0;
@@ -818,9 +872,16 @@ gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
bufp = buf;
lenp = buflen;
+ require_len(3);
+ version = bufp[1];
+
/* Header */
- if (memcmp(bufp, "\x00\x02\x11", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ skip_header('\x11');
+
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
/* r */
read_int(rlen);
@@ -898,6 +959,7 @@ gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
/* Check the MAC */
gcry_md_reset(auth->mac_m2);
gcry_md_write(auth->mac_m2, authstart, authend - authstart);
+
if (memcmp(macstart,
gcry_md_read(auth->mac_m2, GCRY_MD_SHA256),
20)) goto invval;
@@ -921,7 +983,6 @@ gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
/* No error? Then we've completed our end of the
* authentication. */
- auth->protocol_version = 2;
auth->session_id_half = OTRL_SESSIONID_SECOND_HALF_BOLD;
if (auth_succeeded) err = auth_succeeded(auth, asdata);
*havemsgp = 1;
@@ -974,6 +1035,7 @@ gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
unsigned char *authstart, *authend, *macstart;
size_t buflen, lenp, authlen;
int res;
+ unsigned char version;
*havemsgp = 0;
@@ -984,9 +1046,16 @@ gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
bufp = buf;
lenp = buflen;
+ require_len(3);
+ version = bufp[1];
+
/* Header */
- if (memcmp(bufp, "\x00\x02\x12", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ skip_header('\x12');
+
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
/* auth */
authstart = bufp;
@@ -1026,7 +1095,6 @@ gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
/* No error? Then we've completed our end of the
* authentication. */
- auth->protocol_version = 2;
auth->session_id_half = OTRL_SESSIONID_FIRST_HALF_BOLD;
if (auth_succeeded) err = auth_succeeded(auth, asdata);
free(auth->lastauthmsg);
@@ -1153,6 +1221,7 @@ gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh,
/* Clear out this OtrlAuthInfo and start over */
otrl_auth_clear(auth);
auth->initiated = 1;
+ auth->protocol_version = 1;
/* Import the given DH keypair, or else create a fresh one */
if (our_dh) {
@@ -1248,7 +1317,7 @@ gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
pubs = NULL;
free(buf);
buf = NULL;
-
+
if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP && received_reply == 0x01) {
/* They're replying to something we never sent. We must be
* logged in more than once; ignore the message. */
@@ -1314,17 +1383,87 @@ err:
return err;
}
+/*
+ * Copy relevant information from the master OtrlAuthInfo to an
+ * instance OtrlAuthInfo in response to a D-H Commit with a new
+ * instance. The fields copied will depend on the state of the
+ * master auth.
+ */
+gcry_error_t otrl_auth_copy_on_commit(OtrlAuthInfo *m_auth,
+ OtrlAuthInfo *auth)
+{
+ switch(m_auth->authstate) {
+ case OTRL_AUTHSTATE_NONE:
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+ break;
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ /* We sent a D-H Commit Message, and we also received one.
+ * Copy our D_H Commit and auth state */
+ otrl_dh_keypair_free(&(auth->our_dh));
+ auth->initiated = m_auth->initiated;
+ otrl_dh_keypair_copy(&(auth->our_dh), &(m_auth->our_dh));
+ auth->our_keyid = m_auth->our_keyid;
+ memmove(auth->r, m_auth->r, 16);
+ if (auth->encgx) free(auth->encgx);
+ auth->encgx = malloc(m_auth->encgx_len);
+ memmove(auth->encgx, m_auth->encgx, m_auth->encgx_len);
+ memmove(auth->hashgx, m_auth->hashgx, 32);
+
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
+ break;
+
+ default:
+ /* This bad state will be detected and handled later */
+ break;
+ }
+}
+
+/*
+ * Copy relevant information from the master OtrlAuthInfo to an
+ * instance OtrlAuthInfo in response to a D-H Key with a new
+ * instance. The fields copied will depend on the state of the
+ * master auth.
+ */
+gcry_error_t otrl_auth_copy_on_key(OtrlAuthInfo *m_auth,
+ OtrlAuthInfo *auth)
+{
+ switch(m_auth->authstate) {
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ /* Copy our D-H Commit information to the new instance */
+ otrl_dh_keypair_free(&(auth->our_dh));
+ auth->initiated = m_auth->initiated;
+ otrl_dh_keypair_copy(&(auth->our_dh), &(m_auth->our_dh));
+ auth->our_keyid = m_auth->our_keyid;
+ memmove(auth->r, m_auth->r, 16);
+ if (auth->encgx) free(auth->encgx);
+ auth->encgx = malloc(m_auth->encgx_len);
+ memmove(auth->encgx, m_auth->encgx, m_auth->encgx_len);
+ memmove(auth->hashgx, m_auth->hashgx, 32);
+
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
+ break;
+
+ default:
+ /* This bad state will be detected and handled later */
+ break;
+ }
+}
+
#ifdef OTRL_TESTING_AUTH
#include "mem.h"
#include "privkey.h"
-#define CHECK_ERR if (err) { printf("Error: %s\n", gcry_strerror(err)); return 1; }
+#define CHECK_ERR if (err) { printf("Error: %s\n", gcry_strerror(err)); \
+ return 1; }
static gcry_error_t starting(const OtrlAuthInfo *auth, void *asdata)
{
char *name = asdata;
- fprintf(stderr, "\nStarting ENCRYPTED mode for %s (v%d).\n", name, auth->protocol_version);
+ fprintf(stderr, "\nStarting ENCRYPTED mode for %s (v%d).\n",
+ name, auth->protocol_version);
fprintf(stderr, "\nour_dh (%d):", auth->our_keyid);
gcry_mpi_dump(auth->our_dh.pub);
@@ -1359,7 +1498,7 @@ int main(int argc, char **argv)
printf("\n\n ***** V2 *****\n\n");
- err = otrl_auth_start_v2(&bob, NULL, 0);
+ err = otrl_auth_start_v23(&bob, NULL, 0);
CHECK_ERR
printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
err = otrl_auth_handle_commit(&alice, bob.lastauthmsg, NULL, 0);
@@ -1376,7 +1515,8 @@ int main(int argc, char **argv)
alicepriv, starting, "Alice");
CHECK_ERR
if (havemsg) {
- printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg),
+ alice.lastauthmsg);
} else {
printf("\nIGNORE\n\n");
}
@@ -1393,7 +1533,8 @@ int main(int argc, char **argv)
&havemsg, alicepriv, NULL, 0, starting, "Alice");
CHECK_ERR
if (havemsg) {
- printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg),
+ alice.lastauthmsg);
} else {
printf("\nIGNORE\n\n");
}
diff --git a/src/auth.h b/src/auth.h
index 4ca5aa3..ee69ade 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -24,6 +24,7 @@
#include <gcrypt.h>
#include "dh.h"
+
typedef enum {
OTRL_AUTHSTATE_NONE,
OTRL_AUTHSTATE_AWAITING_DHKEY,
@@ -35,6 +36,8 @@ typedef enum {
typedef struct {
OtrlAuthState authstate; /* Our state */
+ struct context *context; /* The context which points to us */
+
DH_keypair our_dh; /* Our D-H key */
unsigned int our_keyid; /* ...and its keyid */
@@ -47,6 +50,7 @@ typedef struct {
gcry_mpi_t their_pub; /* Their D-H public key */
unsigned int their_keyid; /* ...and its keyid */
+
gcry_cipher_hd_t enc_c, enc_cp; /* c and c' encryption keys */
gcry_md_hd_t mac_m1, mac_m1p; /* m1 and m1' MAC keys */
gcry_md_hd_t mac_m2, mac_m2p; /* m2 and m2' MAC keys */
@@ -78,7 +82,7 @@ typedef struct {
/*
* Initialize the fields of an OtrlAuthInfo (already allocated).
*/
-void otrl_auth_new(OtrlAuthInfo *auth);
+void otrl_auth_new(struct context *context);
/*
* Clear the fields of an OtrlAuthInfo (but leave it allocated).
@@ -86,11 +90,11 @@ void otrl_auth_new(OtrlAuthInfo *auth);
void otrl_auth_clear(OtrlAuthInfo *auth);
/*
- * Start a fresh AKE (version 2) using the given OtrlAuthInfo. Generate
+ * Start a fresh AKE (version 2 or 3) using the given OtrlAuthInfo. Generate
* a fresh DH keypair to use. If no error is returned, the message to
* transmit will be contained in auth->lastauthmsg.
*/
-gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth);
+gcry_error_t otrl_auth_start_v23(OtrlAuthInfo *auth, int version);
/*
* Handle an incoming D-H Commit Message. If no error is returned, the
@@ -98,7 +102,7 @@ gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth);
* keypair to use.
*/
gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
- const char *commitmsg);
+ const char *commitmsg, int version);
/*
* Handle an incoming D-H Key Message. If no error is returned, and
@@ -155,4 +159,22 @@ gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata);
+/*
+ * Copy relevant information from the master OtrlAuthInfo to an
+ * instance OtrlAuthInfo in response to a D-H Commit with a new
+ * instance. The fields copied will depend on the state of the
+ * master auth.
+ */
+gcry_error_t otrl_auth_copy_on_commit(OtrlAuthInfo *m_auth,
+ OtrlAuthInfo *auth);
+
+/*
+ * Copy relevant information from the master OtrlAuthInfo to an
+ * instance OtrlAuthInfo in response to a D-H Key with a new
+ * instance. The fields copied will depend on the state of the
+ * master auth.
+ */
+gcry_error_t otrl_auth_copy_on_key(OtrlAuthInfo *m_auth,
+ OtrlAuthInfo *auth);
+
#endif
diff --git a/src/b64.c b/src/b64.c
index bfe163a..cc6e7cb 100644
--- a/src/b64.c
+++ b/src/b64.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -28,30 +28,30 @@ AUTHOR: Bob Trower 08/04/01
LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the
- Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall
- be included in all copies or substantial portions of the
- Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
- KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
- OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated
+ documentation files (the "Software"), to deal in the
+ Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute,
+ sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall
+ be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
VERSION HISTORY:
- Bob Trower 08/04/01 -- Create Version 0.00.00B
+ Bob Trower 08/04/01 -- Create Version 0.00.00B
\******************************************************************* */
@@ -88,9 +88,9 @@ static void encodeblock( char *out, const unsigned char *in, size_t len )
out[0] = cb64[ in0 >> 2 ];
out[1] = cb64[ ((in0 & 0x03) << 4) | ((in1 & 0xf0) >> 4) ];
out[2] = len > 1 ? cb64[ ((in1 & 0x0f) << 2) | ((in2 & 0xc0) >> 6) ]
- : '=';
+ : '=';
out[3] = len > 2 ? cb64[ in2 & 0x3f ]
- : '=';
+ : '=';
}
/*
@@ -120,7 +120,7 @@ size_t otrl_base64_encode(char *base64data, const unsigned char *data,
}
static size_t decode(unsigned char *out, const char *in, size_t b64len)
-{
+{
size_t written = 0;
unsigned char c = 0;
@@ -228,9 +228,10 @@ int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
if (!otrtag) {
return -2;
}
+
endtag = strchr(otrtag, '.');
if (endtag) {
- msglen = endtag-otrtag;
+ msglen = endtag-otrtag;
} else {
return -2;
}
@@ -241,6 +242,7 @@ int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
if (!rawmsg && rawlen > 0) {
return -1;
}
+
rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
*bufp = rawmsg;
diff --git a/src/b64.h b/src/b64.h
index 9a4e636..96f4f28 100644
--- a/src/b64.h
+++ b/src/b64.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/src/context.c b/src/context.c
index e584769..1a166fb 100644
--- a/src/context.c
+++ b/src/context.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -27,6 +27,10 @@
/* libotr headers */
#include "context.h"
+#include "instag.h"
+
+static void set_instance_fingerprint_next(OtrlUserState us,
+ ConnContext *context);
/* Create a new connection context. */
static ConnContext * new_context(const char * user, const char * accountname,
@@ -34,19 +38,24 @@ static ConnContext * new_context(const char * user, const char * accountname,
{
ConnContext * context;
OtrlSMState *smstate;
- context = malloc(sizeof(*context));
+
+ context = malloc(sizeof(ConnContext));
assert(context != NULL);
+
context->username = strdup(user);
context->accountname = strdup(accountname);
context->protocol = strdup(protocol);
+
context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
- otrl_auth_new(&(context->auth));
+ otrl_auth_new(context);
smstate = malloc(sizeof(OtrlSMState));
assert(smstate != NULL);
otrl_sm_state_new(smstate);
context->smstate = smstate;
+ context->our_instance = 0;
+ context->their_instance = OTRL_INSTAG_MASTER;
context->fingerprint_root.fingerprint = NULL;
context->fingerprint_root.context = context;
context->fingerprint_root.next = NULL;
@@ -61,39 +70,147 @@ static ConnContext * new_context(const char * user, const char * accountname,
context->context_priv = context_priv_new();
assert(context->context_priv != NULL);
context->next = NULL;
+ context->m_context = context;
+ context->recent_rcvd_child = NULL;
+ context->recent_sent_child = NULL;
+ context->recent_child = NULL;
+
return context;
}
-/* Look up a connection context by name/account/protocol from the given
+ConnContext * otrl_context_find_recent_instance(ConnContext * context,
+ otrl_instag_t recent_instag) {
+ ConnContext * m_context;
+
+ if (!context) return NULL;
+
+ m_context = context->m_context;
+
+ if (!m_context) return NULL;
+
+ switch(recent_instag) {
+ case OTRL_INSTAG_RECENT:
+ return m_context->recent_child;
+ case OTRL_INSTAG_RECENT_RECEIVED:
+ return m_context->recent_rcvd_child;
+ case OTRL_INSTAG_RECENT_SENT:
+ return m_context->recent_sent_child;
+ default:
+ return NULL;
+ }
+}
+
+/* Find the instance of this context that has the best security level,
+ and for which we have most recently received a message from. Note that most
+ recent in this case is limited to a one-second resolution. */
+ConnContext * otrl_context_find_recent_secure_instance(ConnContext * context)
+{
+ ConnContext *curp; /* for iteration */
+ ConnContext *m_context; /* master */
+ ConnContext *cresult = context; /* best so far */
+
+ if (!context) {
+ return cresult;
+ }
+
+ m_context = context->m_context;
+
+ for (curp = m_context; curp && curp->m_context == m_context;
+ curp = curp->next) {
+ int msgstate_improved = 0; /* 0 == same, 1 == improved */
+ int trust_improved = 0; /* (will immediately 'continue' if worse
+ * than) */
+
+ if (cresult->msgstate == curp->msgstate) {
+ msgstate_improved = 0;
+ } else if (curp->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ msgstate_improved = 1;
+ } else {
+ continue;
+ }
+
+
+ if (otrl_context_is_fingerprint_trusted(cresult->active_fingerprint) ==
+ otrl_context_is_fingerprint_trusted(curp->active_fingerprint)) {
+
+ trust_improved = 0;
+ } else if
+ (otrl_context_is_fingerprint_trusted(curp->active_fingerprint)){
+
+ trust_improved = 1;
+ } else {
+ continue;
+ }
+
+ if (msgstate_improved || trust_improved ||
+ (!msgstate_improved && !trust_improved &&
+ curp->context_priv->lastrecv >=
+ cresult->context_priv->lastrecv)) {
+ cresult = curp;
+ }
+ }
+
+ return cresult;
+}
+
+/* Look up a connection context by name/account/protocol/instag from the given
* OtrlUserState. If add_if_missing is true, allocate and return a new
* context if one does not currently exist. In that event, call
* add_app_data(data, context) so that app_data and app_data_free can be
- * filled in by the application, and set *addedp to 1. */
+ * filled in by the application, and set *addedp to 1.
+ * In the 'their_instance' field note that you can also specify a 'meta-
+ * instance' value such as OTRL_INSTAG_MASTER, OTRL_INSTAL_RECENT,
+ * OTRL_INSTAG_RECENT_RECEIVED and OTRL_INSTAG_RECENT_SENT. */
ConnContext * otrl_context_find(OtrlUserState us, const char *user,
- const char *accountname, const char *protocol, int add_if_missing,
- int *addedp,
+ const char *accountname, const char *protocol,
+ otrl_instag_t their_instance, int add_if_missing, int *addedp,
void (*add_app_data)(void *data, ConnContext *context), void *data)
{
ConnContext ** curp;
int usercmp = 1, acctcmp = 1, protocmp = 1;
if (addedp) *addedp = 0;
if (!user || !accountname || !protocol) return NULL;
+
for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) {
- if ((usercmp = strcmp((*curp)->username, user)) > 0 ||
+ if ((usercmp = strcmp((*curp)->username, user)) > 0 ||
(usercmp == 0 &&
- (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) ||
+ (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) ||
(usercmp == 0 && acctcmp == 0 &&
- (protocmp = strcmp((*curp)->protocol, protocol)) >= 0))
+ (protocmp = strcmp((*curp)->protocol, protocol)) >= 0)
+ && (their_instance < OTRL_MIN_VALID_INSTAG ||
+ (their_instance == (*curp)->their_instance)))
/* We're at the right place in the list. We've either found
* it, or gone too far. */
break;
}
- if (usercmp == 0 && acctcmp == 0 && protocmp == 0) {
- /* Found it! */
- return *curp;
+
+ if (usercmp == 0 && acctcmp == 0 && protocmp == 0 && *curp &&
+ (their_instance < OTRL_MIN_VALID_INSTAG ||
+ (their_instance == (*curp)->their_instance))) {
+ /* Found one! */
+ if (their_instance >= OTRL_MIN_VALID_INSTAG ||
+ their_instance == OTRL_INSTAG_MASTER) {
+ return *curp;
+ }
+
+ /* We need to go back and check more values in the context */
+ switch(their_instance) {
+ case OTRL_INSTAG_BEST:
+ return otrl_context_find_recent_secure_instance(*curp);
+ case OTRL_INSTAG_RECENT:
+ case OTRL_INSTAG_RECENT_RECEIVED:
+ case OTRL_INSTAG_RECENT_SENT:
+ return otrl_context_find_recent_instance(*curp, their_instance);
+ default:
+ return NULL;
+ }
}
+
if (add_if_missing) {
ConnContext *newctx;
+ OtrlInsTag *our_instag = (OtrlInsTag *)otrl_instag_find(us, accountname,
+ protocol);
+
if (addedp) *addedp = 1;
newctx = new_context(user, accountname, protocol);
newctx->next = *curp;
@@ -105,22 +222,71 @@ ConnContext * otrl_context_find(OtrlUserState us, const char *user,
if (add_app_data) {
add_app_data(data, *curp);
}
+
+ /* Initialize specified instance tags */
+ if (our_instag) {
+ newctx->our_instance = our_instag->instag;
+ }
+
+ if (their_instance >= OTRL_MIN_VALID_INSTAG ||
+ their_instance == OTRL_INSTAG_MASTER) {
+ newctx->their_instance = their_instance;
+ }
+
+ if (their_instance >= OTRL_MIN_VALID_INSTAG) {
+ newctx->m_context = otrl_context_find(us, user, accountname,
+ protocol, OTRL_INSTAG_MASTER, 1, NULL, add_app_data, data);
+ }
+
+ if (their_instance == OTRL_INSTAG_MASTER) {
+ /* if we're adding a master, there are no children, so the most
+ * recent context is the one we add. */
+ newctx->recent_child = newctx;
+ newctx->recent_rcvd_child = newctx;
+ newctx->recent_sent_child = newctx;
+ }
+
return *curp;
}
return NULL;
}
+int otrl_context_is_fingerprint_trusted(Fingerprint *fprint) {
+ return fprint && fprint->trust && fprint->trust[0] != '\0';
+}
+
+void otrl_context_update_recent_child(ConnContext *context,
+ unsigned int sent_msg) {
+ ConnContext *m_context = context->m_context;
+
+ if (sent_msg) {
+ m_context->recent_sent_child = context;
+ } else {
+ m_context->recent_rcvd_child = context;
+ }
+
+ m_context->recent_child = context;
+
+}
+
/* Find a fingerprint in a given context, perhaps adding it if not
* present. */
Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
unsigned char fingerprint[20], int add_if_missing, int *addedp)
{
- Fingerprint *f = context->fingerprint_root.next;
+ Fingerprint *f;
if (addedp) *addedp = 0;
+
+ if (!context || !context->m_context) return NULL;
+
+ context = context->m_context;
+
+ f = context->fingerprint_root.next;
while(f) {
if (!memcmp(f->fingerprint, fingerprint, 20)) return f;
f = f->next;
}
+
/* Didn't find it. */
if (add_if_missing) {
if (addedp) *addedp = 1;
@@ -191,6 +357,7 @@ void otrl_context_forget_fingerprint(Fingerprint *fprint,
} else {
if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
context->active_fingerprint != fprint) {
+
free(fprint->fingerprint);
free(fprint->trust);
*(fprint->tous) = fprint->next;
@@ -209,10 +376,33 @@ void otrl_context_forget_fingerprint(Fingerprint *fprint,
}
}
-/* Forget a whole context, so long as it's PLAINTEXT. */
-void otrl_context_forget(ConnContext *context)
+/* Forget a whole context, so long as it's PLAINTEXT. If a context has child
+ * instances, don't remove this instance unless children are also all in
+ * PLAINTEXT state. In this case, the children will also be removed.
+ * Returns 0 on success, 1 on failure. */
+int otrl_context_forget(ConnContext *context)
{
- if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return;
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return 1;
+
+ if (context->their_instance == OTRL_INSTAG_MASTER) {
+ ConnContext *c_iter;
+
+ for (c_iter = context; c_iter &&
+ c_iter->m_context == context->m_context;
+ c_iter = c_iter->next) {
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return 1;
+ }
+
+ c_iter = context->next;
+ while (c_iter && c_iter->m_context == context->m_context) {
+ if (!otrl_context_forget(c_iter)) {
+ c_iter = context->next;
+ } else {
+ return 1;
+ }
+ }
+
+ }
/* Just to be safe, force to plaintext. This also frees any
* extraneous data lying around. */
@@ -245,13 +435,19 @@ void otrl_context_forget(ConnContext *context)
}
free(context);
+ return 0;
}
/* Forget all the contexts in a given OtrlUserState. */
void otrl_context_forget_all(OtrlUserState us)
{
+ ConnContext *c_iter;
+
+ for (c_iter = us->context_root; c_iter; c_iter = c_iter->next) {
+ otrl_context_force_plaintext(c_iter);
+ }
+
while (us->context_root) {
- otrl_context_force_plaintext(us->context_root);
otrl_context_forget(us->context_root);
}
}
diff --git a/src/context.h b/src/context.h
index 1ae658a..d7595a9 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -29,6 +29,10 @@
#include "auth.h"
#include "sm.h"
+typedef struct context ConnContext; /* Forward declare */
+
+#include "instag.h"
+
typedef enum {
OTRL_MSGSTATE_PLAINTEXT, /* Not yet started an encrypted
conversation */
@@ -50,7 +54,7 @@ typedef struct s_fingerprint {
char *trust; /* The trust level of the fingerprint */
} Fingerprint;
-typedef struct context {
+struct context {
struct context * next; /* Linked list pointer */
struct context ** tous; /* A pointer to the pointer to us */
@@ -65,15 +69,39 @@ typedef struct context {
this account... */
char * protocol; /* ... and this protocol */
+ struct context *m_context; /* If this is a child context, this
+ field will point to the master
+ context. Otherwise it will point to
+ itself. */
+ struct context *recent_rcvd_child; /* If this is a master context, this
+ points to the child context that
+ has received a message most recently.
+ By default, it will point to the
+ master context. In child contexts
+ this field is NULL. */
+ struct context *recent_sent_child; /* Similar to above, but it points to
+ the child who has sent most
+ recently. */
+ struct context *recent_child; /* Similar to above, but will point to
+ the most recent of recent_rcvd_child
+ and recent_sent_child */
+
+ otrl_instag_t our_instance; /* Our instance tag for this computer*/
+ otrl_instag_t their_instance; /* The user's instance tag */
+
OtrlMessageState msgstate; /* The state of message disposition
with this user */
OtrlAuthInfo auth; /* The state of ongoing
authentication with this user */
Fingerprint fingerprint_root; /* The root of a linked list of
- Fingerprints entries */
+ Fingerprints entries. This list will
+ only be populated in master contexts.
+ For child contexts,
+ fingerprint_root.next will always
+ point to NULL. */
Fingerprint *active_fingerprint; /* Which fingerprint is in use now?
- A pointer into the above list */
+ A pointer into the above list */
unsigned char sessionid[20]; /* The sessionid and bold half */
size_t sessionid_len; /* determined when this private */
@@ -95,21 +123,29 @@ typedef struct context {
void (*app_data_free)(void *);
OtrlSMState *smstate; /* The state of the current
- socialist millionaires exchange */
-} ConnContext;
+ socialist millionaires exchange */
+};
#include "userstate.h"
-/* Look up a connection context by name/account/protocol from the given
- * OtrlUserState. If add_if_missing is true, allocate and return a new
- * context if one does not currently exist. In that event, call
+/* Look up a connection context by name/account/protocol/instance from the
+ * given OtrlUserState. If add_if_missing is true, allocate and return a
+ * new context if one does not currently exist. In that event, call
* add_app_data(data, context) so that app_data and app_data_free can be
- * filled in by the application, and set *addedp to 1. */
+ * filled in by the application, and set *addedp to 1.
+ * In the 'their_instance' field note that you can also specify a 'meta-
+ * instance' value such as OTRL_INSTAG_MASTER, OTRL_INSTAL_RECENT,
+ * OTRL_INSTAG_RECENT_RECEIVED and OTRL_INSTAG_RECENT_SENT. */
ConnContext * otrl_context_find(OtrlUserState us, const char *user,
- const char *accountname, const char *protocol, int add_if_missing,
- int *addedp,
+ const char *accountname, const char *protocol,
+ otrl_instag_t their_instance, int add_if_missing, int *addedp,
void (*add_app_data)(void *data, ConnContext *context), void *data);
+/* This method gets called after sending or receiving a message, to update the
+ * master context's "recent context" pointers. */
+void otrl_context_update_recent_child(ConnContext *context,
+ unsigned int sent_msg);
+
/* Find a fingerprint in a given context, perhaps adding it if not
* present. */
Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
@@ -132,8 +168,11 @@ void otrl_context_force_plaintext(ConnContext *context);
void otrl_context_forget_fingerprint(Fingerprint *fprint,
int and_maybe_context);
-/* Forget a whole context, so long as it's PLAINTEXT. */
-void otrl_context_forget(ConnContext *context);
+/* Forget a whole context, so long as it's PLAINTEXT. If a context has child
+ * instances, don't remove this instance unless children are also all in
+ * PLAINTEXT state. In this case, the children will also be removed.
+ * Returns 0 on success, 1 on failure. */
+int otrl_context_forget(ConnContext *context);
/* Forget all the contexts in a given OtrlUserState. */
void otrl_context_forget_all(OtrlUserState us);
diff --git a/src/context_priv.c b/src/context_priv.c
index 62ea68b..f93136e 100644
--- a/src/context_priv.c
+++ b/src/context_priv.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -44,6 +44,7 @@ ConnContextPriv *context_priv_new()
context_priv->generation = 0;
context_priv->lastsent = 0;
context_priv->lastmessage = NULL;
+ context_priv->lastrecv = 0;
context_priv->may_retransmit = 0;
context_priv->their_keyid = 0;
context_priv->their_y = NULL;
diff --git a/src/context_priv.h b/src/context_priv.h
index b05b5ff..1bc7e6b 100644
--- a/src/context_priv.h
+++ b/src/context_priv.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -74,6 +74,9 @@ typedef struct context_priv {
/* The last time a Data Message was sent */
time_t lastsent;
+ /* The last time a Data Message was received */
+ time_t lastrecv;
+
/* The plaintext of the last Data Message sent */
char *lastmessage;
diff --git a/src/dh.c b/src/dh.c
index a28d376..b2caac8 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -27,6 +27,7 @@
/* libotr headers */
#include "dh.h"
+
static const char* DH1536_MODULUS_S = "0x"
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
@@ -91,7 +92,7 @@ void otrl_dh_keypair_free(DH_keypair *kp)
/*
* Generate a DH keypair for a specified group.
- */
+ */
gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp)
{
unsigned char *secbuf = NULL;
diff --git a/src/dh.h b/src/dh.h
index 788b20f..eedaf06 100644
--- a/src/dh.h
+++ b/src/dh.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -74,7 +74,7 @@ void otrl_dh_keypair_free(DH_keypair *kp);
/*
* Generate a DH keypair for a specified group.
- */
+ */
gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp);
/*
diff --git a/src/instag.c b/src/instag.c
new file mode 100644
index 0000000..a17f774
--- /dev/null
+++ b/src/instag.c
@@ -0,0 +1,258 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
+ * <otr at cypherpunks.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "instag.h"
+#include "userstate.h"
+
+/* Forget the given instag. */
+void otrl_instag_forget(OtrlInsTag* instag) {
+ if (!instag) return;
+
+ if (instag->accountname) free(instag->accountname);
+ if (instag->protocol) free(instag->protocol);
+
+ /* Re-link the list */
+ *(instag->tous) = instag->next;
+ if (instag->next) {
+ instag->next->tous = instag->tous;
+ }
+
+ free(instag);
+}
+
+/* Forget all instags in a given OtrlUserState. */
+void otrl_instag_forget_all(OtrlUserState us) {
+ while(us->instag_root) {
+ otrl_instag_forget(us->instag_root);
+ }
+}
+
+/* Fetch the instance tag from the given OtrlUserState associated with
+ * the given account */
+OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname,
+ const char *protocol)
+{
+ OtrlInsTag *p;
+
+ for(p=us->instag_root; p; p=p->next) {
+ if (!strcmp(p->accountname, accountname) &&
+ !strcmp(p->protocol, protocol)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/* Read our instance tag from a file on disk into the given
+ * OtrlUserState. */
+gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename)
+{
+ gcry_error_t err;
+ FILE *instf;
+
+ /* Open the instance tag file. */
+ instf = fopen(filename, "rb");
+ if (!instf) {
+ return gcry_error_from_errno(errno);
+ }
+
+ err = otrl_instag_read_FILEp(us, instf);
+ fclose(instf);
+ return err;
+}
+
+/* Read our instance tag from a file on disk into the given
+ * OtrlUserState. The FILE* must be open for reading. */
+gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf)
+{
+ if (!instf) return gcry_error(GPG_ERR_NO_ERROR);
+
+ OtrlInsTag *p;
+ char storeline[1000];
+ size_t maxsize = sizeof(storeline);
+
+ while(fgets(storeline, maxsize, instf)) {
+ char *prevpos;
+ char *pos;
+ unsigned int instag = 0;
+ int i;
+
+ p = malloc(sizeof(*p));
+ if (!p) {
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /* Parse the line, which should be of the form:
+ * accountname\tprotocol\t40_hex_nybbles\n */
+ prevpos = storeline;
+ pos = strchr(prevpos, '\t');
+ if (!pos) {
+ free(p);
+ continue;
+ }
+ *pos = '\0';
+ pos++;
+ p->accountname = malloc(pos - prevpos);
+ memmove(p->accountname, prevpos, pos - prevpos);
+
+ prevpos = pos;
+ pos = strchr(prevpos, '\t');
+ if (!pos) {
+ free(p);
+ continue;
+ }
+ *pos = '\0';
+ pos++;
+ p->protocol = malloc(pos - prevpos);
+ memmove(p->protocol, prevpos, pos - prevpos);
+
+ prevpos = pos;
+ pos = strchr(prevpos, '\r');
+ if (!pos) pos = strchr(prevpos, '\n');
+ if (!pos) {
+ free(p);
+ continue;
+ }
+ *pos = '\0';
+ pos++;
+ /* hex str of length 8 */
+ if (strlen(prevpos) != 8) {
+ free(p);
+ continue;
+ }
+
+ sscanf(prevpos, "%08x", &instag);
+
+ if (instag < OTRL_MIN_VALID_INSTAG) {
+ free(p);
+ continue;
+ }
+ p->instag = instag;
+
+ /* Link it up */
+ p->next = us->instag_root;
+ if (p->next) {
+ p->next->tous = &(p->next);
+ }
+ p->tous = &(us->instag_root);
+ us->instag_root = p;
+ }
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Generate a new instance tag for the given account and write to file */
+gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename,
+ const char *accountname, const char *protocol)
+{
+ gcry_error_t err;
+ FILE *instf;
+
+ /* Open the instance tag file. */
+ instf = fopen(filename, "wb");
+ if (!instf) {
+ return gcry_error_from_errno(errno);
+ }
+
+ err = otrl_instag_generate_FILEp(us, instf, accountname, protocol);
+ fclose(instf);
+ return err;
+}
+
+/* Return a new valid instance tag */
+otrl_instag_t otrl_instag_get_new()
+{
+ otrl_instag_t result = 0;
+
+ while(result < OTRL_MIN_VALID_INSTAG) {
+ otrl_instag_t * instag = (otrl_instag_t *)gcry_random_bytes(
+ sizeof(otrl_instag_t), GCRY_STRONG_RANDOM);
+ result = *instag;
+ gcry_free(instag);
+ }
+}
+
+/* Generate a new instance tag for the given account and write to file
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf,
+ const char *accountname, const char *protocol)
+{
+ OtrlInsTag *p;
+ if (!accountname || !protocol) return (gcry_error_t)NULL;
+
+ p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag));
+ p->accountname = malloc(strlen(accountname)+1);
+ p->protocol = malloc(strlen(protocol)+1);
+ strcpy(p->accountname, accountname);
+ strcpy(p->protocol, protocol);
+
+ p->instag = otrl_instag_get_new();
+
+ /* Add to our list in OtrlUserState */
+ p->next = us->instag_root;
+ if (p->next) {
+ p->next->tous = &(p->next);
+ }
+ p->tous = &(us->instag_root);
+ us->instag_root = p;
+
+ otrl_instag_write_FILEp(us, instf);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Write our instance tags to a file on disk. */
+gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename)
+{
+ gcry_error_t err;
+ FILE *instf;
+
+ /* Open the instance tag file. */
+ instf = fopen(filename, "wb");
+ if (!instf) {
+ return gcry_error_from_errno(errno);
+ }
+
+ err = otrl_instag_write_FILEp(us, instf);
+ fclose(instf);
+ return err;
+}
+
+/* Write our instance tags to a file on disk.
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf)
+{
+ OtrlInsTag *p;
+ for(p=us->instag_root; p; p=p->next) {
+ fprintf(instf, "%s\t%s\t%08x\n", p->accountname, p->protocol,
+ p->instag);
+ }
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
diff --git a/src/instag.h b/src/instag.h
new file mode 100644
index 0000000..c3ec036
--- /dev/null
+++ b/src/instag.h
@@ -0,0 +1,89 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
+ * <otr at cypherpunks.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __INSTAG_H__
+#define __INSTAG_H__
+
+#include <stdio.h>
+#include <errno.h>
+
+#define OTRL_INSTAG_MASTER 0
+#define OTRL_INSTAG_BEST 1 /* Most secure, based on: conv status,
+ * then fingerprint status, then most recent. */
+#define OTRL_INSTAG_RECENT 2
+#define OTRL_INSTAG_RECENT_RECEIVED 3
+#define OTRL_INSTAG_RECENT_SENT 4
+
+#define OTRL_MIN_VALID_INSTAG 0x100 /* Instag values below this are reserved
+ * for meta instags, defined above, */
+
+typedef unsigned int otrl_instag_t;
+
+/* The list of instance tags used for our accounts */
+typedef struct s_OtrlInsTag {
+ struct s_OtrlInsTag *next;
+ struct s_OtrlInsTag **tous;
+
+ char *accountname;
+ char *protocol;
+ otrl_instag_t instag;
+} OtrlInsTag;
+
+#include "userstate.h"
+
+/* Forget the given instag. */
+void otrl_instag_forget(OtrlInsTag* instag);
+
+/* Forget all instags in a given OtrlUserState. */
+void otrl_instag_forget_all(OtrlUserState us);
+
+/* Fetch the instance tag from the given OtrlUserState associated with
+ * the given account */
+OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname,
+ const char *protocol);
+
+/* Read our instance tag from a file on disk into the given
+ * OtrlUserState. */
+gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename);
+
+/* Read our instance tag from a file on disk into the given
+ * OtrlUserState. The FILE* must be open for reading. */
+gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf);
+
+/* Return a new valid instance tag */
+otrl_instag_t otrl_instag_get_new();
+
+/* Get a new instance tag for the given account and write to file*/
+gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename,
+ const char *accountname, const char *protocol);
+
+/* Get a new instance tag for the given account and write to file
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf,
+ const char *accountname, const char *protocol);
+
+/* Write our instance tags to a file on disk. */
+gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename);
+
+/* Write our instance tags to a file on disk.
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf);
+
+#endif
diff --git a/src/mem.c b/src/mem.c
index 075f3c0..6af3d0f 100644
--- a/src/mem.c
+++ b/src/mem.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/src/mem.h b/src/mem.h
index e1f5a1e..f128163 100644
--- a/src/mem.h
+++ b/src/mem.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/src/message.c b/src/message.c
index dcc18db..7f6112d 100644
--- a/src/message.c
+++ b/src/message.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -28,10 +28,12 @@
/* libotr headers */
#include "privkey.h"
+#include "userstate.h"
#include "proto.h"
#include "auth.h"
#include "message.h"
#include "sm.h"
+#include "instag.h"
/* The API version */
extern unsigned int otrl_api_version;
@@ -51,24 +53,26 @@ static gcry_error_t fragment_and_send(const OtrlMessageAppOps *ops,
OtrlFragmentPolicy fragPolicy, char **returnFragment)
{
int mms = 0;
+
if (message && ops->inject_message) {
- int msglen;
+ int msglen;
- if (otrl_api_version >= 0x030100 && ops->max_message_size) {
+ if (ops->max_message_size) {
mms = ops->max_message_size(opdata, context);
- }
- msglen = strlen(message);
+ }
+ msglen = strlen(message);
/* Don't incur overhead of fragmentation unless necessary */
- if(mms != 0 && msglen > mms) {
+ if(mms != 0 && msglen > mms) {
char **fragments;
gcry_error_t err;
int i;
- int fragment_count = ((msglen - 1) / (mms -19)) + 1;
- /* like ceil(msglen/(mms - 19)) */
+ int headerlen = context->protocol_version == 3 ? 37 : 19;
+ /* Like ceil(msglen/(mms - headerlen)) */
+ int fragment_count = ((msglen - 1) / (mms - headerlen)) + 1;
err = otrl_proto_fragment_create(mms, fragment_count, &fragments,
- message);
+ context, message);
if (err) {
return err;
}
@@ -92,7 +96,8 @@ static gcry_error_t fragment_and_send(const OtrlMessageAppOps *ops,
*returnFragment = strdup(fragments[fragment_count-1]);
} else {
ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, fragments[fragment_count-1]);
+ context->protocol, context->username,
+ fragments[fragment_count-1]);
}
/* Now free all fragment memory */
otrl_proto_fragment_free(&fragments, fragment_count);
@@ -100,19 +105,37 @@ static gcry_error_t fragment_and_send(const OtrlMessageAppOps *ops,
} else {
/* No fragmentation necessary */
if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
- ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, message);
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, message);
} else {
/* Copy and return the entire given message. */
int l = strlen(message) + 1;
- *returnFragment = malloc(sizeof(char)*l);
- strcpy(*returnFragment, message);
+ *returnFragment = strdup(message);
}
}
}
+
return gcry_error(GPG_ERR_NO_ERROR);
}
+static void populate_context_instag(OtrlUserState us, const OtrlMessageAppOps
+ *ops, void *opdata, const char *accountname, const char *protocol,
+ ConnContext *context) {
+ OtrlInsTag *p_instag;
+
+ p_instag = otrl_instag_find(us, accountname, protocol);
+ if ((!p_instag) && ops->create_instag) {
+ ops->create_instag(opdata, accountname, protocol);
+ p_instag = otrl_instag_find(us, accountname, protocol);
+ }
+
+ if (!p_instag || p_instag->instag < OTRL_MIN_VALID_INSTAG) {
+ p_instag->instag = otrl_instag_get_new();
+ }
+
+ context->our_instance = p_instag->instag;
+}
+
/* Deallocate a message allocated by other otrl_message_* routines. */
void otrl_message_free(char *message)
{
@@ -131,6 +154,14 @@ void otrl_message_free(char *message)
* tlvs is a chain of OtrlTLVs to append to the private message. It is
* usually correct to just pass NULL here.
*
+ * If non-NULL, ops->convert_msg will be called just before encrypting a
+ * message.
+ *
+ * "instag" specifies the instance tag of the buddy (protocol version 3 only).
+ * Meta-instances may also be specified (e.g., OTRL_INSTAG_MOST_SECURE).
+ * If "contextp" is not NULL, it will be set to the ConnContext used for
+ * sending the message.
+ *
* If no fragmentation or msg injection is wanted, use OTRL_FRAGMENT_SEND_SKIP
* as the OtrlFragmentPolicy. In this case, this function will assign *messagep
* with the encrypted msg. If the routine returns non-zero, then the library
@@ -140,47 +171,62 @@ void otrl_message_free(char *message)
* of *messagep, and send that instead.
*
* Other fragmentation policies are OTRL_FRAGMENT_SEND_ALL,
- * OTRL_FRAGMENT_SEND_ALL_BUT_LAST, or OTRL_FRAGMENT_SEND_ALL_BUT_FIRST. In these
- * cases, the appropriate fragments will be automatically sent. For the last two
- * policies, the remaining fragment will be passed in *original_msg.
+ * OTRL_FRAGMENT_SEND_ALL_BUT_LAST, or OTRL_FRAGMENT_SEND_ALL_BUT_FIRST. In
+ * these cases, the appropriate fragments will be automatically sent. For the
+ * last two policies, the remaining fragment will be passed in *original_msg.
*
- * Call otrl_message_free(*messagep) if you don't need *messagep or if you're
+ * Call otrl_message_free(*messagep) if you don't need *messagep or when you're
* done with it. */
gcry_error_t otrl_message_sending(OtrlUserState us,
const OtrlMessageAppOps *ops,
void *opdata, const char *accountname, const char *protocol,
- const char *recipient, char **original_msgp, OtrlTLV *tlvs,
- char **messagep, OtrlFragmentPolicy fragPolicy,
- void (*convert_msg)(void *convert_data, const char *source, char **target),
- void *convert_data,
+ const char *recipient, otrl_instag_t their_instag,
+ const char *original_msg, OtrlTLV *tlvs, char **messagep,
+ OtrlFragmentPolicy fragPolicy, ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data)
{
- struct context * context;
+ ConnContext * context = NULL;
char * msgtosend;
const char * err_msg;
gcry_error_t err_code, err;
OtrlPolicy policy = OTRL_POLICY_DEFAULT;
int context_added = 0;
+ int convert_called = 0;
+ char *converted_msg = NULL;
*messagep = NULL;
err = gcry_error(GPG_ERR_NO_ERROR); /* Default to no error */
+ if (contextp) {
+ *contextp = NULL;
+ }
+
if (!accountname || !protocol || !recipient ||
- !original_msgp || !*original_msgp || !messagep) {
- err = gcry_error(GPG_ERR_NO_ERROR);
- goto fragment;
+ !original_msg || !messagep) {
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto fragment;
}
/* See if we have a fingerprint for this user */
context = otrl_context_find(us, recipient, accountname, protocol,
- 1, &context_added, add_appdata, data);
+ their_instag, 1, &context_added, add_appdata, data);
/* Update the context list if we added one */
if (context_added && ops->update_context_list) {
ops->update_context_list(opdata);
}
+ /* Find or generate the instance tag if needed */
+ if (!context->our_instance) {
+ populate_context_instag(us, ops, opdata, accountname, protocol,
+ context);
+ }
+
+ if (contextp) {
+ *contextp = context;
+ }
+
/* Check the policy */
if (ops->policy) {
policy = ops->policy(opdata, context);
@@ -188,27 +234,27 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
/* Should we go on at all? */
if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
- err = gcry_error(GPG_ERR_NO_ERROR);
- goto fragment;
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto fragment;
}
- /* XXX: Add flags to the policy specifying whether to treat a typed
- * "?OTR?" as a signal to start OTR, or just an ordinary message,
- * for (1) unencrypted conversations, (2) encrypted conversations. */
-
/* If this is an OTR Query message, don't encrypt it. */
- if (otrl_proto_message_type(*original_msgp) == OTRL_MSGTYPE_QUERY) {
+ if (otrl_proto_message_type(original_msg) == OTRL_MSGTYPE_QUERY) {
/* Replace the "?OTR?" with a custom message */
char *bettermsg = otrl_proto_default_query_msg(accountname, policy);
if (bettermsg) {
*messagep = bettermsg;
}
+ if (context) {
+ context->otr_offer = OFFER_SENT;
+ }
err = gcry_error(GPG_ERR_NO_ERROR);
goto fragment;
}
/* What is the current message disposition? */
switch(context->msgstate) {
+
case OTRL_MSGSTATE_PLAINTEXT:
if ((policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
/* We're trying to send an unencrypted message with a policy
@@ -221,15 +267,17 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
}
context->context_priv->lastmessage =
- gcry_malloc_secure(strlen(*original_msgp) + 1);
+ gcry_malloc_secure(strlen(original_msg) + 1);
if (context->context_priv->lastmessage) {
char *bettermsg = otrl_proto_default_query_msg(accountname,
policy);
- strcpy(context->context_priv->lastmessage, *original_msgp);
+ strcpy(context->context_priv->lastmessage, original_msg);
context->context_priv->lastsent = time(NULL);
+ otrl_context_update_recent_child(context, 1);
context->context_priv->may_retransmit = 2;
if (bettermsg) {
*messagep = bettermsg;
+ context->otr_offer = OFFER_SENT;
} else {
err = gcry_error(GPG_ERR_ENOMEM);
goto fragment;
@@ -241,16 +289,18 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
/* See if this user can speak OTR. Append the
* OTR_MESSAGE_TAG to the plaintext message, and see
* if he responds. */
- size_t msglen = strlen(*original_msgp);
+ size_t msglen = strlen(original_msg);
size_t basetaglen = strlen(OTRL_MESSAGE_TAG_BASE);
size_t v1taglen = (policy & OTRL_POLICY_ALLOW_V1) ?
strlen(OTRL_MESSAGE_TAG_V1) : 0;
size_t v2taglen = (policy & OTRL_POLICY_ALLOW_V2) ?
strlen(OTRL_MESSAGE_TAG_V2) : 0;
+ size_t v3taglen = (policy & OTRL_POLICY_ALLOW_V3) ?
+ strlen(OTRL_MESSAGE_TAG_V3) : 0;
char *taggedmsg = malloc(msglen + basetaglen + v1taglen
- +v2taglen + 1);
+ + v2taglen + v3taglen + 1);
if (taggedmsg) {
- strcpy(taggedmsg, *original_msgp);
+ strcpy(taggedmsg, original_msg);
strcpy(taggedmsg + msglen, OTRL_MESSAGE_TAG_BASE);
if (v1taglen) {
strcpy(taggedmsg + msglen + basetaglen,
@@ -260,6 +310,10 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
strcpy(taggedmsg + msglen + basetaglen + v1taglen,
OTRL_MESSAGE_TAG_V2);
}
+ if (v3taglen) {
+ strcpy(taggedmsg + msglen + basetaglen + v1taglen
+ + v2taglen, OTRL_MESSAGE_TAG_V3);
+ }
*messagep = taggedmsg;
if (context) {
context->otr_offer = OFFER_SENT;
@@ -270,31 +324,44 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
break;
case OTRL_MSGSTATE_ENCRYPTED:
/* convert the original message if necessary */
- if (convert_msg) {
- /* using msgtosend as a temporary placeholder */
- msgtosend = *original_msgp;
- convert_msg(convert_data, msgtosend, original_msgp);
- free(msgtosend);
- msgtosend = NULL;
+ if (ops->convert_msg) {
+ ops->convert_msg(opdata, context, OTRL_CONVERT_SENDING,
+ &converted_msg, original_msg);
+
+ if (converted_msg) {
+ convert_called = 1;
+ }
}
/* Create the new, encrypted message */
- err_code = otrl_proto_create_data(&msgtosend, context,
- *original_msgp, tlvs, 0, NULL);
+ if (convert_called) {
+ err_code = otrl_proto_create_data(&msgtosend, context,
+ converted_msg, tlvs, 0, NULL);
+
+ if (ops->convert_free) {
+ ops->convert_free(opdata, context, converted_msg);
+ converted_msg = NULL;
+ }
+ } else {
+ err_code = otrl_proto_create_data(&msgtosend, context,
+ original_msg, tlvs, 0, NULL);
+ }
if (!err_code) {
context->context_priv->lastsent = time(NULL);
+ otrl_context_update_recent_child(context, 1);
*messagep = msgtosend;
} else {
/* Uh, oh. Whatever we do, *don't* send the message in the
* clear. */
if (ops->handle_msg_event) {
- ops->handle_msg_event(opdata, OTRL_MSGEVENT_ENCRYPTION_ERROR,
- context, NULL, (gcry_error_t)NULL);
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_ENCRYPTION_ERROR,
+ context, NULL, (gcry_error_t)NULL);
}
if (ops->otr_error_message) {
- err_msg = ops->otr_error_message(opdata, context,
+ err_msg = ops->otr_error_message(opdata, context,
OTRL_ERRCODE_ENCRYPTION_ERROR);
- *messagep = malloc(strlen(OTR_ERROR_PREFIX) +
+ *messagep = malloc(strlen(OTR_ERROR_PREFIX) +
strlen(err_msg) + 1);
if (*messagep) {
strcpy(*messagep, OTR_ERROR_PREFIX);
@@ -325,26 +392,17 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
fragment:
if (fragPolicy == OTRL_FRAGMENT_SEND_SKIP ) {
- /* Do not fragment/inject. Default behaviour of libotr3.2.0 */
- return err;
+ /* Do not fragment/inject. Default behaviour of libotr3.2.0 */
+ return err;
} else {
/* Fragment and send according to policy */
- if (err && *messagep == NULL) {
- /* Do not send plaintext */
- char *ourm = strdup("");
- free(*original_msgp);
- *original_msgp = ourm;
- } else if (*messagep) {
- ConnContext *conncontext = otrl_context_find(us, recipient,
- accountname, protocol, 0, NULL, NULL, NULL);
- free(*original_msgp);
- *original_msgp = NULL;
- if (conncontext) {
- err = fragment_and_send(ops, NULL, conncontext, *messagep,
- fragPolicy, original_msgp);
+ if (!err && *messagep) {
+ if (context) {
+ err = fragment_and_send(ops, NULL, context, *messagep,
+ fragPolicy, messagep);
}
- }
- return err;
+ }
+ return err;
}
}
@@ -357,15 +415,16 @@ static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
if (!err) {
const char *msg = context->auth.lastauthmsg;
if (msg && *msg) {
- fragment_and_send(ops, opdata, context, msg,
+ fragment_and_send(ops, opdata, context, msg,
OTRL_FRAGMENT_SEND_ALL, NULL);
context->context_priv->lastsent = time(NULL);
+ otrl_context_update_recent_child(context, 1);
}
} else {
- if (ops->handle_msg_event) {
+ if (ops->handle_msg_event) {
ops->handle_msg_event(opdata, OTRL_MSGEVENT_SETUP_ERROR,
context, NULL, err);
- }
+ }
}
return err;
}
@@ -391,12 +450,12 @@ static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
/* See if we're talking to ourselves */
if (!gcry_mpi_cmp(auth->their_pub, auth->our_dh.pub)) {
- /* Yes, we are. */
- if (edata->ops->handle_msg_event) {
+ /* Yes, we are. */
+ if (edata->ops->handle_msg_event) {
edata->ops->handle_msg_event(edata->opdata,
OTRL_MSGEVENT_MSG_REFLECTED, edata->context,
NULL, (gcry_error_t)NULL);
- }
+ }
edata->ignore_message = 1;
return gcry_error(GPG_ERR_NO_ERROR);
}
@@ -421,7 +480,8 @@ static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
/* Is this a new session or just a refresh of an existing one? */
if (edata->context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
oldprint == found_print &&
- edata->context->context_priv->our_keyid - 1 == edata->context->auth.our_keyid &&
+ edata->context->context_priv->our_keyid - 1 ==
+ edata->context->auth.our_keyid &&
!gcry_mpi_cmp(edata->context->context_priv->our_old_dh_key.pub,
edata->context->auth.our_dh.pub) &&
((edata->context->context_priv->their_keyid > 0 &&
@@ -448,38 +508,45 @@ static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
memmove(edata->context->sessionid,
edata->context->auth.secure_session_id, 20);
edata->context->sessionid_len =
- edata->context->auth.secure_session_id_len;
+ edata->context->auth.secure_session_id_len;
edata->context->sessionid_half =
- edata->context->auth.session_id_half;
+ edata->context->auth.session_id_half;
edata->context->protocol_version =
- edata->context->auth.protocol_version;
+ edata->context->auth.protocol_version;
- edata->context->context_priv->their_keyid = edata->context->auth.their_keyid;
+ edata->context->context_priv->their_keyid =
+ edata->context->auth.their_keyid;
gcry_mpi_release(edata->context->context_priv->their_y);
gcry_mpi_release(edata->context->context_priv->their_old_y);
- edata->context->context_priv->their_y = gcry_mpi_copy(edata->context->auth.their_pub);
+ edata->context->context_priv->their_y =
+ gcry_mpi_copy(edata->context->auth.their_pub);
edata->context->context_priv->their_old_y = NULL;
- if (edata->context->context_priv->our_keyid - 1 != edata->context->auth.our_keyid ||
+ if (edata->context->context_priv->our_keyid - 1 !=
+ edata->context->auth.our_keyid ||
gcry_mpi_cmp(edata->context->context_priv->our_old_dh_key.pub,
- edata->context->auth.our_dh.pub)) {
+ edata->context->auth.our_dh.pub)) {
otrl_dh_keypair_free(&(edata->context->context_priv->our_dh_key));
otrl_dh_keypair_free(&(edata->context->context_priv->our_old_dh_key));
otrl_dh_keypair_copy(&(edata->context->context_priv->our_old_dh_key),
&(edata->context->auth.our_dh));
- otrl_dh_gen_keypair(edata->context->context_priv->our_old_dh_key.groupid,
+ otrl_dh_gen_keypair(
+ edata->context->context_priv->our_old_dh_key.groupid,
&(edata->context->context_priv->our_dh_key));
- edata->context->context_priv->our_keyid = edata->context->auth.our_keyid + 1;
+ edata->context->context_priv->our_keyid = edata->context->auth.our_keyid
+ + 1;
}
/* Create the session keys from the DH keys */
otrl_dh_session_free(&(edata->context->context_priv->sesskeys[0][0]));
err = otrl_dh_session(&(edata->context->context_priv->sesskeys[0][0]),
- &(edata->context->context_priv->our_dh_key), edata->context->context_priv->their_y);
+ &(edata->context->context_priv->our_dh_key),
+ edata->context->context_priv->their_y);
if (err) return err;
otrl_dh_session_free(&(edata->context->context_priv->sesskeys[1][0]));
err = otrl_dh_session(&(edata->context->context_priv->sesskeys[1][0]),
- &(edata->context->context_priv->our_old_dh_key), edata->context->context_priv->their_y);
+ &(edata->context->context_priv->our_old_dh_key),
+ edata->context->context_priv->their_y);
if (err) return err;
edata->context->context_priv->generation++;
@@ -526,7 +593,7 @@ static void maybe_resend(EncrData *edata)
const char *resent_prefix;
int used_ops_resentmp = 1;
resent_prefix = edata->ops->resent_msg_prefix ?
- edata->ops->resent_msg_prefix(edata->opdata,
+ edata->ops->resent_msg_prefix(edata->opdata,
edata->context) : NULL;
if (!resent_prefix) {
resent_prefix = "[resent]"; /* Assign default prefix */
@@ -560,8 +627,9 @@ static void maybe_resend(EncrData *edata)
resendmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
free(resendmsg);
edata->context->context_priv->lastsent = now;
+ otrl_context_update_recent_child(edata->context, 1);
if (resending) {
- /* We're not sending it for the first time; let the user
+ /* We're not sending it for the first time; let the user
* know we resent it */
if (edata->ops->handle_msg_event) {
edata->ops->handle_msg_event(edata->opdata,
@@ -660,14 +728,14 @@ static void init_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
: OTRL_TLV_SMP2,
smpmsglen, smpmsg);
err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
if (!err) {
- /* Send it, and set the next expected message to the
+ /* Send it, and set the next expected message to the
* logical response */
- err = fragment_and_send(ops, opdata, context,
+ err = fragment_and_send(ops, opdata, context,
sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
- context->smstate->nextExpected =
- initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
+ context->smstate->nextExpected =
+ initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
}
free(sendsmp);
otrl_tlv_free(sendtlv);
@@ -723,6 +791,36 @@ void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
otrl_tlv_free(sendtlv);
}
+static void message_malformed(const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context) {
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata, OTRL_MSGEVENT_RCVDMSG_MALFORMED, context,
+ NULL, (gcry_error_t)NULL);
+ }
+
+ if (ops->inject_message && ops->otr_error_message) {
+ const char *err_msg = ops->otr_error_message(opdata, context,
+ OTRL_ERRCODE_MSG_MALFORMED);
+
+ if (err_msg) {
+ char *buf = malloc(strlen(OTR_ERROR_PREFIX) + strlen(err_msg) + 1);
+
+ if (buf) {
+ strcpy(buf, OTR_ERROR_PREFIX);
+ strcat(buf, err_msg);
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, buf);
+ free(buf);
+ }
+
+ if (ops->otr_error_message_free) {
+ ops->otr_error_message_free(opdata, err_msg);
+ }
+ }
+ }
+}
+
+
/* Handle a message just received from the network. It is safe to pass
* all received messages to this routine. add_appdata is a function
* that will be called in the event that a new ConnContext is created.
@@ -732,8 +830,11 @@ void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
* "context->app" field, for example. If you don't need to do this, you
* can pass NULL for the last two arguments of otrl_message_receiving.
*
- * convert_msg is a function that will be called on each msg that is received.
- * You can use it to perform some tweaks on your incoming messages.
+ * If non-NULL, ops->convert_msg will be called after a data message is
+ * decrypted.
+ *
+ * If "contextp" is not NULL, it will be set to the ConnContext used for
+ * receiving the message.
*
* If otrl_message_receiving returns 1, then the message you received
* was an internal protocol message, and no message should be delivered
@@ -753,36 +854,51 @@ void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, const char *accountname, const char *protocol,
const char *sender, const char *message, char **newmessagep,
- OtrlTLV **tlvsp,
- void (*convert_msg)(void *convert_data, const char *source, char **target),
- void *convert_data,
+ OtrlTLV **tlvsp, ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data)
{
- ConnContext *context;
+ ConnContext *context, *m_context, *best_context;
OtrlMessageType msgtype;
int context_added = 0;
- OtrlMessageState msgstate;
OtrlPolicy policy = OTRL_POLICY_DEFAULT;
int fragment_assembled = 0;
- char *unfragmessage = NULL;
+ char *unfragmessage = NULL, *otrtag = NULL;
EncrData edata;
+ otrl_instag_t our_instance, their_instance;
+ int version;
+ gcry_error_t err;
if (!accountname || !protocol || !sender || !message || !newmessagep)
- return 0;
+ return 0;
*newmessagep = NULL;
if (tlvsp) *tlvsp = NULL;
- /* Find our context and state with this correspondent */
- context = otrl_context_find(us, sender, accountname,
- protocol, 1, &context_added, add_appdata, data);
+ if (contextp) {
+ *contextp = NULL;
+ }
+
+ /* Find the master context and state with this correspondent */
+ m_context = otrl_context_find(us, sender, accountname,
+ protocol, OTRL_INSTAG_MASTER, 1, &context_added, add_appdata, data);
+ context = m_context;
/* Update the context list if we added one */
if (context_added && ops->update_context_list) {
ops->update_context_list(opdata);
}
+ best_context = otrl_context_find(us, sender, accountname,
+ protocol, OTRL_INSTAG_BEST, 0, NULL, add_appdata, data);
+
+ /* Find or generate the instance tag if needed */
+ if (!context->our_instance) {
+ populate_context_instag(us, ops, opdata, accountname, protocol,
+ context);
+ }
+
+
/* Check the policy */
if (ops->policy) {
policy = ops->policy(opdata, context);
@@ -790,29 +906,57 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
/* Should we go on at all? */
if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
- return 0;
+ return 0;
}
- /* See if we have a fragment */
- switch(otrl_proto_fragment_accumulate(&unfragmessage, context, message)) {
- case OTRL_FRAGMENT_UNFRAGMENTED:
- /* Do nothing */
- break;
- case OTRL_FRAGMENT_INCOMPLETE:
- /* We've accumulated this fragment, but we don't have a
- * complete message yet */
- return 1;
- case OTRL_FRAGMENT_COMPLETE:
- /* We've got a new complete message, in unfragmessage. */
- fragment_assembled = 1;
- message = unfragmessage;
- break;
+ otrtag = strstr(message, "?OTR");
+ if (otrtag) {
+ /* See if we have a V3 fragment */
+ if (strstr(message, "?OTR|")) {
+ /* Get the instance tag from fragment header*/
+ sscanf(otrtag, "?OTR|%x|%x,", &their_instance, &our_instance);
+ /* Ignore message if it is intended for a different instance */
+ if (our_instance && context->our_instance != our_instance) {
+
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
+ m_context, NULL, (gcry_error_t)NULL);
+ }
+ return 1;
+ }
+ /* Get the context for this instance */
+ if (their_instance >= OTRL_MIN_VALID_INSTAG) {
+ context = otrl_context_find(us, sender, accountname,
+ protocol, their_instance, 1, &context_added,
+ add_appdata, data);
+ } else {
+ message_malformed(ops, opdata, context);
+ return 1;
+ }
+ }
+ switch(otrl_proto_fragment_accumulate(&unfragmessage,
+ context, message)) {
+ case OTRL_FRAGMENT_UNFRAGMENTED:
+ /* Do nothing */
+ break;
+ case OTRL_FRAGMENT_INCOMPLETE:
+ /* We've accumulated this fragment, but we don't have a
+ * complete message yet */
+ return 1;
+ case OTRL_FRAGMENT_COMPLETE:
+ /* We've got a new complete message, in unfragmessage. */
+ fragment_assembled = 1;
+ message = unfragmessage;
+ otrtag = strstr(message, "?OTR");
+ break;
+ }
}
/* What type of message is it? Note that this just checks the
* header; it's not necessarily a _valid_ message of this type. */
msgtype = otrl_proto_message_type(message);
- msgstate = context->msgstate;
+ version = otrl_proto_message_version(message);
/* See if they responded to our OTR offer */
if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG)) {
@@ -823,6 +967,89 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
}
}
+ /* Check that this version is allowed by the policy */
+ if (((version == 3) && !(policy & OTRL_POLICY_ALLOW_V3))
+ || ((version == 2) && !(policy & OTRL_POLICY_ALLOW_V2))
+ || ((version == 1) && !(policy & OTRL_POLICY_ALLOW_V1))) {
+ return 1;
+ }
+ /* Check the to and from instance tags */
+ if (version == 3) {
+ err = otrl_proto_instance(otrtag, &their_instance, &our_instance);
+ if (!err) {
+ if (our_instance && context->our_instance != our_instance) {
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
+ m_context, NULL, (gcry_error_t)NULL);
+ }
+ return 1; /* ignore message intended for a different instance */
+ }
+
+ if (their_instance >= OTRL_MIN_VALID_INSTAG) {
+ context = otrl_context_find(us, sender, accountname,
+ protocol, their_instance, 1, &context_added,
+ add_appdata, data);
+ } else {
+ message_malformed(ops, opdata, context);
+ return 1;
+ }
+ }
+
+ if (context_added) {
+ /* Context added because of new instance (either here or when
+ * accumulating fragments */
+ /* Copy information from m_context to the new instance context */
+ context->auth.protocol_version = 3;
+ context->protocol_version = 3;
+
+ if (context_added) {
+ context->msgstate = m_context->msgstate;
+ }
+
+ if (msgtype == OTRL_MSGTYPE_DH_COMMIT) {
+ otrl_auth_copy_on_commit(&(m_context->auth), &(context->auth));
+ } else if (msgtype == OTRL_MSGTYPE_DH_KEY) {
+ otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
+ } else {
+ return 1; /* Ignore unexpected message */
+ }
+
+ /* Update the context list */
+ if (ops->update_context_list) {
+ ops->update_context_list(opdata);
+ }
+ } else if (m_context != context && (context->msgstate !=
+ OTRL_MSGSTATE_ENCRYPTED || context->otr_offer == OFFER_SENT)) {
+ /* Switching from m_context to existing instance context */
+ if (msgtype == OTRL_MSGTYPE_DH_KEY && m_context->auth.authstate
+ == OTRL_AUTHSTATE_AWAITING_DHKEY &&
+ !(context->auth.authstate ==
+ OTRL_AUTHSTATE_AWAITING_DHKEY)) {
+ context->msgstate = m_context->msgstate;
+ context->auth.protocol_version = 3;
+ context->protocol_version = 3;
+ otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
+ } else if (msgtype == OTRL_MSGTYPE_DH_COMMIT &&
+ m_context->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY
+ && !(context->auth.authstate ==
+ OTRL_AUTHSTATE_AWAITING_DHKEY)) {
+ context->msgstate = m_context->msgstate;
+ context->auth.protocol_version = 3;
+ context->protocol_version = 3;
+ otrl_auth_copy_on_commit(&(m_context->auth), &(context->auth));
+ }
+ }
+ }
+
+ if (contextp) {
+ *contextp = context;
+ }
+
+ /* update time of last received message */
+ context->context_priv->lastrecv = time(NULL);
+ otrl_context_update_recent_child(context, 0);
+
edata.gone_encrypted = 0;
edata.us = us;
edata.context = context;
@@ -837,8 +1064,8 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
DH_keypair *our_dh;
unsigned int our_keyid;
OtrlPrivKey *privkey;
- gcry_error_t err;
int haveauthmsg;
+
case OTRL_MSGTYPE_QUERY:
/* See if we should use an existing DH keypair, or generate
* a fresh one. */
@@ -852,8 +1079,12 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
/* Find the best version of OTR that we both speak */
switch(otrl_proto_query_bestversion(message, policy)) {
+ case 3:
+ err = otrl_auth_start_v23(&(context->auth), 3);
+ send_or_error_auth(ops, opdata, err, context);
+ break;
case 2:
- err = otrl_auth_start_v2(&(context->auth));
+ err = otrl_auth_start_v23(&(context->auth), 2);
send_or_error_auth(ops, opdata, err, context);
break;
case 1:
@@ -884,34 +1115,30 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
break;
case OTRL_MSGTYPE_DH_COMMIT:
- if ((policy & OTRL_POLICY_ALLOW_V2)) {
- err = otrl_auth_handle_commit(&(context->auth), message);
- send_or_error_auth(ops, opdata, err, context);
- }
+ err = otrl_auth_handle_commit(&(context->auth), otrtag, version);
+ send_or_error_auth(ops, opdata, err, context);
if (edata.ignore_message == -1) edata.ignore_message = 1;
break;
case OTRL_MSGTYPE_DH_KEY:
- if ((policy & OTRL_POLICY_ALLOW_V2)) {
- /* Get our private key */
- privkey = otrl_privkey_find(us, context->accountname,
- context->protocol);
- if (privkey == NULL) {
- /* We've got no private key! */
- if (ops->create_privkey) {
- ops->create_privkey(opdata, context->accountname,
- context->protocol);
- privkey = otrl_privkey_find(us,
- context->accountname, context->protocol);
- }
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
}
- if (privkey) {
- err = otrl_auth_handle_key(&(context->auth), message,
- &haveauthmsg, privkey);
- if (err || haveauthmsg) {
- send_or_error_auth(ops, opdata, err, context);
- }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_key(&(context->auth), otrtag,
+ &haveauthmsg, privkey);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
}
}
@@ -919,27 +1146,25 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
break;
case OTRL_MSGTYPE_REVEALSIG:
- if ((policy & OTRL_POLICY_ALLOW_V2)) {
- /* Get our private key */
- privkey = otrl_privkey_find(us, context->accountname,
- context->protocol);
- if (privkey == NULL) {
- /* We've got no private key! */
- if (ops->create_privkey) {
- ops->create_privkey(opdata, context->accountname,
- context->protocol);
- privkey = otrl_privkey_find(us,
- context->accountname, context->protocol);
- }
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
}
- if (privkey) {
- err = otrl_auth_handle_revealsig(&(context->auth),
- message, &haveauthmsg, privkey, go_encrypted,
- &edata);
- if (err || haveauthmsg) {
- send_or_error_auth(ops, opdata, err, context);
- maybe_resend(&edata);
- }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_revealsig(&(context->auth),
+ otrtag, &haveauthmsg, privkey, go_encrypted,
+ &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
}
}
@@ -947,50 +1172,46 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
break;
case OTRL_MSGTYPE_SIGNATURE:
- if ((policy & OTRL_POLICY_ALLOW_V2)) {
- err = otrl_auth_handle_signature(&(context->auth),
- message, &haveauthmsg, go_encrypted, &edata);
- if (err || haveauthmsg) {
- send_or_error_auth(ops, opdata, err, context);
- maybe_resend(&edata);
- }
+ err = otrl_auth_handle_signature(&(context->auth),
+ otrtag, &haveauthmsg, go_encrypted, &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
}
if (edata.ignore_message == -1) edata.ignore_message = 1;
break;
case OTRL_MSGTYPE_V1_KEYEXCH:
- if ((policy & OTRL_POLICY_ALLOW_V1)) {
- /* See if we should use an existing DH keypair, or generate
- * a fresh one. */
- if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
- our_dh = &(context->context_priv->our_old_dh_key);
- our_keyid = context->context_priv->our_keyid - 1;
- } else {
- our_dh = NULL;
- our_keyid = 0;
- }
+ /* See if we should use an existing DH keypair, or generate
+ * a fresh one. */
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ our_dh = &(context->context_priv->our_old_dh_key);
+ our_keyid = context->context_priv->our_keyid - 1;
+ } else {
+ our_dh = NULL;
+ our_keyid = 0;
+ }
- /* Get our private key */
- privkey = otrl_privkey_find(us, context->accountname,
- context->protocol);
- if (privkey == NULL) {
- /* We've got no private key! */
- if (ops->create_privkey) {
- ops->create_privkey(opdata, context->accountname,
- context->protocol);
- privkey = otrl_privkey_find(us,
- context->accountname, context->protocol);
- }
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
}
- if (privkey) {
- err = otrl_auth_handle_v1_key_exchange(&(context->auth),
- message, &haveauthmsg, privkey, our_dh, our_keyid,
- go_encrypted, &edata);
- if (err || haveauthmsg) {
- send_or_error_auth(ops, opdata, err, context);
- maybe_resend(&edata);
- }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_v1_key_exchange(&(context->auth),
+ message, &haveauthmsg, privkey, our_dh, our_keyid,
+ go_encrypted, &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
}
}
@@ -1017,28 +1238,25 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
edata.ignore_message = 1;
break;
}
- if (ops->handle_msg_event) {
- ops->handle_msg_event(opdata,
- OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE,
- context, NULL, (gcry_error_t)NULL);
+
+ if(best_context && best_context != context &&
+ best_context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
+ m_context, NULL, (gcry_error_t)NULL);
+ }
+ } else if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE,
+ context, NULL, (gcry_error_t)NULL);
}
edata.ignore_message = 1;
- if (ops->inject_message && ops->otr_error_message) {
- err_msg = ops->otr_error_message(opdata, context,
- OTRL_ERRCODE_MSG_NOT_IN_PRIVATE);
- buf = malloc(strlen(OTR_ERROR_PREFIX) +
- strlen(err_msg) + 1);
- if (buf) {
- strcpy(buf, OTR_ERROR_PREFIX);
- strcat(buf, err_msg);
- ops->inject_message(opdata, accountname, protocol,
- sender, buf);
- free(buf);
- }
- if (ops->otr_error_message_free) {
- ops->otr_error_message_free(opdata, err_msg);
- }
- }
+
+ /* We don't actually want to send anything in this case,
+ since this could just be a message intended for another
+ v2 instance. We still notify the local user though */
break;
case OTRL_MSGSTATE_ENCRYPTED:
@@ -1115,7 +1333,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
gcry_free(extrakey);
extrakey = NULL;
- /* If TLVs contain SMP data, process it */
+ /* If TLVs contain SMP data, process it */
nextMsg = context->smstate->nextExpected;
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
@@ -1127,9 +1345,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
char *question = (char *)tlv->data;
char *qend = memchr(question, '\0', tlv->len - 1);
size_t qlen = qend ? (qend - question + 1) :
- tlv->len;
+ tlv->len;
otrl_sm_step2a(context->smstate, tlv->data + qlen,
- tlv->len - qlen, 1);
+ tlv->len - qlen, 1);
if (context->smstate->sm_prog_state !=
OTRL_SMP_PROG_CHEATED) {
@@ -1145,9 +1363,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
0, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
context->smstate->sm_prog_state =
- OTRL_SMP_PROG_OK;
+ OTRL_SMP_PROG_OK;
}
} else {
if (ops->handle_smp_event) {
@@ -1180,9 +1398,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 0, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
context->smstate->sm_prog_state =
- OTRL_SMP_PROG_OK;
+ OTRL_SMP_PROG_OK;
}
} else {
if (ops->handle_smp_event) {
@@ -1209,13 +1427,13 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
sendtlv = otrl_tlv_new(OTRL_TLV_SMP3,
nextmsglen, nextmsg);
err = otrl_proto_create_data(&sendsmp,
- context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE,
- NULL);
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE,
+ NULL);
if (!err) {
err = fragment_and_send(ops,
- opdata, context, sendsmp,
- OTRL_FRAGMENT_SEND_ALL, NULL);
+ opdata, context, sendsmp,
+ OTRL_FRAGMENT_SEND_ALL, NULL);
}
free(sendsmp);
otrl_tlv_free(sendtlv);
@@ -1226,7 +1444,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 60, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT4;
+ OTRL_SMP_EXPECT4;
} else {
if (ops->handle_smp_event) {
ops->handle_smp_event(opdata,
@@ -1234,9 +1452,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 0, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
context->smstate->sm_prog_state =
- OTRL_SMP_PROG_OK;
+ OTRL_SMP_PROG_OK;
}
free(nextmsg);
} else {
@@ -1260,7 +1478,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
/* Set trust level based on result */
if (context->smstate->received_question == 0) {
set_smp_trust(ops, opdata, context,
- (err == gcry_error(GPG_ERR_NO_ERROR)));
+ (err == gcry_error(GPG_ERR_NO_ERROR)));
}
if (context->smstate->sm_prog_state !=
@@ -1269,13 +1487,13 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
sendtlv = otrl_tlv_new(OTRL_TLV_SMP4,
nextmsglen, nextmsg);
err = otrl_proto_create_data(&sendsmp,
- context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE,
- NULL);
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE,
+ NULL);
if (!err) {
err = fragment_and_send(ops,
- opdata, context, sendsmp,
- OTRL_FRAGMENT_SEND_ALL, NULL);
+ opdata, context, sendsmp,
+ OTRL_FRAGMENT_SEND_ALL, NULL);
}
free(sendsmp);
otrl_tlv_free(sendtlv);
@@ -1298,9 +1516,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 0, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
context->smstate->sm_prog_state =
- OTRL_SMP_PROG_OK;
+ OTRL_SMP_PROG_OK;
}
free(nextmsg);
} else {
@@ -1316,10 +1534,10 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
if (tlv) {
if (nextMsg == OTRL_SMP_EXPECT4) {
err = otrl_sm_step5(context->smstate, tlv->data,
- tlv->len);
+ tlv->len);
/* Set trust level based on result */
set_smp_trust(ops, opdata, context,
- (err == gcry_error(GPG_ERR_NO_ERROR)));
+ (err == gcry_error(GPG_ERR_NO_ERROR)));
if (context->smstate->sm_prog_state !=
OTRL_SMP_PROG_CHEATED) {
@@ -1333,7 +1551,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 100, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
} else {
if (ops->handle_smp_event) {
ops->handle_smp_event(opdata,
@@ -1341,9 +1559,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 0, NULL);
}
context->smstate->nextExpected =
- OTRL_SMP_EXPECT1;
+ OTRL_SMP_EXPECT1;
context->smstate->sm_prog_state =
- OTRL_SMP_PROG_OK;
+ OTRL_SMP_PROG_OK;
}
} else {
if (ops->handle_smp_event) {
@@ -1362,10 +1580,10 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
context, 0, NULL);
}
}
-
+
if (plaintext[0] == '\0') {
/* If it's a heartbeat (an empty message), don't
- * display it to the user, but log a debug message. */
+ * display it to the user, but signal an event. */
if (ops->handle_msg_event) {
ops->handle_msg_event(opdata,
OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD,
@@ -1388,7 +1606,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
OTRL_MSGFLAGS_IGNORE_UNREADABLE,
NULL);
if (!err) {
- /* Send it, and log a debug message */
+ /* Send it, and inject a debug message */
if (ops->inject_message) {
ops->inject_message(opdata, accountname,
protocol, sender, heartbeat);
@@ -1396,12 +1614,13 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
free(heartbeat);
context->context_priv->lastsent = now;
+ otrl_context_update_recent_child(context, 1);
- /* Log the heartbeat message */
+ /* Signal an event for the heartbeat message */
if (ops->handle_msg_event) {
ops->handle_msg_event(opdata,
- OTRL_MSGEVENT_LOG_HEARTBEAT_SENT,
- context, NULL, (gcry_error_t)NULL);
+ OTRL_MSGEVENT_LOG_HEARTBEAT_SENT,
+ context, NULL, (gcry_error_t)NULL);
}
}
}
@@ -1416,17 +1635,28 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
}
if (edata.ignore_message != 1) {
- *newmessagep = plaintext;
- edata.ignore_message = 0;
- /* convert the original message if necessary */
- if (convert_msg && *newmessagep) {
- char *original_msg;
- original_msg = *newmessagep;
- convert_msg(convert_data, original_msg,
- newmessagep);
- free(original_msg);
- original_msg = NULL;
- }
+ char *converted_msg = NULL;
+
+ *newmessagep = plaintext;
+ edata.ignore_message = 0;
+
+ /* convert the plaintext message if necessary */
+ if (ops->convert_msg) {
+ ops->convert_msg(opdata, context,
+ OTRL_CONVERT_RECEIVING, &converted_msg,
+ plaintext);
+
+ if (converted_msg) {
+ free(plaintext);
+ plaintext = NULL;
+ *newmessagep = strdup(converted_msg);
+
+ if (ops->convert_free) {
+ ops->convert_free(opdata, context,
+ converted_msg);
+ }
+ }
+ }
} else {
free(plaintext);
}
@@ -1446,20 +1676,20 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
free(msgtosend);
}
- if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
- /* Mark the last message we sent as eligible for
- * retransmission */
- context->context_priv->may_retransmit = 1;
- }
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ /* Mark the last message we sent as eligible for
+ * retransmission */
+ context->context_priv->may_retransmit = 1;
+ }
- /* In any event, display the error message, with the
- * display_otr_message callback, if possible */
- if (ops->handle_msg_event) {
- /* Remove the OTR error prefix and pass the msg */
- const char *just_err_msg = strstr(message, OTR_ERROR_PREFIX);
- if (!just_err_msg) {
+ /* In any event, display the error message, with the
+ * display_otr_message callback, if possible */
+ if (ops->handle_msg_event) {
+ /* Remove the OTR error prefix and pass the msg */
+ const char *just_err_msg = strstr(message, OTR_ERROR_PREFIX);
+ if (!just_err_msg) {
just_err_msg = message;
- } else {
+ } else {
just_err_msg += (strlen(OTR_ERROR_PREFIX));
if (*just_err_msg == ' ') {
/* Advance pointer to skip the space character */
@@ -1468,9 +1698,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
ops->handle_msg_event(opdata,
OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR,
context, just_err_msg, (gcry_error_t)NULL);
- }
+ edata.ignore_message = 1;
+ }
}
- edata.ignore_message = 1;
break;
case OTRL_MSGTYPE_TAGGEDPLAINTEXT:
@@ -1491,8 +1721,12 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
if (bestversion && context->msgstate != OTRL_MSGSTATE_ENCRYPTED
&& (policy & OTRL_POLICY_WHITESPACE_START_AKE)) {
switch(bestversion) {
+ case 3:
+ err = otrl_auth_start_v23(&(context->auth), 3);
+ send_or_error_auth(ops, opdata, err, context);
+ break;
case 2:
- err = otrl_auth_start_v2(&(context->auth));
+ err = otrl_auth_start_v23(&(context->auth), 2);
send_or_error_auth(ops, opdata, err, context);
break;
case 1:
@@ -1524,7 +1758,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
/* FALLTHROUGH */
case OTRL_MSGTYPE_NOTOTR:
- if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
+ if (best_context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
(policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
/* Not fine. Let the user know. */
const char *plainmsg = (*newmessagep) ? *newmessagep : message;
@@ -1532,16 +1766,16 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
ops->handle_msg_event(opdata,
OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED,
context, plainmsg, (gcry_error_t)NULL);
+ free(*newmessagep);
+ *newmessagep = NULL;
+ edata.ignore_message = 1;
}
- free(*newmessagep);
- *newmessagep = NULL;
- edata.ignore_message = 1;
}
break;
case OTRL_MSGTYPE_UNKNOWN:
/* We received an OTR message we didn't recognize. Ignore
- * it, but make a log entry. */
+ * it, and signal an event. */
if (ops->handle_msg_event) {
ops->handle_msg_event(opdata,
OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED,
@@ -1563,20 +1797,17 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
/* 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. */
-void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
- void *opdata, const char *accountname, const char *protocol,
- const char *username)
+ * and we think he's logged in. Affects only the specified context. */
+static void disconnect_context(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context)
{
- ConnContext *context = otrl_context_find(us, username, accountname,
- protocol, 0, NULL, NULL, NULL);
-
if (!context) return;
if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
context->context_priv->their_keyid > 0 &&
ops->is_logged_in &&
- ops->is_logged_in(opdata, accountname, protocol, username) == 1) {
+ ops->is_logged_in(opdata, context->accountname, context->protocol,
+ context->username) == 1) {
if (ops->inject_message) {
char *encmsg = NULL;
gcry_error_t err;
@@ -1585,8 +1816,8 @@ void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
err = otrl_proto_create_data(&encmsg, context, "", tlv,
OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
if (!err) {
- ops->inject_message(opdata, accountname, protocol,
- username, encmsg);
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, encmsg);
}
free(encmsg);
}
@@ -1598,6 +1829,45 @@ void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
}
}
+
+/* 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. */
+void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, const char *accountname, const char *protocol,
+ const char *username, otrl_instag_t instance)
+{
+ ConnContext *context = otrl_context_find(us, username, accountname,
+ protocol, instance, 0, NULL, NULL, NULL);
+
+ if (!context) return;
+
+ disconnect_context(us, ops, opdata, context);
+}
+
+/* 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 all matching instances. */
+void otrl_message_disconnect_all_instances(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, const char *accountname,
+ const char *protocol, const char *username)
+{
+ ConnContext * c_iter;
+ ConnContext *context;
+
+ if (!username || !accountname || !protocol) return;
+
+ context = otrl_context_find(us, username, accountname,
+ protocol, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
+
+ if (!context) return;
+
+ for (c_iter = context; c_iter && c_iter->m_context == context->m_context;
+ c_iter = c_iter->next) {
+ disconnect_context(us, ops, opdata, c_iter);
+ }
+}
+
/* Get the current extra symmetric key (of size OTRL_EXTRAKEY_BYTES
* bytes) and let the other side know what we're going to use it for.
* The key is stored in symkey, which must already be allocated
diff --git a/src/message.h b/src/message.h
index f2d1c7a..87927c0 100644
--- a/src/message.h
+++ b/src/message.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -61,7 +61,8 @@ typedef enum {
OTRL_MSGEVENT_LOG_HEARTBEAT_SENT,
OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR,
OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED,
- OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED
+ OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE
} OtrlMessageEvent;
typedef enum {
@@ -70,6 +71,11 @@ typedef enum {
OTRL_NOTIFY_INFO
} OtrlNotifyLevel;
+typedef enum {
+ OTRL_CONVERT_SENDING,
+ OTRL_CONVERT_RECEIVING
+} OtrlConvertType;
+
typedef struct s_OtrlMessageAppOps {
/* Return the OTR policy for the given context. */
OtrlPolicy (*policy)(void *opdata, ConnContext *context);
@@ -148,7 +154,7 @@ typedef struct s_OtrlMessageAppOps {
* - OTRL_ERRCODE_MSG_MALFORMED
* message sent is malformed */
const char *(*otr_error_message)(void *opdata, ConnContext *context,
- OtrlErrorCode err_code);
+ OtrlErrorCode err_code);
/* Deallocate a string returned by otr_error_message */
void (*otr_error_message_free)(void *opdata, const char *err_msg);
@@ -228,10 +234,27 @@ typedef struct s_OtrlMessageAppOps {
* also be passed and it will contain the plaintext message.
* - OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED
* Cannot recognize the type of OTR message received.
- * */
+ * - OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE
+ * Received and discarded a message intended for another instance. */
void (*handle_msg_event)(void *opdata, OtrlMessageEvent msg_event,
- ConnContext *context, const char *message,
- gcry_error_t err);
+ ConnContext *context, const char *message,
+ gcry_error_t err);
+
+ /* Create a instance tag for the given accountname/protocol if
+ * desired. */
+ void (*create_instag)(void *opdata, const char *accountname,
+ const char *protocol);
+
+ /* Called immediately before a data message is encrypted, and after a data
+ * message is decrypted. The OtrlConvertType parameter has the value
+ * OTRL_CONVERT_SENDING or OTRL_CONVERT_RECEIVING to differentiate these
+ * cases. */
+ void (*convert_msg)(void *opdata, ConnContext *context,
+ OtrlConvertType convert_type, char ** dest, const char *src);
+
+ /* Deallocate a string returned by convert_msg. */
+ void (*convert_free)(void *opdata, ConnContext *context, char *dest);
+
} OtrlMessageAppOps;
/* Deallocate a message allocated by other otrl_message_* routines. */
@@ -249,8 +272,13 @@ void otrl_message_free(char *message);
* tlvs is a chain of OtrlTLVs to append to the private message. It is
* usually correct to just pass NULL here.
*
- * convert_msg is a function that will be called on each msg to be sent. You
- * can use it to perform some tweaks on your outgoing messages.
+ * If non-NULL, ops->convert_msg will be called just before encrypting a
+ * message.
+ *
+ * "instag" specifies the instance tag of the buddy (protocol version 3 only).
+ * Meta-instances may also be specified (e.g., OTRL_INSTAG_MOST_SECURE).
+ * If "contextp" is not NULL, it will be set to the ConnContext used for
+ * sending the message.
*
* If no fragmentation or msg injection is wanted, use OTRL_FRAGMENT_SEND_SKIP
* as the OtrlFragmentPolicy. In this case, this function will assign *messagep
@@ -261,19 +289,18 @@ void otrl_message_free(char *message);
* of *messagep, and send that instead.
*
* Other fragmentation policies are OTRL_FRAGMENT_SEND_ALL,
- * OTRL_FRAGMENT_SEND_ALL_BUT_LAST, or OTRL_FRAGMENT_SEND_ALL_BUT_FIRST. In these
- * cases, the appropriate fragments will be automatically sent. For the last two
- * policies, the remaining fragment will be passed in *original_msg.
+ * OTRL_FRAGMENT_SEND_ALL_BUT_LAST, or OTRL_FRAGMENT_SEND_ALL_BUT_FIRST. In
+ * these cases, the appropriate fragments will be automatically sent. For the
+ * last two policies, the remaining fragment will be passed in *original_msg.
*
* Call otrl_message_free(*messagep) if you don't need *messagep or when you're
* done with it. */
gcry_error_t otrl_message_sending(OtrlUserState us,
const OtrlMessageAppOps *ops,
void *opdata, const char *accountname, const char *protocol,
- const char *recipient, char **original_msgp, OtrlTLV *tlvs,
- char **messagep, OtrlFragmentPolicy fragPolicy,
- void (*convert_msg)(void *convert_data, const char *source, char **target),
- void *convert_data,
+ const char *recipient, otrl_instag_t instag, const char *original_msg,
+ OtrlTLV *tlvs, char **messagep, OtrlFragmentPolicy fragPolicy,
+ ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data);
@@ -286,8 +313,11 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
* "context->app" field, for example. If you don't need to do this, you
* can pass NULL for the last two arguments of otrl_message_receiving.
*
- * convert_msg is a function that will be called on each msg that is received.
- * You can use it to perform some tweaks on your incoming messages.
+ * If non-NULL, ops->convert_msg will be called after a data message is
+ * decrypted.
+ *
+ * If "contextp" is not NULL, it will be set to the ConnContext used for
+ * receiving the message.
*
* If otrl_message_receiving returns 1, then the message you received
* was an internal protocol message, and no message should be delivered
@@ -307,18 +337,23 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, const char *accountname, const char *protocol,
const char *sender, const char *message, char **newmessagep,
- OtrlTLV **tlvsp,
- void (*convert_msg)(void *convert_data, const char *source, char **target),
- void *convert_data,
+ OtrlTLV **tlvsp, ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data);
/* 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. */
+ * and we think he's logged in. Affects only the specified instance. */
void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, const char *accountname, const char *protocol,
- const char *username);
+ const char *username, otrl_instag_t instance);
+
+/* 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 all matching instances. */
+void otrl_message_disconnect_all_instances(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, const char *accountname,
+ const char *protocol, const char *username);
/* Initiate the Socialist Millionaires' Protocol */
void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
diff --git a/src/privkey.c b/src/privkey.c
index 6e58f80..2100585 100644
--- a/src/privkey.c
+++ b/src/privkey.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -246,10 +246,10 @@ gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
char *name, *proto;
gcry_sexp_t accounts;
OtrlPrivKey *p;
-
+
/* Get the ith "account" S-exp */
accounts = gcry_sexp_nth(allkeys, i);
-
+
/* It's really an "account" S-exp? */
token = gcry_sexp_nth_data(accounts, 0, &tokenlen);
if (tokenlen != 7 || strncmp(token, "account", 7)) {
@@ -424,7 +424,7 @@ static gcry_error_t sexp_write(FILE *privf, gcry_sexp_t sexp)
return gcry_error(GPG_ERR_ENOMEM);
}
gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, buf, buflen);
-
+
fprintf(privf, "%s", buf);
free(buf);
@@ -500,7 +500,7 @@ gcry_error_t otrl_privkey_generate_start(OtrlUserState us,
gcry_error_t otrl_privkey_generate_calculate(void *newkey)
{
struct s_pending_privkey_calc *ppc =
- (struct s_pending_privkey_calc *)newkey;
+ (struct s_pending_privkey_calc *)newkey;
gcry_error_t err;
gcry_sexp_t key, parms;
static const char *parmstr = "(genkey (dsa (nbits 4:1024)))";
@@ -546,10 +546,10 @@ static FILE* privkey_fopen(const char *filename, gcry_error_t *errp)
/* 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. */
-void otrl_privkey_generate_cancel(OtrlUserState us, void *newkey)
+void otrl_privkey_generate_cancelled(OtrlUserState us, void *newkey)
{
struct s_pending_privkey_calc *ppc =
- (struct s_pending_privkey_calc *)newkey;
+ (struct s_pending_privkey_calc *)newkey;
if (us) {
pending_forget(pending_find(us, ppc->accountname, ppc->protocol));
@@ -586,7 +586,7 @@ gcry_error_t otrl_privkey_generate_finish_FILEp(OtrlUserState us,
void *newkey, FILE *privf)
{
struct s_pending_privkey_calc *ppc =
- (struct s_pending_privkey_calc *)newkey;
+ (struct s_pending_privkey_calc *)newkey;
gcry_error_t ret = gcry_error(GPG_ERR_INV_VALUE);
if (ppc && us && privf) {
@@ -612,7 +612,7 @@ gcry_error_t otrl_privkey_generate_finish_FILEp(OtrlUserState us,
ret = otrl_privkey_read_FILEp(us, privf);
}
- otrl_privkey_generate_cancel(us, newkey);
+ otrl_privkey_generate_cancelled(us, newkey);
return ret;
}
@@ -751,7 +751,7 @@ gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us,
}
/* Get the context for this user, adding if not yet present */
context = otrl_context_find(us, username, accountname, protocol,
- 1, NULL, add_app_data, data);
+ OTRL_INSTAG_MASTER, 1, NULL, add_app_data, data);
/* Add the fingerprint if not already there */
fng = otrl_context_find_fingerprint(context, fingerprint, 1, NULL);
otrl_context_set_trust(fng, trust);
@@ -790,7 +790,10 @@ gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us,
if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
for(context = us->context_root; context; context = context->next) {
- /* Don't both with the first (fingerprintless) entry. */
+ /* Fingerprints are only stored in the master contexts */
+ if (context->their_instance != OTRL_INSTAG_MASTER) continue;
+
+ /* Don't bother with the first (fingerprintless) entry. */
for (fprint = context->fingerprint_root.next; fprint;
fprint = fprint->next) {
int i;
@@ -929,3 +932,4 @@ gcry_error_t otrl_privkey_verify(const unsigned char *sigbuf, size_t siglen,
return err;
}
+
diff --git a/src/privkey.h b/src/privkey.h
index 200168e..a7e6ed8 100644
--- a/src/privkey.h
+++ b/src/privkey.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -80,7 +80,7 @@ gcry_error_t otrl_privkey_generate_finish_FILEp(OtrlUserState us,
/* 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. */
-void otrl_privkey_generate_cancel(OtrlUserState us, void *newkey);
+void otrl_privkey_generate_cancelled(OtrlUserState us, void *newkey);
/* 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
diff --git a/src/proto.c b/src/proto.c
index f5e3980..7fbc83a 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -102,19 +102,23 @@ static gcry_error_t reveal_macs(ConnContext *context,
return gcry_error(GPG_ERR_ENOMEM);
}
if (sess1->rcvmacused) {
- memmove(newmacs + context->context_priv->numsavedkeys * 20, sess1->rcvmackey, 20);
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess1->rcvmackey, 20);
context->context_priv->numsavedkeys++;
}
if (sess1->sendmacused) {
- memmove(newmacs + context->context_priv->numsavedkeys * 20, sess1->sendmackey, 20);
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess1->sendmackey, 20);
context->context_priv->numsavedkeys++;
}
if (sess2->rcvmacused) {
- memmove(newmacs + context->context_priv->numsavedkeys * 20, sess2->rcvmackey, 20);
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess2->rcvmackey, 20);
context->context_priv->numsavedkeys++;
}
if (sess2->sendmacused) {
- memmove(newmacs + context->context_priv->numsavedkeys * 20, sess2->sendmackey, 20);
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess2->sendmackey, 20);
context->context_priv->numsavedkeys++;
}
context->context_priv->saved_mac_keys = newmacs;
@@ -131,8 +135,8 @@ static gcry_error_t rotate_dh_keys(ConnContext *context)
/* Rotate the keypair */
otrl_dh_keypair_free(&(context->context_priv->our_old_dh_key));
memmove(&(context->context_priv->our_old_dh_key),
- &(context->context_priv->our_dh_key),
- sizeof(DH_keypair));
+ &(context->context_priv->our_dh_key),
+ sizeof(DH_keypair));
/* Rotate the session keys */
err = reveal_macs(context, &(context->context_priv->sesskeys[1][0]),
@@ -141,11 +145,11 @@ static gcry_error_t rotate_dh_keys(ConnContext *context)
otrl_dh_session_free(&(context->context_priv->sesskeys[1][0]));
otrl_dh_session_free(&(context->context_priv->sesskeys[1][1]));
memmove(&(context->context_priv->sesskeys[1][0]),
- &(context->context_priv->sesskeys[0][0]),
- sizeof(DH_sesskeys));
+ &(context->context_priv->sesskeys[0][0]),
+ sizeof(DH_sesskeys));
memmove(&(context->context_priv->sesskeys[1][1]),
- &(context->context_priv->sesskeys[0][1]),
- sizeof(DH_sesskeys));
+ &(context->context_priv->sesskeys[0][1]),
+ sizeof(DH_sesskeys));
/* Create a new DH key */
otrl_dh_gen_keypair(DH1536_GROUP_ID, &(context->context_priv->our_dh_key));
@@ -154,7 +158,8 @@ static gcry_error_t rotate_dh_keys(ConnContext *context)
/* Make the session keys */
if (context->context_priv->their_y) {
err = otrl_dh_session(&(context->context_priv->sesskeys[0][0]),
- &(context->context_priv->our_dh_key), context->context_priv->their_y);
+ &(context->context_priv->our_dh_key),
+ context->context_priv->their_y);
if (err) return err;
} else {
otrl_dh_session_blank(&(context->context_priv->sesskeys[0][0]));
@@ -187,11 +192,11 @@ static gcry_error_t rotate_y_keys(ConnContext *context, gcry_mpi_t new_y)
otrl_dh_session_free(&(context->context_priv->sesskeys[0][1]));
otrl_dh_session_free(&(context->context_priv->sesskeys[1][1]));
memmove(&(context->context_priv->sesskeys[0][1]),
- &(context->context_priv->sesskeys[0][0]),
- sizeof(DH_sesskeys));
+ &(context->context_priv->sesskeys[0][0]),
+ sizeof(DH_sesskeys));
memmove(&(context->context_priv->sesskeys[1][1]),
- &(context->context_priv->sesskeys[1][0]),
- sizeof(DH_sesskeys));
+ &(context->context_priv->sesskeys[1][0]),
+ sizeof(DH_sesskeys));
/* Copy in the new public key */
context->context_priv->their_y = gcry_mpi_copy(new_y);
@@ -199,10 +204,12 @@ static gcry_error_t rotate_y_keys(ConnContext *context, gcry_mpi_t new_y)
/* Make the session keys */
err = otrl_dh_session(&(context->context_priv->sesskeys[0][0]),
- &(context->context_priv->our_dh_key), context->context_priv->their_y);
+ &(context->context_priv->our_dh_key),
+ context->context_priv->their_y);
if (err) return err;
err = otrl_dh_session(&(context->context_priv->sesskeys[1][0]),
- &(context->context_priv->our_old_dh_key), context->context_priv->their_y);
+ &(context->context_priv->our_old_dh_key),
+ context->context_priv->their_y);
if (err) return err;
return gcry_error(GPG_ERR_NO_ERROR);
@@ -214,8 +221,9 @@ static gcry_error_t rotate_y_keys(ConnContext *context, gcry_mpi_t new_y)
char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy)
{
char *msg;
- int v1_supported, v2_supported;
- const char *version_tag;
+ int v1_supported, v2_supported, v3_supported;
+ char *version_tag;
+ char *bufp;
/* Don't use g_strdup_printf here, because someone (not us) is going
* to free() the *message pointer, not g_free() it. We can't
* require that they g_free() it, because this pointer will probably
@@ -230,51 +238,73 @@ char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy)
/* Figure out the version tag */
v1_supported = (policy & OTRL_POLICY_ALLOW_V1);
v2_supported = (policy & OTRL_POLICY_ALLOW_V2);
+ v3_supported = (policy & OTRL_POLICY_ALLOW_V3);
+ version_tag = malloc(8);
+ bufp = version_tag;
if (v1_supported) {
+ *bufp = '?';
+ bufp++;
+ }
+ if (v2_supported || v3_supported) {
+ *bufp = 'v';
+ bufp++;
if (v2_supported) {
- version_tag = "?v2?";
- } else {
- version_tag = "?";
+ *bufp = '2';
+ bufp++;
}
- } else {
- if (v2_supported) {
- version_tag = "v2?";
- } else {
- version_tag = "v?";
+ if (v3_supported) {
+ *bufp = '3';
+ bufp++;
}
+ *bufp = '?';
+ bufp++;
}
+ *bufp = '\0';
/* Remove two "%s", add '\0' */
msg = malloc(strlen(format) + strlen(version_tag) + strlen(ourname) - 3);
- if (!msg) return NULL;
+ if (!msg) {
+ free(version_tag);
+ return NULL;
+ }
sprintf(msg, format, version_tag, ourname);
+ free(version_tag);
return msg;
}
/* Return the best version of OTR support by both sides, given an OTR
* Query Message and the local policy. */
-unsigned int otrl_proto_query_bestversion(const char *querymsg,
+unsigned int otrl_proto_query_bestversion(const char *otrquerymsg,
OtrlPolicy policy)
{
char *otrtag;
unsigned int query_versions = 0;
+ unsigned int query_position = 0;
- otrtag = strstr(querymsg, "?OTR");
+
+ otrtag = strstr(otrquerymsg, "?OTR");
otrtag += 4;
- if (*otrtag == '?') {
+
+ if (otrtag && *otrtag == '?') {
query_versions = (1<<0);
++otrtag;
}
- if (*otrtag == 'v') {
+ if (otrtag && *otrtag == 'v') {
for(++otrtag; *otrtag && *otrtag != '?'; ++otrtag) {
switch(*otrtag) {
case '2':
query_versions |= (1<<1);
break;
+ case '3':
+ query_versions |= (1<<2);
+ break;
}
}
}
+ if ((policy & OTRL_POLICY_ALLOW_V3) && (query_versions & (1<<2))) {
+ return 3;
+ }
if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
return 2;
}
@@ -318,6 +348,9 @@ unsigned int otrl_proto_whitespace_bestversion(const char *msg,
if (!strncmp(endtag, OTRL_MESSAGE_TAG_V2, 8)) {
query_versions |= (1<<1);
}
+ if (!strncmp(endtag, OTRL_MESSAGE_TAG_V3, 8)) {
+ query_versions |= (1<<2);
+ }
endtag += 8;
} else {
break;
@@ -327,6 +360,9 @@ unsigned int otrl_proto_whitespace_bestversion(const char *msg,
*starttagp = starttag;
*endtagp = endtag;
+ if ((policy & OTRL_POLICY_ALLOW_V3) && (query_versions & (1<<2))) {
+ return 3;
+ }
if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
return 2;
}
@@ -336,7 +372,7 @@ unsigned int otrl_proto_whitespace_bestversion(const char *msg,
return 0;
}
-/* Return the Message type of the given message. */
+/* Find the message type. */
OtrlMessageType otrl_proto_message_type(const char *message)
{
char *otrtag;
@@ -351,23 +387,78 @@ OtrlMessageType otrl_proto_message_type(const char *message)
}
}
- if (!strncmp(otrtag, "?OTR?", 5)) return OTRL_MSGTYPE_QUERY;
- if (!strncmp(otrtag, "?OTRv", 5)) return OTRL_MSGTYPE_QUERY;
- if (!strncmp(otrtag, "?OTR:AAIC", 9)) return OTRL_MSGTYPE_DH_COMMIT;
- if (!strncmp(otrtag, "?OTR:AAIK", 9)) return OTRL_MSGTYPE_DH_KEY;
- if (!strncmp(otrtag, "?OTR:AAIR", 9)) return OTRL_MSGTYPE_REVEALSIG;
- if (!strncmp(otrtag, "?OTR:AAIS", 9)) return OTRL_MSGTYPE_SIGNATURE;
- if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTRL_MSGTYPE_V1_KEYEXCH;
- if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTRL_MSGTYPE_DATA;
- if (!strncmp(otrtag, "?OTR:AAID", 9)) return OTRL_MSGTYPE_DATA;
- if (!strncmp(otrtag, "?OTR Error:", 11)) return OTRL_MSGTYPE_ERROR;
-
+ if (!strncmp(otrtag, "?OTR:AAM", 8) || !strncmp(otrtag, "?OTR:AAI", 8)) {
+ switch(*(otrtag + 8)) {
+ case 'C': return OTRL_MSGTYPE_DH_COMMIT;
+ case 'K': return OTRL_MSGTYPE_DH_KEY;
+ case 'R': return OTRL_MSGTYPE_REVEALSIG;
+ case 'S': return OTRL_MSGTYPE_SIGNATURE;
+ case 'D': return OTRL_MSGTYPE_DATA;
+ }
+ } else {
+ if (!strncmp(otrtag, "?OTR?", 5)) return OTRL_MSGTYPE_QUERY;
+ if (!strncmp(otrtag, "?OTRv", 5)) return OTRL_MSGTYPE_QUERY;
+ if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTRL_MSGTYPE_V1_KEYEXCH;
+ if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTRL_MSGTYPE_DATA;
+ if (!strncmp(otrtag, "?OTR Error:", 11)) return OTRL_MSGTYPE_ERROR;
+ }
return OTRL_MSGTYPE_UNKNOWN;
}
+/* Find the message version. */
+int otrl_proto_message_version(const char *message)
+{
+ char *otrtag;
+
+ otrtag = strstr(message, "?OTR");
+
+ if (!otrtag) {
+ return 0;
+ }
+
+ if (!strncmp(otrtag, "?OTR:AAM", 8))
+ return 3;
+ if (!strncmp(otrtag, "?OTR:AAI", 8))
+ return 2;
+ if (!strncmp(otrtag, "?OTR:AAE", 8))
+ return 1;
+
+ return 0;
+}
+
+/* Find the instance tags in this message */
+gcry_error_t otrl_proto_instance(const char *otrmsg,
+ unsigned int *instance_from, unsigned int *instance_to)
+{
+ gcry_error_t err;
+
+ const char *otrtag = otrmsg;
+ unsigned char *bufp = NULL;
+ unsigned char *bufp_head = NULL;
+ size_t lenp;
+
+ if (strncmp(otrtag, "?OTR:AAM", 8)) {
+ goto invval;
+ }
+
+ if (strlen(otrtag) < 21 ) return err;
+
+ /* Decode and extract instance tag */
+ bufp = malloc(9);
+ bufp_head = bufp;
+ lenp = otrl_base64_decode(bufp, otrtag+9, 12);
+ read_int(*instance_from);
+ read_int(*instance_to);
+ free(bufp_head);
+ return gcry_error(GPG_ERR_NO_ERROR);
+ invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ return err;
+}
+
/* Create an OTR Data message. Pass the plaintext as msg, and an
* optional chain of TLVs. A newly-allocated string will be returned in
- * *encmessagep. Put the current extra symmetric key into extrakey
+ * *encmessagep. Put the current extra symmetric key into extrakey
* (if non-NULL). */
gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
const char *msg, const OtrlTLV *tlvs, unsigned char flags,
@@ -406,12 +497,13 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
*encmessagep = NULL;
- /* Header, send keyid, recv keyid, counter, msg len, msg
+ /* Header, msg flags, send keyid, recv keyid, counter, msg len, msg
* len of revealed mac keys, revealed mac keys, MAC */
- buflen = 3 + (version == 2 ? 1 : 0) + 4 + 4 + 8 + 4 + msglen +
- 4 + reveallen + 20;
+ buflen = OTRL_HEADER_LEN + (version == 3 ? 8 : 0)
+ + (version == 2 || version == 3 ? 1 : 0) + 4 + 4
+ + 8 + 4 + msglen + 4 + reveallen + 20;
gcry_mpi_print(format, NULL, 0, &pubkeylen,
- context->context_priv->our_dh_key.pub);
+ context->context_priv->our_dh_key.pub);
buflen += pubkeylen + 4;
buf = malloc(buflen);
msgbuf = gcry_malloc_secure(msglen);
@@ -428,18 +520,31 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
lenp = buflen;
if (version == 1) {
memmove(bufp, "\x00\x01\x03", 3); /* header */
- } else {
+ } else if (version == 2) {
memmove(bufp, "\x00\x02\x03", 3); /* header */
+ } else {
+ memmove(bufp, "\x00\x03\x03", 3); /* header */
}
+
debug_data("Header", bufp, 3);
bufp += 3; lenp -= 3;
- if (version == 2) {
+
+ if (version == 3) {
+ /* v3 instance tags */
+ write_int(context->our_instance);
+ debug_int("Sender instag", bufp-4);
+ write_int(context->their_instance);
+ debug_int("Recipient instag", bufp-4);
+ }
+
+ if (version == 2 || version == 3) {
bufp[0] = flags;
bufp += 1; lenp -= 1;
}
- write_int(context->context_priv->our_keyid-1); /* sender keyid */
+
+ write_int(context->context_priv->our_keyid-1); /* sender keyid */
debug_int("Sender keyid", bufp-4);
- write_int(context->context_priv->their_keyid); /* recipient keyid */
+ write_int(context->context_priv->their_keyid); /* recipient keyid */
debug_int("Recipient keyid", bufp-4);
write_mpi(context->context_priv->our_dh_key.pub, pubkeylen, "Y"); /* Y */
@@ -557,14 +662,15 @@ gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
lenp = rawlen;
require_len(3);
- if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) {
- /* Invalid header */
- goto invval;
- }
version = bufp[1];
- bufp += 3; lenp -= 3;
+ skip_header('\x03');
- if (version == 2) {
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
+
+ if (version == 2 || version == 3) {
require_len(1);
if (flagsp) *flagsp = bufp[0];
bufp += 1; lenp -= 1;
@@ -630,18 +736,21 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
macstart = bufp;
require_len(3);
- if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) {
- /* Invalid header */
- goto invval;
- }
version = bufp[1];
- bufp += 3; lenp -= 3;
- if (version == 2) {
+ skip_header('\x03');
+
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
+
+ if (version == 2 || version == 3) {
require_len(1);
if (flagsp) *flagsp = bufp[0];
bufp += 1; lenp -= 1;
}
+
read_int(sender_keyid);
read_int(recipient_keyid);
read_mpi(sender_next_y);
@@ -767,55 +876,63 @@ OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
{
OtrlFragmentResult res = OTRL_FRAGMENT_INCOMPLETE;
const char *tag;
+ unsigned short n = 0, k = 0;
+ int start = 0, end = 0;
- tag = strstr(msg, "?OTR,");
+ tag = strstr(msg, "?OTR|");
if (tag) {
- unsigned short n = 0, k = 0;
- int start = 0, end = 0;
-
+ sscanf(tag, "?OTR|%*x|%*x,%hu,%hu,%n%*[^,],%n", &k, &n, &start, &end);
+ } else if (tag = strstr(msg, "?OTR,")) {
sscanf(tag, "?OTR,%hu,%hu,%n%*[^,],%n", &k, &n, &start, &end);
- if (k > 0 && n > 0 && k <= n && start > 0 && end > 0 && start < end) {
- if (k == 1) {
- int fraglen = end - start - 1;
- size_t newsize = fraglen + 1;
- free(context->context_priv->fragment);
- context->context_priv->fragment = NULL;
- if (newsize > fraglen) { /* Check for overflow */
- context->context_priv->fragment = malloc(newsize);
- }
- if (context->context_priv->fragment) {
- memmove(context->context_priv->fragment, tag + start, fraglen);
- context->context_priv->fragment_len = fraglen;
- context->context_priv->fragment[context->context_priv->fragment_len] = '\0';
- context->context_priv->fragment_n = n;
- context->context_priv->fragment_k = k;
- } else {
- context->context_priv->fragment_len = 0;
- context->context_priv->fragment_n = 0;
- context->context_priv->fragment_k = 0;
- }
- } else if (n == context->context_priv->fragment_n &&
- k == context->context_priv->fragment_k + 1) {
- int fraglen = end - start - 1;
- char *newfrag = NULL;
- size_t newsize = context->context_priv->fragment_len + fraglen + 1;
- if (newsize > fraglen) { /* Check for overflow */
- newfrag = realloc(context->context_priv->fragment, newsize);
- }
- if (newfrag) {
- context->context_priv->fragment = newfrag;
- memmove(context->context_priv->fragment + context->context_priv->fragment_len,
- tag + start, fraglen);
- context->context_priv->fragment_len += fraglen;
- context->context_priv->fragment[context->context_priv->fragment_len] = '\0';
- context->context_priv->fragment_k = k;
- } else {
- free(context->context_priv->fragment);
- context->context_priv->fragment = NULL;
- context->context_priv->fragment_len = 0;
- context->context_priv->fragment_n = 0;
- context->context_priv->fragment_k = 0;
- }
+ } else {
+ /* Unfragmented message, so discard any fragment we may have */
+ free(context->context_priv->fragment);
+ context->context_priv->fragment = NULL;
+ context->context_priv->fragment_len = 0;
+ context->context_priv->fragment_n = 0;
+ context->context_priv->fragment_k = 0;
+ res = OTRL_FRAGMENT_UNFRAGMENTED;
+ return res;
+ }
+
+ if (k > 0 && n > 0 && k <= n && start > 0 && end > 0 && start < end) {
+ if (k == 1) {
+ int fraglen = end - start - 1;
+ size_t newsize = fraglen + 1;
+ free(context->context_priv->fragment);
+ context->context_priv->fragment = NULL;
+ if (newsize > fraglen) { /* Check for overflow */
+ context->context_priv->fragment = malloc(newsize);
+ }
+ if (context->context_priv->fragment) {
+ memmove(context->context_priv->fragment, tag + start, fraglen);
+ context->context_priv->fragment_len = fraglen;
+ context->context_priv->fragment[
+ context->context_priv->fragment_len] = '\0';
+ context->context_priv->fragment_n = n;
+ context->context_priv->fragment_k = k;
+ } else {
+ context->context_priv->fragment_len = 0;
+ context->context_priv->fragment_n = 0;
+ context->context_priv->fragment_k = 0;
+ }
+ } else if (n == context->context_priv->fragment_n &&
+ k == context->context_priv->fragment_k + 1) {
+ int fraglen = end - start - 1;
+ char *newfrag = NULL;
+ size_t newsize = context->context_priv->fragment_len + fraglen + 1;
+ if (newsize > fraglen) { /* Check for overflow */
+ newfrag = realloc(context->context_priv->fragment, newsize);
+ }
+ if (newfrag) {
+ context->context_priv->fragment = newfrag;
+ memmove(context->context_priv->fragment +
+ context->context_priv->fragment_len,
+ tag + start, fraglen);
+ context->context_priv->fragment_len += fraglen;
+ context->context_priv->fragment[
+ context->context_priv->fragment_len] = '\0';
+ context->context_priv->fragment_k = k;
} else {
free(context->context_priv->fragment);
context->context_priv->fragment = NULL;
@@ -823,26 +940,25 @@ OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
context->context_priv->fragment_n = 0;
context->context_priv->fragment_k = 0;
}
- }
-
- if (context->context_priv->fragment_n > 0 &&
- context->context_priv->fragment_n == context->context_priv->fragment_k) {
- /* We've got a complete message */
- *unfragmessagep = context->context_priv->fragment;
+ } else {
+ free(context->context_priv->fragment);
context->context_priv->fragment = NULL;
context->context_priv->fragment_len = 0;
context->context_priv->fragment_n = 0;
context->context_priv->fragment_k = 0;
- res = OTRL_FRAGMENT_COMPLETE;
}
- } else {
- /* Unfragmented message, so discard any fragment we may have */
- free(context->context_priv->fragment);
+ }
+
+ if (context->context_priv->fragment_n > 0 &&
+ context->context_priv->fragment_n ==
+ context->context_priv->fragment_k) {
+ /* We've got a complete message */
+ *unfragmessagep = context->context_priv->fragment;
context->context_priv->fragment = NULL;
context->context_priv->fragment_len = 0;
context->context_priv->fragment_n = 0;
context->context_priv->fragment_k = 0;
- res = OTRL_FRAGMENT_UNFRAGMENTED;
+ res = OTRL_FRAGMENT_COMPLETE;
}
return res;
@@ -850,14 +966,15 @@ OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
/* Create a fragmented message. */
gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
- char ***fragments, const char *message)
+ char ***fragments, ConnContext *context, const char *message)
{
char *fragdata;
int fragdatalen = 0;
unsigned short curfrag = 0;
int index = 0;
int msglen = strlen(message);
- int headerlen = 19; /* Should vary by number of msgs */
+ /* Should vary by number of msgs */
+ int headerlen = context->protocol_version == 3 ? 37 : 19;
char **fragmentarray = malloc(fragment_count * sizeof(char*));
if(!fragmentarray) return gcry_error(GPG_ERR_ENOMEM);
@@ -867,40 +984,50 @@ gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
*/
for(curfrag = 1; curfrag <= fragment_count; curfrag++) {
int i;
- char *fragmentmsg;
+ char *fragmentmsg;
if (msglen - index < mms - headerlen) {
- fragdatalen = msglen - index;
+ fragdatalen = msglen - index;
} else {
fragdatalen = mms - headerlen;
}
+
fragdata = malloc(fragdatalen + 1);
- if(!fragdata) {
+ if(!fragdata) {
for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
- free(fragmentarray);
- return gcry_error(GPG_ERR_ENOMEM);
- }
- strncpy(fragdata, message, fragdatalen);
- fragdata[fragdatalen] = 0;
-
- fragmentmsg = malloc(fragdatalen+headerlen+1);
- if(!fragmentmsg) {
+ free(fragmentarray);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ strncpy(fragdata, message, fragdatalen);
+ fragdata[fragdatalen] = 0;
+
+ fragmentmsg = malloc(fragdatalen+headerlen+1);
+ if(!fragmentmsg) {
for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
- free(fragmentarray);
- free(fragdata);
- return gcry_error(GPG_ERR_ENOMEM);
- }
+ free(fragmentarray);
+ free(fragdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
- /*
- * Create the actual fragment and store it in the array
- */
- snprintf(fragmentmsg, fragdatalen + headerlen, "?OTR,%05hu,%05hu,%s,", curfrag, fragment_count, fragdata);
- fragmentmsg[fragdatalen + headerlen] = 0;
+ /*
+ * Create the actual fragment and store it in the array
+ */
+ if (context->auth.protocol_version != 3) {
+ snprintf(fragmentmsg, fragdatalen + headerlen,
+ "?OTR,%05hu,%05hu,%s,", curfrag, fragment_count, fragdata);
+ } else {
+ /* V3 messages require instance tags in the header */
+ snprintf(fragmentmsg, fragdatalen + headerlen,
+ "?OTR|%08x|%08x,%05hu,%05hu,%s,",
+ context->our_instance, context->their_instance, curfrag,
+ fragment_count, fragdata);
+ }
+ fragmentmsg[fragdatalen + headerlen] = 0;
- fragmentarray[curfrag-1] = fragmentmsg;
+ fragmentarray[curfrag-1] = fragmentmsg;
- free(fragdata);
- index += fragdatalen;
+ free(fragdata);
+ index += fragdatalen;
message += fragdatalen;
}
@@ -917,7 +1044,7 @@ void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen)
for(i = 0; i < arraylen; i++)
{
if(fragmentarray[i]) {
- free(fragmentarray[i]);
+ free(fragmentarray[i]);
}
}
free(fragmentarray);
diff --git a/src/proto.h b/src/proto.h
index afd74fe..7f98999 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -31,6 +31,7 @@
/* The following must each be of length 8 */
#define OTRL_MESSAGE_TAG_V1 " \t \t \t "
#define OTRL_MESSAGE_TAG_V2 " \t\t \t "
+#define OTRL_MESSAGE_TAG_V3 " \t\t \t\t"
/* The possible flags contained in a Data Message */
#define OTRL_MSGFLAGS_IGNORE_UNREADABLE 0x01
@@ -39,27 +40,36 @@ typedef unsigned int OtrlPolicy;
#define OTRL_POLICY_ALLOW_V1 0x01
#define OTRL_POLICY_ALLOW_V2 0x02
-#define OTRL_POLICY_REQUIRE_ENCRYPTION 0x04
-#define OTRL_POLICY_SEND_WHITESPACE_TAG 0x08
-#define OTRL_POLICY_WHITESPACE_START_AKE 0x10
-#define OTRL_POLICY_ERROR_START_AKE 0x20
+#define OTRL_POLICY_ALLOW_V3 0x04
+#define OTRL_POLICY_REQUIRE_ENCRYPTION 0x08
+#define OTRL_POLICY_SEND_WHITESPACE_TAG 0x10
+#define OTRL_POLICY_WHITESPACE_START_AKE 0x20
+#define OTRL_POLICY_ERROR_START_AKE 0x40
-#define OTRL_POLICY_VERSION_MASK (OTRL_POLICY_ALLOW_V1 | OTRL_POLICY_ALLOW_V2)
+#define OTRL_POLICY_VERSION_MASK (OTRL_POLICY_ALLOW_V1 | OTRL_POLICY_ALLOW_V2 |\
+ OTRL_POLICY_ALLOW_V3)
+
+/* Length of OTR message headers */
+#define OTRL_HEADER_LEN 3
+#define OTRL_B64_HEADER_LEN 4
/* For v1 compatibility */
#define OTRL_POLICY_NEVER 0x00
#define OTRL_POLICY_OPPORTUNISTIC \
( OTRL_POLICY_ALLOW_V1 | \
OTRL_POLICY_ALLOW_V2 | \
+ OTRL_POLICY_ALLOW_V3 | \
OTRL_POLICY_SEND_WHITESPACE_TAG | \
OTRL_POLICY_WHITESPACE_START_AKE | \
OTRL_POLICY_ERROR_START_AKE )
#define OTRL_POLICY_MANUAL \
( OTRL_POLICY_ALLOW_V1 | \
- OTRL_POLICY_ALLOW_V2 )
+ OTRL_POLICY_ALLOW_V2 | \
+ OTRL_POLICY_ALLOW_V3)
#define OTRL_POLICY_ALWAYS \
( OTRL_POLICY_ALLOW_V1 | \
OTRL_POLICY_ALLOW_V2 | \
+ OTRL_POLICY_ALLOW_V3 | \
OTRL_POLICY_REQUIRE_ENCRYPTION | \
OTRL_POLICY_WHITESPACE_START_AKE | \
OTRL_POLICY_ERROR_START_AKE )
@@ -86,10 +96,11 @@ typedef enum {
} OtrlFragmentResult;
typedef enum {
+ OTRL_FRAGMENT_SEND_SKIP, /* Return new message back to caller,
+ * but don't inject. */
OTRL_FRAGMENT_SEND_ALL,
OTRL_FRAGMENT_SEND_ALL_BUT_FIRST,
- OTRL_FRAGMENT_SEND_ALL_BUT_LAST,
- OTRL_FRAGMENT_SEND_SKIP
+ OTRL_FRAGMENT_SEND_ALL_BUT_LAST
} OtrlFragmentPolicy;
/* Initialize the OTR library. Pass the version of the API you are
@@ -122,12 +133,20 @@ unsigned int otrl_proto_query_bestversion(const char *querymsg,
unsigned int otrl_proto_whitespace_bestversion(const char *msg,
const char **starttagp, const char **endtagp, OtrlPolicy policy);
-/* Return the Message type of the given message. */
+/* Find the message type. */
OtrlMessageType otrl_proto_message_type(const char *message);
+/* Find the message version. */
+int otrl_proto_message_version(const char *message);
+
+/* Find the instance tags in this message. */
+gcry_error_t otrl_proto_instance(const char *otrmsg,
+ unsigned int *instance_from, unsigned int *instance_to);
+
/* Create an OTR Data message. Pass the plaintext as msg, and an
* optional chain of TLVs. A newly-allocated string will be returned in
- * *encmessagep. */
+ * *encmessagep. Put the current extra symmetric key into extrakey
+ * (if non-NULL). */
gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
const char *msg, const OtrlTLV *tlvs, unsigned char flags,
unsigned char *extrakey);
@@ -149,7 +168,7 @@ OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
ConnContext *context, const char *msg);
gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
- char ***fragments, const char *message);
+ char ***fragments, ConnContext *context, const char *message);
void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen);
#endif
diff --git a/src/serial.h b/src/serial.h
index 439a271..ed8fef9 100644
--- a/src/serial.h
+++ b/src/serial.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -83,4 +83,25 @@
bufp += mpilen; lenp -= mpilen; \
} while(0)
+/* Write version and msg type into bufp*/
+#define write_header(version, msgtype) do { \
+ bufp[0] = 0x00; \
+ bufp[1] = version & 0xff; \
+ bufp[2] = msgtype; \
+ debug_data("Header", bufp, 3); \
+ bufp += 3; lenp -= 3; \
+ } while(0)
+
+/* Verify msg header is v1, v2 or v3 and has type x,
+* increment bufp past msg header */
+#define skip_header(x) do { \
+ require_len(3); \
+ if ((bufp[0] != 0x00) || (bufp[2] != x)) \
+ goto invval; \
+ if ((bufp[1] == 0x01) || (bufp[1] == 0x02) || \
+ (bufp[1] == 0x03)) { \
+ bufp += 3; lenp -= 3; \
+ } else goto invval; \
+ } while(0)
+
#endif
diff --git a/src/sm.c b/src/sm.c
index f3bcbd0..fd8588a 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -21,7 +21,7 @@
/* system headers */
#include <stdlib.h>
#include <stdio.h>
-#include <sys/types.h>
+#include <sys/types.h>
/* libgcrypt headers */
#include <gcrypt.h>
@@ -157,7 +157,7 @@ void otrl_sm_msg2_init(gcry_mpi_t **msg2)
msg[7] = gcry_mpi_new(SM_MOD_LEN_BITS);
msg[8] = NULL;
msg[9] = gcry_mpi_new(SM_MOD_LEN_BITS);
- msg[10] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[10] = gcry_mpi_new(SM_MOD_LEN_BITS);
*msg2 = msg;
}
@@ -224,7 +224,7 @@ void otrl_sm_msg_free(gcry_mpi_t **message, int msglen)
gcry_mpi_t *msg = *message;
int i;
for (i=0; i<msglen; i++) {
- gcry_mpi_release(msg[i]);
+ gcry_mpi_release(msg[i]);
}
free(msg);
*message = NULL;
@@ -256,7 +256,7 @@ static gcry_error_t otrl_sm_hash(gcry_mpi_t* hash, int version,
size_t totalsize;
unsigned char* dataa;
unsigned char* datab;
-
+
gcry_mpi_aprint(GCRYMPI_FMT_USG, &dataa, &sizea, a);
totalsize = 1 + 4 + sizea;
if (b) {
@@ -307,9 +307,9 @@ static gcry_error_t serialize_mpi_array(unsigned char **buffer, int *buflen,
unsigned char *bufp;
for (i=0; i<count; i++) {
- gcry_mpi_aprint(GCRYMPI_FMT_USG, &(tempbuffer[i]), &(list_sizes[i]),
+ gcry_mpi_aprint(GCRYMPI_FMT_USG, &(tempbuffer[i]), &(list_sizes[i]),
mpis[i]);
- totalsize += list_sizes[i];
+ totalsize += list_sizes[i];
}
*buflen = (count+1)*4 + totalsize;
@@ -321,15 +321,15 @@ static gcry_error_t serialize_mpi_array(unsigned char **buffer, int *buflen,
write_int(count);
for(i=0; i<count; i++)
{
- nextsize = list_sizes[i];
- write_int(nextsize);
-
- for(j=0; j<nextsize; j++)
- bufp[j] = tempbuffer[i][j];
-
+ nextsize = list_sizes[i];
+ write_int(nextsize);
+
+ for(j=0; j<nextsize; j++)
+ bufp[j] = tempbuffer[i][j];
+
bufp += nextsize;
- lenp -= nextsize;
- gcry_free(tempbuffer[i]);
+ lenp -= nextsize;
+ gcry_free(tempbuffer[i]);
}
free(tempbuffer);
free(list_sizes);
@@ -401,7 +401,8 @@ static int check_expon(gcry_mpi_t x)
/*
* Proof of knowledge of a discrete logarithm
*/
-static gcry_error_t otrl_sm_proof_know_log(gcry_mpi_t *c, gcry_mpi_t *d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+static gcry_error_t otrl_sm_proof_know_log(gcry_mpi_t *c, gcry_mpi_t *d,
+ const gcry_mpi_t g, const gcry_mpi_t x, int version)
{
gcry_mpi_t r = randomExponent();
gcry_mpi_t temp = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -416,9 +417,11 @@ static gcry_error_t otrl_sm_proof_know_log(gcry_mpi_t *c, gcry_mpi_t *d, const g
}
/*
- * Verify a proof of knowledge of a discrete logarithm. Checks that c = h(g^d x^c)
+ * Verify a proof of knowledge of a discrete logarithm.
+ * Checks that c = h(g^d x^c)
*/
-static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d,
+ const gcry_mpi_t g, const gcry_mpi_t x, int version)
{
int comp;
@@ -431,7 +434,7 @@ static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d, const
gcry_mpi_powm(xc, x, c, SM_MODULUS);
gcry_mpi_mulm(gdxc, gd, xc, SM_MODULUS);
otrl_sm_hash(&hgdxc, version, gdxc, NULL);
-
+
comp = gcry_mpi_cmp(hgdxc, c);
gcry_mpi_release(gd);
gcry_mpi_release(xc);
@@ -444,7 +447,9 @@ static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d, const
/*
* Proof of knowledge of coordinates with first components being equal
*/
-static gcry_error_t otrl_sm_proof_equal_coords(gcry_mpi_t *c, gcry_mpi_t *d1, gcry_mpi_t *d2, const OtrlSMState *state, const gcry_mpi_t r, int version)
+static gcry_error_t otrl_sm_proof_equal_coords(gcry_mpi_t *c, gcry_mpi_t *d1,
+ gcry_mpi_t *d2, const OtrlSMState *state, const gcry_mpi_t r,
+ int version)
{
gcry_mpi_t r1 = randomExponent();
gcry_mpi_t r2 = randomExponent();
@@ -476,7 +481,9 @@ static gcry_error_t otrl_sm_proof_equal_coords(gcry_mpi_t *c, gcry_mpi_t *d1, gc
/*
* Verify a proof of knowledge of coordinates with first components being equal
*/
-static gcry_error_t otrl_sm_check_equal_coords(const gcry_mpi_t c, const gcry_mpi_t d1, const gcry_mpi_t d2, const gcry_mpi_t p, const gcry_mpi_t q, const OtrlSMState *state, int version)
+static gcry_error_t otrl_sm_check_equal_coords(const gcry_mpi_t c,
+ const gcry_mpi_t d1, const gcry_mpi_t d2, const gcry_mpi_t p,
+ const gcry_mpi_t q, const OtrlSMState *state, int version)
{
int comp;
@@ -519,7 +526,8 @@ static gcry_error_t otrl_sm_check_equal_coords(const gcry_mpi_t c, const gcry_mp
/*
* Proof of knowledge of logs with exponents being equal
*/
-static gcry_error_t otrl_sm_proof_equal_logs(gcry_mpi_t *c, gcry_mpi_t *d, OtrlSMState *state, int version)
+static gcry_error_t otrl_sm_proof_equal_logs(gcry_mpi_t *c, gcry_mpi_t *d,
+ OtrlSMState *state, int version)
{
gcry_mpi_t r = randomExponent();
gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -544,7 +552,9 @@ static gcry_error_t otrl_sm_proof_equal_logs(gcry_mpi_t *c, gcry_mpi_t *d, OtrlS
/*
* Verify a proof of knowledge of logs with exponents being equal
*/
-static gcry_error_t otrl_sm_check_equal_logs(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t r, const OtrlSMState *state, int version)
+static gcry_error_t otrl_sm_check_equal_logs(const gcry_mpi_t c,
+ const gcry_mpi_t d, const gcry_mpi_t r, const OtrlSMState *state,
+ int version)
{
int comp;
@@ -556,7 +566,7 @@ static gcry_error_t otrl_sm_check_equal_logs(const gcry_mpi_t c, const gcry_mpi_
/* Here, we recall the exponents used to create g3.
* If we have previously seen g3o = g1^x where x is unknown
* during the DH exchange to produce g3, then we may proceed with:
- *
+ *
* To verify, we test that hash(g1^d * g3o^c, qab^d * r^c) = c
* If indeed c = hash(g1^r1, qab^r1), d = r1- x * c
* And if indeed r = qab^x
@@ -602,11 +612,11 @@ gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate,
*output = NULL;
*outputlen = 0;
-
+
gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
if (! astate->g1) {
- otrl_sm_state_init(astate);
+ otrl_sm_state_init(astate);
}
gcry_mpi_set(astate->secret, secret_mpi);
gcry_mpi_release(secret_mpi);
@@ -632,7 +642,8 @@ gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate,
/* Receive the first message in SMP exchange, which was generated by
* otrl_sm_step1. Input is saved until the user inputs their secret
* information. No output. */
-gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question)
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input,
+ const int inputlen, int received_question)
{
gcry_mpi_t *msg1;
gcry_error_t err;
@@ -646,21 +657,21 @@ gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input,
/* Read from input to find the mpis */
err = unserialize_mpi_array(&msg1, SM_MSG1_LEN, input, inputlen);
-
+
if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
if (check_group_elem(msg1[0]) || check_expon(msg1[2]) ||
check_group_elem(msg1[3]) || check_expon(msg1[5])) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Store Alice's g3a value for later in the protocol */
gcry_mpi_set(bstate->g3o, msg1[3]);
-
+
/* Verify Alice's proofs */
- if (otrl_sm_check_know_log(msg1[1], msg1[2], bstate->g1, msg1[0], 1) ||
- otrl_sm_check_know_log(msg1[4], msg1[5], bstate->g1, msg1[3], 2)) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ if (otrl_sm_check_know_log(msg1[1], msg1[2], bstate->g1, msg1[0], 1) ||
+ otrl_sm_check_know_log(msg1[4], msg1[5], bstate->g1, msg1[3], 2)) {
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Create Bob's half of the generators g2 and g3 */
@@ -685,7 +696,8 @@ gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input,
* [4] = c3, [5] = d3, Bob's ZK proof of knowledge of g3b exponent
* [6] = pb, [7] = qb, Bob's halves of the (Pa/Pb) and (Qa/Qb) values
* [8] = cp, [9] = d5, [10] = d6, Bob's ZK proof that pb, qb formed correctly */
-gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen)
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret,
+ int secretlen, unsigned char **output, int* outputlen)
{
/* Convert the given secret to the proper form and store it */
gcry_mpi_t r, qb1, qb2;
@@ -694,7 +706,7 @@ gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret,
*output = NULL;
*outputlen = 0;
-
+
gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
gcry_mpi_set(bstate->secret, secret_mpi);
gcry_mpi_release(secret_mpi);
@@ -718,7 +730,8 @@ gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret,
gcry_mpi_mulm(bstate->q, qb1, qb2, SM_MODULUS);
gcry_mpi_set(msg2[7], bstate->q);
- otrl_sm_proof_equal_coords(&(msg2[8]), &(msg2[9]), &(msg2[10]), bstate, r, 5);
+ otrl_sm_proof_equal_coords(&(msg2[8]), &(msg2[9]),
+ &(msg2[10]), bstate, r, 5);
/* Convert to serialized form */
serialize_mpi_array(output, outputlen, SM_MSG2_LEN, msg2);
@@ -739,18 +752,19 @@ gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret,
* [2] = cp, [3] = d5, [4] = d6, Alice's ZK proof that pa, qa formed correctly
* [5] = ra, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3a
* [6] = cr, [7] = d7, Alice's ZK proof that ra is formed correctly */
-gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
+ const int inputlen, unsigned char **output, int* outputlen)
{
/* Read from input to find the mpis */
gcry_mpi_t r, qa1, qa2, inv;
gcry_mpi_t *msg2;
gcry_mpi_t *msg3;
gcry_error_t err;
-
+
*output = NULL;
*outputlen = 0;
astate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
-
+
err = unserialize_mpi_array(&msg2, SM_MSG2_LEN, input, inputlen);
if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
@@ -758,7 +772,7 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
check_group_elem(msg2[6]) || check_group_elem(msg2[7]) ||
check_expon(msg2[2]) || check_expon(msg2[5]) ||
check_expon(msg2[9]) || check_expon(msg2[10])) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
otrl_sm_msg3_init(&msg3);
@@ -767,9 +781,9 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
gcry_mpi_set(astate->g3o, msg2[3]);
/* Verify Bob's knowledge of discreet log proofs */
- if (otrl_sm_check_know_log(msg2[1], msg2[2], astate->g1, msg2[0], 3) ||
- otrl_sm_check_know_log(msg2[4], msg2[5], astate->g1, msg2[3], 4)) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ if (otrl_sm_check_know_log(msg2[1], msg2[2], astate->g1, msg2[0], 3) ||
+ otrl_sm_check_know_log(msg2[4], msg2[5], astate->g1, msg2[3], 4)) {
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Combine the two halves from Bob and Alice and determine g2 and g3 */
@@ -777,8 +791,9 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
gcry_mpi_powm(astate->g3, msg2[3], astate->x3, SM_MODULUS);
/* Verify Bob's coordinate equality proof */
- if (otrl_sm_check_equal_coords(msg2[8], msg2[9], msg2[10], msg2[6], msg2[7], astate, 5))
- return gcry_error(GPG_ERR_INV_VALUE);
+ if (otrl_sm_check_equal_coords(msg2[8], msg2[9], msg2[10], msg2[6], msg2[7],
+ astate, 5))
+ return gcry_error(GPG_ERR_INV_VALUE);
/* Calculate P and Q values for Alice */
r = randomExponent();
@@ -791,7 +806,8 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
gcry_mpi_mulm(astate->q, qa1, qa2, SM_MODULUS);
gcry_mpi_set(msg3[1], astate->q);
- otrl_sm_proof_equal_coords(&(msg3[2]), &(msg3[3]), &(msg3[4]), astate, r, 6);
+ otrl_sm_proof_equal_coords(&(msg3[2]), &(msg3[3]), &(msg3[4]), astate,
+ r, 6);
/* Calculate Ra and proof */
inv = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -823,7 +839,8 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
* This method also checks if Alice and Bob's secrets were the same. If
* so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
* returned instead. */
-gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input,
+ const int inputlen, unsigned char **output, int* outputlen)
{
/* Read from input to find the mpis */
int comp;
@@ -836,7 +853,7 @@ gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, c
*output = NULL;
*outputlen = 0;
bstate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
-
+
if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
otrl_sm_msg4_init(&msg4);
@@ -844,12 +861,13 @@ gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, c
if (check_group_elem(msg3[0]) || check_group_elem(msg3[1]) ||
check_group_elem(msg3[5]) || check_expon(msg3[3]) ||
check_expon(msg3[4]) || check_expon(msg3[7])) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Verify Alice's coordinate equality proof */
- if (otrl_sm_check_equal_coords(msg3[2], msg3[3], msg3[4], msg3[0], msg3[1], bstate, 6))
- return gcry_error(GPG_ERR_INV_VALUE);
+ if (otrl_sm_check_equal_coords(msg3[2], msg3[3], msg3[4], msg3[0], msg3[1],
+ bstate, 6))
+ return gcry_error(GPG_ERR_INV_VALUE);
/* Find Pa/Pb and Qa/Qb */
inv = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -860,7 +878,7 @@ gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, c
/* Verify Alice's log equality proof */
if (otrl_sm_check_equal_logs(msg3[6], msg3[7], msg3[5], bstate, 7))
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
/* Calculate Rb and proof */
gcry_mpi_powm(msg4[0], bstate->qab, bstate->x3, SM_MODULUS);
@@ -883,16 +901,17 @@ gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, c
OTRL_SMP_PROG_SUCCEEDED;
if (comp)
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
else
- return gcry_error(GPG_ERR_NO_ERROR);
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Receives the final SMP message, which was generated in otrl_sm_step.
* This method checks if Alice and Bob's secrets were the same. If
* so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
* returned instead. */
-gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen)
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input,
+ const int inputlen)
{
/* Read from input to find the mpis */
int comp;
@@ -901,16 +920,16 @@ gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input,
gcry_error_t err;
err = unserialize_mpi_array(&msg4, SM_MSG4_LEN, input, inputlen);
astate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
-
+
if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
if (check_group_elem(msg4[0]) || check_expon(msg4[2])) {
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Verify Bob's log equality proof */
if (otrl_sm_check_equal_logs(msg4[1], msg4[2], msg4[0], astate, 8))
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
/* Calculate Rab and verify that secrets match */
rab = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -924,7 +943,7 @@ gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input,
OTRL_SMP_PROG_SUCCEEDED;
if (comp)
- return gcry_error(GPG_ERR_INV_VALUE);
+ return gcry_error(GPG_ERR_INV_VALUE);
else
- return gcry_error(GPG_ERR_NO_ERROR);
+ return gcry_error(GPG_ERR_NO_ERROR);
}
diff --git a/src/sm.h b/src/sm.h
index 2e44b46..eae1241 100644
--- a/src/sm.h
+++ b/src/sm.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/src/tests.c b/src/tests.c
index ba429b3..d64ac35 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -4,6 +4,7 @@
#include "proto.h"
#include "privkey.h"
#include "message.h"
+#include "instag.h"
#define ALICE "alice"
#define BOB "bob"
@@ -11,7 +12,6 @@
static OtrlPolicy ALICEPOLICY = OTRL_POLICY_DEFAULT &~ OTRL_POLICY_ALLOW_V1;
static OtrlPolicy BOBPOLICY = OTRL_POLICY_DEFAULT;
-
void receiving(const char *from, const char *to, const char *msg);
typedef struct s_node {
@@ -22,6 +22,18 @@ typedef struct s_node {
static MsgNode *noderoot = NULL;
static MsgNode **nodeend = &noderoot;
+void otrl_sm_init(void) {}
+void otrl_sm_state_new(OtrlSMState *smst) {}
+void otrl_sm_state_init(OtrlSMState *smst) {}
+void otrl_sm_state_free(OtrlSMState *smst) {}
+gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate, const unsigned char* secret, int secretlen, unsigned char** output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);}
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question) {return gcry_error(GPG_ERR_NO_ERROR);}
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);}
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);}
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);}
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen) {return gcry_error(GPG_ERR_NO_ERROR);}
+
+
static void dispatch(void)
{
while(noderoot) {
@@ -136,7 +148,7 @@ static void sending(const char *from, const char *to, const char *msg)
char *newmsg;
err = otrl_message_sending(us, &ops, NULL, from, PROTO, to, msg,
- tlvs, &newmsg, NULL, NULL);
+ tlvs, &newmsg, NULL, NULL, NULL);
if (!err) {
inject(from, to, newmsg ? newmsg : msg);
@@ -146,14 +158,18 @@ static void sending(const char *from, const char *to, const char *msg)
otrl_tlv_free(tlvs);
}
-void test(int vers, int both)
+static void test(int vers, int both)
{
printf("\n\n*** Testing version %d, %s ***\n\n", vers,
both ? "simultaneous start" : "Alice start");
otrl_context_forget_all(us);
- ALICEPOLICY = vers == 1 ? (OTRL_POLICY_DEFAULT &~ OTRL_POLICY_ALLOW_V2) :
- OTRL_POLICY_DEFAULT;
+ if (vers == 1)
+ ALICEPOLICY = OTRL_POLICY_ALLOW_V1;
+ else if (vers == 2)
+ ALICEPOLICY = OTRL_POLICY_ALLOW_V2;
+ else
+ ALICEPOLICY = OTRL_POLICY_DEFAULT;
sending(ALICE, BOB, "?OTR?");
if (both) {
sending(BOB, ALICE, "?OTR?");
@@ -170,7 +186,7 @@ void test_unreadable(void)
printf("\n\n*** Testing Bob receiving unreadable messages from "
"Alice ***\n\n");
- bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, NULL, NULL, NULL);
+ bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL);
otrl_context_force_plaintext(bobcontext);
sending(ALICE, BOB, "unreadable text");
dispatch();
@@ -188,8 +204,8 @@ void test_crash1(void)
sending(ALICE, BOB, "?OTR?");
dispatch();
- alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, NULL, NULL, NULL);
- bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, NULL, NULL, NULL);
+ alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, 0, NULL, NULL, NULL);
+ bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL);
sending(ALICE, BOB, "Hi!"); dispatch();
sending(BOB, ALICE, "There!"); dispatch();
@@ -213,12 +229,16 @@ void test_refresh(int vers)
printf("\n\n*** Testing refresh ***\n\n");
otrl_context_forget_all(us);
- ALICEPOLICY = vers == 1 ? (OTRL_POLICY_DEFAULT &~ OTRL_POLICY_ALLOW_V2) :
- OTRL_POLICY_DEFAULT;
+ if (vers == 1)
+ ALICEPOLICY = OTRL_POLICY_ALLOW_V1;
+ else if (vers == 2)
+ ALICEPOLICY = OTRL_POLICY_ALLOW_V2;
+ else
+ ALICEPOLICY = OTRL_POLICY_DEFAULT;
sending(ALICE, BOB, "?OTR?"); dispatch();
- alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, NULL, NULL, NULL);
- bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, NULL, NULL, NULL);
+ alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, 0, NULL, NULL, NULL);
+ bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL);
printf("%p %p\n", alicecontext, bobcontext);
sending(ALICE, BOB, "Hi!"); dispatch();
@@ -236,13 +256,16 @@ int main(int argc, char **argv)
us = otrl_userstate_create();
otrl_privkey_read(us, "/home/iang/.gaim/otr.private_key");
+ otrl_instag_read(us, "inst.txt");
test(1,0);
test(2,0);
+ test(3,0);
test(1,1);
test(2,1);
test_unreadable();
test_crash1();
+ test_refresh(3);
test_refresh(2);
test_refresh(1);
diff --git a/src/tlv.c b/src/tlv.c
index 687051b..5e87c3d 100644
--- a/src/tlv.c
+++ b/src/tlv.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/src/tlv.h b/src/tlv.h
index f1c3436..256d80b 100644
--- a/src/tlv.h
+++ b/src/tlv.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
@@ -36,7 +36,7 @@ typedef struct s_OtrlTLV {
/* The sender has thrown away his OTR session keys with you */
#define OTRL_TLV_DISCONNECTED 0x0001
-/* The message contains a step in the Socialist Millionaires' Protocol. */
+/* The message contains a step in the Socialist Millionaires' Protocol. */
#define OTRL_TLV_SMP1 0x0002
#define OTRL_TLV_SMP2 0x0003
#define OTRL_TLV_SMP3 0x0004
diff --git a/src/userstate.c b/src/userstate.c
index 51d543c..b0f46b0 100644
--- a/src/userstate.c
+++ b/src/userstate.c
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -39,8 +39,8 @@ OtrlUserState otrl_userstate_create(void)
if (!us) return NULL;
us->context_root = NULL;
us->privkey_root = NULL;
+ us->instag_root = NULL;
us->pending_root = NULL;
-
return us;
}
@@ -50,5 +50,6 @@ void otrl_userstate_free(OtrlUserState us)
otrl_context_forget_all(us);
otrl_privkey_forget_all(us);
otrl_privkey_pending_forget_all(us);
+ otrl_instag_forget_all(us);
free(us);
}
diff --git a/src/userstate.h b/src/userstate.h
index 2f0106a..258376a 100644
--- a/src/userstate.h
+++ b/src/userstate.h
@@ -1,7 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
- * Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -23,12 +23,14 @@
typedef struct s_OtrlUserState* OtrlUserState;
+#include "instag.h"
#include "context.h"
#include "privkey-t.h"
struct s_OtrlUserState {
ConnContext *context_root;
OtrlPrivKey *privkey_root;
+ OtrlInsTag *instag_root;
OtrlPendingPrivKey *pending_root;
};
diff --git a/src/version.h b/src/version.h
index d755018..ffb52f6 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
* Nikita Borisov
* <otr at cypherpunks.ca>
*
diff --git a/test_suite/README b/test_suite/README
new file mode 100644
index 0000000..dd4788c
--- /dev/null
+++ b/test_suite/README
@@ -0,0 +1,22 @@
+OTR TEST SUITE
+
+BEFORE RUNNING
+This test suite is known to run with Python 2.7.1. The OTR C client first needs to compiled and statically linked with
+various versions of libotr. See the README file in "otr_c_client" for more information about this.
+
+TESTS
+Two tests are currently included.
+
+otr_test_general.py
+This test establishes OTR sessions between pairs of all 3.X versions of OTR clients. It also tests multiple 4.0 clients
+corresponding to one account establishing OTR sessions with another account with multiple 4.0 clients.
+
+otr_test_mixed.py
+This test establishes OTR sessions with multple 4.0 clients corresponding to one account with another accout with
+multiple 4.0 clients. This test also includes another 3.X with one of these accounts. All 3.X versions are tested.
+
+RUNNING
+Executing one of the above files directly will execute the corresponding test. The underlying instant messaging server
+will be started and stopped as necessary.
+
+After each test, "Test succeeded" or "Test failed" will be printed.
diff --git a/test_suite/dummy_im.py b/test_suite/dummy_im.py
new file mode 100644
index 0000000..3d098fe
--- /dev/null
+++ b/test_suite/dummy_im.py
@@ -0,0 +1,211 @@
+#!/usr/bin/python
+
+import time
+import sys
+import threading
+import socket
+from struct import *
+
+
+class client_context():
+ def __init__(self, accountname, protocol, client_thread):
+ self.accountname = accountname
+ self.protocol = protocol
+ self.thread = client_thread
+
+class client_thread(threading.Thread):
+ def __init__(self, server, socket, debug=False):
+ threading.Thread.__init__(self)
+ self.server = server
+ self.sock = socket
+ self.done = False
+ self.done_sem = threading.Semaphore()
+ self.send_sem = threading.Semaphore()
+ self.ctx = None
+ self.debug = debug
+
+ def run(self):
+ #Registration: 1 byte accountname len, accountname, 1 byte protocol len, protocol
+ accountname_len = self.read_1b_val()
+ accountname = self.read_bytes(accountname_len)
+
+ protocol_len = self.read_1b_val()
+ protocol = self.read_bytes(protocol_len)
+
+ if self.debug:
+ print('Client accountname: ' + accountname + ' protocol: ' + protocol)
+
+ #create client_context and add to table
+ self.ctx = client_context(accountname, protocol, self)
+ self.server.add_client(self.ctx)
+
+ while not self.check_client_done():
+ try: #Message: 1 byte accountname len, accountname, 1 byte protocol len, protocol, 4 byte msg len, msg
+ accountname_len = self.read_1b_val()
+ accountname = self.read_bytes(accountname_len)
+
+ protocol_len = self.read_1b_val()
+ protocol = self.read_bytes(protocol_len)
+
+ msg_len = self.read_4b_val()
+ msg = self.read_bytes(msg_len)
+
+ if not self.check_client_done():
+ self.server.deliver_msg(self.ctx.accountname, self.ctx.protocol, accountname, protocol, msg)
+
+ except:
+ if self.debug:
+ print('removing client due to error')
+ break
+
+ self.server.remove_client(self.ctx)
+ self.sock.close()
+
+ def send_msg(self, msg):
+ self.send_sem.acquire()
+ totalsent = 0
+ while not self.check_client_done() and totalsent < len(msg):
+ sent = self.sock.send(msg[totalsent:])
+ if sent == 0:
+ self.set_client_done()
+ totalsent = totalsent + sent
+ self.send_sem.release()
+ if self.debug:
+ print 'sent: ', totalsent
+
+ def set_client_done(self):
+ self.done_sem.acquire()
+ self.done = True
+ self.done_sem.release()
+ if self.ctx is not None:
+ if self.debug:
+ print('Client accountname: ' + self.ctx.accountname + ' protocol: ' + self.ctx.protocol + ' is marked as done')
+ else:
+ if self.debug:
+ print('Uninitialized client disconnected')
+
+ def check_client_done(self):
+ done_val = False
+ self.done_sem.acquire()
+ done_val = self.done
+ self.done_sem.release()
+ return done_val
+
+ def read_bytes(self, num_bytes):
+ msg = ''
+ while not self.check_client_done() and len(msg) < num_bytes:
+ chunk = self.sock.recv(num_bytes-len(msg))
+ if chunk == '':
+ self.set_client_done()
+ break
+ msg = msg + chunk
+ return msg
+
+ def read_1b_val(self):
+ byte = self.read_bytes(1)
+ if len(byte) == 1:
+ return ord(byte)
+ else: return
+
+ def read_4b_val(self):
+ bytes = self.read_bytes(4)
+ if len(bytes) == 4:
+ val = unpack('!I',bytes)
+ return int(val[0])
+ else: return
+
+
+class im_server(threading.Thread):
+ def __init__(self, port, debug=False):
+ threading.Thread.__init__(self)
+ self.port = port
+ self.finished = False
+ self.clients_sem = threading.Semaphore()
+ self.clients = dict()
+ self.debug=debug
+
+ def run(self):
+ serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ serversocket.bind(('', self.port))
+ serversocket.listen(50)
+
+ if self.debug:
+ print('Server listening...')
+
+ while not self.finished:
+ (clientsocket, address) = serversocket.accept()
+ if self.debug:
+ print('Client connected: ' + str(address))
+ cl_thread = client_thread(self, clientsocket, self.debug)
+ cl_thread.daemon = True
+ cl_thread.start()
+
+ serversocket.close()
+
+ def set_finished(self):
+ self.finished = True
+
+ def add_client(self, ctx):
+ self.clients_sem.acquire()
+
+ if self.debug:
+ print('Adding client to dict: ' + str(ctx))
+ entry_list = self.clients.get((ctx.accountname, ctx.protocol))
+ if entry_list is None:
+ entry_list = [ctx]
+ self.clients[(ctx.accountname, ctx.protocol)] = entry_list
+ else:
+ entry_list.append(ctx)
+
+ self.clients_sem.release()
+
+ def remove_client(self, ctx):
+ self.clients_sem.acquire()
+
+ if self.debug:
+ print('Removing client from dict: ' + str(ctx))
+ entry_list = self.clients.get((ctx.accountname, ctx.protocol))
+ if entry_list is not None and entry_list.count(ctx) > 0:
+ entry_list.remove(ctx)
+ if len(entry_list) == 0:
+ del self.clients[(ctx.accountname, ctx.protocol)]
+
+ self.clients_sem.release()
+
+ def deliver_msg(self, src_accountname, src_protocol, dst_accountname, dst_protocol, msg):
+ self.clients_sem.acquire()
+ new_msg = bytearray()
+ new_msg.append(len(src_accountname))
+ new_msg.extend(src_accountname)
+ new_msg.append(len(src_protocol))
+ new_msg.extend(src_protocol)
+
+ for b in pack('!I', len(msg)):
+ new_msg.append(ord(b))
+
+ new_msg.extend(msg)
+
+ entry_list = self.clients.get((dst_accountname, dst_protocol))
+ if entry_list is not None:
+ for c in entry_list:
+ if self.debug:
+ print('Delivering msg from accountname: ' + src_accountname +
+ ' protocol: ' + src_protocol + ' to accountname: ' + dst_accountname +
+ ' protocol: ' + dst_protocol)
+ c.thread.send_msg(new_msg)
+
+ self.clients_sem.release()
+
+def main(argv):
+ port = 1536
+
+ if len(argv) > 0:
+ port = int(argv[1])
+
+ server = im_server(port, debug=True)
+ #server.daemon = True
+ server.start()
+
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/test_suite/dummy_im/dummy_client.py b/test_suite/dummy_im/dummy_client.py
new file mode 100755
index 0000000..e675b5c
--- /dev/null
+++ b/test_suite/dummy_im/dummy_client.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+
+import sys
+import socket
+import threading
+from struct import *
+
+class kb_thread(threading.Thread):
+ def __init__(self, socket):
+ threading.Thread.__init__(self)
+ self.sock = socket
+
+ def run(self):
+ while True:
+ msg = getline(sys.stdin)
+
+ account_end = msg.find(' ')
+ if account_end < 0: continue
+ accountname = msg[:account_end]
+
+ proto_end = msg[account_end+1:].find(' ') + account_end + 1
+ if proto_end < 0: continue
+ protocol = msg[account_end+1:proto_end]
+
+ msg = msg[proto_end+1:]
+
+ print("Sending msg -- account: " + accountname + " protocol: " + protocol + " msg: " + msg)
+
+ send_msg(self.sock, accountname, protocol, msg)
+
+def getline(stream, delimiter="\n"):
+ def _gen():
+ while 1:
+ line = stream.readline()
+ if delimiter in line:
+ yield line[0:line.index(delimiter)]
+ break
+ else:
+ yield line
+ return "".join(_gen())
+
+def send(sock, msg, length=0):
+ if length == 0: length = len(msg)
+ totalsent = 0
+ while totalsent < length:
+ sent = sock.send(msg[totalsent:])
+ if sent == 0:
+ return
+ totalsent = totalsent + sent
+
+def read_bytes(sock, num_bytes):
+ msg = ''
+ while len(msg) < num_bytes:
+ chunk = sock.recv(num_bytes-len(msg))
+ if chunk == '':
+ return
+ msg = msg + chunk
+ return msg
+
+def read_1b_val(sock):
+ byte = read_bytes(sock, 1)
+ if len(byte) == 1:
+ return ord(byte)
+ else: return
+
+def read_4b_val(sock):
+ bytes = read_bytes(sock, 4)
+ if len(bytes) == 4:
+ val = unpack('!I',bytes)
+ return int(val[0])
+ else: return
+
+def recv_msg(sock):
+ recv_acc_len = read_1b_val(sock)
+ recv_acc = read_bytes(sock, recv_acc_len)
+ recv_proto_len = read_1b_val(sock)
+ recv_proto = read_bytes(sock, recv_proto_len)
+ recv_msg_len = read_4b_val(sock)
+ recv_msg = read_bytes(sock, recv_msg_len)
+ print("Received msg: " + recv_msg)
+ return recv_msg
+
+def send_msg(sock, accountname, protocol, msg):
+ new_msg = bytearray()
+ new_msg.append(len(accountname))
+ new_msg.extend(accountname)
+ new_msg.append(len(protocol))
+ new_msg.extend(protocol)
+ for b in pack('!I', len(msg)):
+ new_msg.append(ord(b))
+ new_msg.extend(msg)
+ send(sock, new_msg)
+
+def main(argv):
+ if len(argv) < 3:
+ print("Usage: " + argv[0] + " <accountname> <protocol>")
+ return
+
+ accountname = argv[1]
+ protocol = argv[2]
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(("localhost", 1536))
+
+ acc_len = len(accountname)
+ packed_acc_len = pack('B', acc_len)
+
+ #init
+ send(s, packed_acc_len, 1)
+ send(s, accountname)
+ send(s, pack('B', len(protocol)), 1)
+ send(s, protocol)
+
+ input_handler = kb_thread(s)
+ input_handler.start()
+
+ while True:
+ recv_msg(s)
+
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/test_suite/instance_tags0.txt b/test_suite/instance_tags0.txt
new file mode 100644
index 0000000..04aa50e
--- /dev/null
+++ b/test_suite/instance_tags0.txt
@@ -0,0 +1,3 @@
+otrtest1 prpl-aim 27e31597
+otrtest2 prpl-aim 27e31598
+otrtest3 prpl-aim 27e31599
diff --git a/test_suite/instance_tags1.txt b/test_suite/instance_tags1.txt
new file mode 100644
index 0000000..ac822fc
--- /dev/null
+++ b/test_suite/instance_tags1.txt
@@ -0,0 +1,3 @@
+otrtest1 prpl-aim 27e32500
+otrtest2 prpl-aim 27e32501
+otrtest3 prpl-aim 27e32502
diff --git a/test_suite/instance_tags2.txt b/test_suite/instance_tags2.txt
new file mode 100644
index 0000000..80c0e28
--- /dev/null
+++ b/test_suite/instance_tags2.txt
@@ -0,0 +1,3 @@
+otrtest1 prpl-aim 27e32603
+otrtest2 prpl-aim 27e32604
+otrtest3 prpl-aim 27e32605
diff --git a/test_suite/instance_tags3.txt b/test_suite/instance_tags3.txt
new file mode 100644
index 0000000..9d579dd
--- /dev/null
+++ b/test_suite/instance_tags3.txt
@@ -0,0 +1,3 @@
+otrtest1 prpl-aim 27e33506
+otrtest2 prpl-aim 27e33507
+otrtest3 prpl-aim 27e33508
diff --git a/test_suite/instance_tags4.txt b/test_suite/instance_tags4.txt
new file mode 100644
index 0000000..e772568
--- /dev/null
+++ b/test_suite/instance_tags4.txt
@@ -0,0 +1,3 @@
+otrtest1 prpl-aim 27e42509
+otrtest2 prpl-aim 27e42500
+otrtest3 prpl-aim 27e42501
diff --git a/test_suite/otr.private_key b/test_suite/otr.private_key
new file mode 100644
index 0000000..ca67d84
--- /dev/null
+++ b/test_suite/otr.private_key
@@ -0,0 +1,41 @@
+(privkeys
+ (account
+(name otrtest2)
+(protocol prpl-aim)
+(private-key
+ (dsa
+ (p #00E4240AE9740D5FB1DF076A3270143BBD5B66DEB9BFD601CF3C3DC3E8D76BF1EA0D1A66E209516600E915B79552887EE9810D613C63EA6D2B6E69321C3C81F71F533F02430D20AF65C05C15BA46EF10C78EFF3BE687C33A4F6EA90877AB30E6423070AAFD3F22B5939D0DC8FBE5145A68F38EBA98ADCA7E06EFBC5E345E9AEDEF#)
+ (q "\x00�ջ�*��i#�b�U~�{U��q")
+ (g #00D3A15721AEF3A49DF26E875EC8C5FF4096B0B1717AF89485A015245176FD4AE3065D9F7B2D185B1E02CEAFB53E6984B1D341E62EBB4A5C189EB40959CFED0C48DAE73F3391C5E1482372E9DE539CD6911C54583DC0A3179547A38E19549F81A6DBFA8036C471C2A358D4B3AB86C7424556DFD61C35CC1EC08304B0102A0EEE08#)
+ (y #6E3996E07B6CB54DC443BD83033293956E5B7CCC511D19DCAC8735496B061D6EB6DF3D5540548D3C97201393E669EF9066BCA6A72E3AC68710188BEDF06FDF22548D8709BD4E9E1AD02DB0E193399BEB39DE5218D70C999D9856D3205DB79BA8DFC188726E646A1BE1722843F5E56A51C499C5BE5F63FACE7802ECD565E99F39#)
+ (x #2C5887BA7EC5F426B6BE4438E2FAE4589DBF2CB6#)
+ )
+ )
+ )
+ (account
+(name otrtest1)
+(protocol prpl-aim)
+(private-key
+ (dsa
+ (p #00BD114F05B275A8A94954047983C5CD96ED95C782D2ED65A18E78C98E8EAFBAF58BBD046BE9895AD55FD0FF95907E7EBD6ACA2688D24779BDE9F0AAB13924CE65F597F9C9B9953DDBACF51DA7113FBAB9BE1DF6C6EA836DEB48983CCDCFC4125B5013D0CE52F890D0C391A035D30BCD5169A3451FD7023685274576DCB5F8FA47#)
+ (q #00D1DA3915346A704EB2D2F2A48CD48F3DCC4CF25D#)
+ (g #501BCFB989AD2C346BBD7782CA0230551F976B1A07EE3AEE27E4B63B7B00B1ACA712AD85784986411278163156D4DBA9DF75C8560F9C2E02C02AEC830EC403A56B6F64432869D6CA9314A648076511343507629BF4FC96F8FDBB9797258DDF11F437B1450BA23F1AA7E885EC6A33D37B7D7EC384A004420DB238E140B94AAAFE#)
+ (y #7C9CB7732164787DD1931BB58257665EB60D6AA72B8D64D634530A61BE93D5AF01427962646542F18401B73032B12B9CBCAE8E3CF080DAD55C6612A97D6D8776CF2CBDD3AAC75D302B60E6956E5B3C60B39E171A2D5F150A924C6E22981EFDF052D5C6507B2DEC15E96CB6CAF7B260D5386BBDD7D7F69B4BF14451D64D847AEB#)
+ (x #00AB1E941176D94505911118AC799A504ADCCE88F8#)
+ )
+ )
+ )
+ (account
+(name otrtest3)
+(protocol prpl-aim)
+(private-key
+ (dsa
+ (p #00BB4C57669E50E4C35F8E4CA84855CF2C83EE75C4F44B4BB4A7E88590D394D7A738E82EE97892E5051CE45E200741E18D423137AA8E6679B1CFAB4FF11D45D8C9CBDE388D30FC800B4879713E3C57BA48A92FE135BB9AF265F770B706FB9A04802244D12CBFFD97ACE5C73FCE88C2B716B4B22B994CD6429A7E16D9B6D1874137#)
+ (q #00C40DA63B679A80FC31BF49A68503BB39754D0A45#)
+ (g #6C0A48BEA859587D6677306D1777A2A0635470F149A86EB64EA62EAAA4C21ECE4375ACD016B776E3AD3411C18BB3FF37F963FCEBB8820FF8838AFA6FCD1B39558DAB78450AE2ED9457DEDBDCE13DF5A6B20A738D2973D375D360C044AF7F0204CCC372098F0B6460963274B1EA0B5FEC93571A15F5C03DCDF54EE83BB198F363#)
+ (y #00AB2C8A82F020DB99EF5B7A8330EC43E0D5EBD623FEB67D1B046D88FACA01D8E31E4D7865DC62D4DA58CF8BC7FF4B57C203A9F7F5C85DAB1B63D63299EF13AD89AAA7E6638C9DBC42D096408936C9F0382224CFB5C1528DCC8C7F2554CB4CA2FF3C3239BC921F1C690295DD9AE69C8EF5BBD8E58A8FAA8BB9D5F88463CAECEE7B#)
+ (x #7824B713A4E5FA6D6C69172196648CD4657A1ED1#)
+ )
+ )
+ )
+)
diff --git a/test_suite/otr_c_client/Makefile b/test_suite/otr_c_client/Makefile
new file mode 100644
index 0000000..d8229c7
--- /dev/null
+++ b/test_suite/otr_c_client/Makefile
@@ -0,0 +1,8 @@
+all:
+ gcc dummy_client.c -DOTR30 -g -o dummy_client_30 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.0/libotr/src -L./libs -lotr3.0 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error
+ gcc dummy_client.c -DOTR31 -g -o dummy_client_31 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.1/libotr/src -L./libs -lotr3.1 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error
+ gcc dummy_client.c -DOTR32 -g -o dummy_client_32 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.2/libotr/src -L./libs -lotr3.2 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error
+ gcc dummy_client.c -DOTR40 -g -o dummy_client_40 -Wl,-Bstatic -I/home/rdfsmits/otr/git/otr/libotr/src -L./libs -lotr4.0 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error
+clean:
+ rm dummy_client_30 dummy_client_31 dummy_client_32 dummy_client_40
+
diff --git a/test_suite/otr_c_client/README b/test_suite/otr_c_client/README
new file mode 100644
index 0000000..7f3187f
--- /dev/null
+++ b/test_suite/otr_c_client/README
@@ -0,0 +1,10 @@
+The Makefile is currently set up to find pre-compiled libotr static libraries in a ./libs directory.
+You will need to change the header include directories as appropriate.
+
+It expects the following files (copied and renamed from the appropriate versions):
+
+libotr3.0.a
+libotr3.1.a
+libotr3.2.a
+libotr4.0.a
+
diff --git a/test_suite/otr_c_client/dummy_client.c b/test_suite/otr_c_client/dummy_client.c
new file mode 100644
index 0000000..d42d793
--- /dev/null
+++ b/test_suite/otr_c_client/dummy_client.c
@@ -0,0 +1,1071 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include <gcrypt.h>
+
+#include "proto.h"
+#include "privkey.h"
+#include "message.h"
+#include "context.h"
+
+#ifdef OTR40
+#include "instag.h"
+#endif
+
+#define PROCESSING_DONE "DONE"
+
+#define DEFAULT_IP "127.0.0.1"
+#define DEFAULT_PORT 1536
+
+#define Q_ID_RECEIVED 1
+#define Q_ID_RECEIVED_OTR 2
+#define Q_ID_ERR 3
+#define Q_ID_GONE_SECURE 4
+
+int sockfd = -1;
+
+FILE *logfd = NULL;
+
+OtrlUserState us;
+static char* our_account = "default_account";
+static char* our_protocol = "default_protocol";
+
+pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+static OtrlPolicy op_policy(void *opdata, ConnContext *context) {
+ return OTRL_POLICY_DEFAULT;
+}
+
+static void op_inject(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient, const char *message);
+
+
+char* pass_otr_in_msg(char* account, char* protocol, char* msg);
+
+void write_query_response(uint32_t id, const char* msg);
+void write_query_response_s(uint32_t id, unsigned char* buf, uint32_t msg_size);
+
+#ifdef OTR40
+const char* otr_error_message(void *opdata, ConnContext *context, OtrlErrorCode err_code);
+void otr_error_message_free(void *opdata, const char *err_msg);
+void handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err);
+#endif
+
+#if defined OTR30 || defined OTR31 || defined OTR32
+int display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg);
+#endif
+
+uint32_t all_contexts_to_buf(unsigned char** buf_p);
+
+uint32_t context_to_buf(unsigned char** buf_p, uint32_t protocol_version, char* username, char * accountname,
+ char * protocol, uint32_t otr_offer, uint32_t msg_state, uint32_t auth_state,
+ uint32_t our_instance, uint32_t their_instance);
+
+void gone_secure(void *opdata, ConnContext *context);
+
+int max_message_size(void *opdata, ConnContext *context);
+
+#ifdef OTR30
+static OtrlMessageAppOps ops = {
+ op_policy,
+ NULL,
+ NULL,
+ op_inject,
+ NULL,
+ display_otr_message,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gone_secure,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
+#if defined OTR31 || defined OTR32
+static OtrlMessageAppOps ops = {
+ op_policy,
+ NULL,
+ NULL,
+ op_inject,
+ NULL,
+ display_otr_message,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gone_secure,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
+#ifdef OTR40
+static OtrlMessageAppOps ops = {
+ op_policy,
+ NULL,
+ NULL,
+ op_inject,
+ NULL,
+ NULL,
+ NULL,
+ gone_secure,
+ NULL,
+ NULL,
+ max_message_size,
+ NULL,
+ NULL,
+ NULL,
+ otr_error_message,
+ otr_error_message_free,
+ NULL,
+ NULL,
+ NULL,
+ handle_msg_event,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
+int max_message_size(void *opdata, ConnContext *context) {
+#ifdef FRAG40
+ return 100;
+#else
+ return 0;
+#endif
+}
+
+static void read_fingerprint(char *file) {
+ if (otrl_privkey_read_fingerprints(us, file, NULL, NULL)) {
+ fprintf(stderr, "Error reading fingerprints");
+ }
+}
+
+static void read_privkey(char *file) {
+ if (otrl_privkey_read(us, file)) {
+ fprintf(stderr, "Error reading private key");
+ }
+}
+
+#ifdef OTR40
+static void read_instag(char *file) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "About to read instance tag: %s \n", file);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ if (otrl_instag_read(us, file)) {
+ fprintf(stderr, "Error reading instance tags");
+ }
+}
+#endif
+
+void waitForEofClear(FILE *f) {
+ while (feof(f)) {
+ clearerr(f);
+ sleep(1);
+ }
+}
+
+uint32_t read_stdin(char* buf, uint32_t num) {
+ uint32_t n_read = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < num; i++) {
+ unsigned char c = EOF;
+
+ waitForEofClear(stdin);
+ c = fgetc(stdin);
+
+ if (c == EOF) break;
+
+ buf[i] = c;
+ n_read++;
+ }
+ /*waitForEofClear(stdin);
+ n_read = read(0, buf, num);*/
+
+ return n_read;
+}
+
+uint32_t write_stdout(char* buf, uint32_t num) {
+ uint32_t i = 0;
+ pthread_mutex_lock(&stdout_mutex);
+
+
+ while (i < num) {
+ int wrote = fputc((int)buf[i], stdout);
+ fflush(stdout);
+
+ if (((unsigned char)wrote) != (unsigned char)(buf[i])) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Expected to write %x, returned %x\n", (unsigned char)buf[i], wrote);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ sleep(1);
+ continue;
+ }
+
+ i++;
+ }
+ /*i = write(1, buf, num);*/
+
+ pthread_mutex_unlock(&stdout_mutex);
+ return i;
+}
+
+char* pass_otr_out_msg(uint32_t id, char* account, char* protocol, char* message) {
+ char *new_message = NULL;
+ gcry_error_t err;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Passing to OTR message_sending %s to account %s protocol %s\n", message, account, protocol);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+#ifdef OTR40
+#ifdef FRAG40
+ err = otrl_message_sending(us, &ops, NULL,
+ our_account, protocol, account, OTRL_INSTAG_BEST, message, NULL, &new_message,
+ OTRL_FRAGMENT_SEND_ALL_BUT_LAST, NULL, NULL, NULL);
+#else
+ err = otrl_message_sending(us, &ops, NULL,
+ our_account, protocol, account, OTRL_INSTAG_BEST, message, NULL, &new_message,
+ OTRL_FRAGMENT_SEND_SKIP, NULL, NULL, NULL);
+#endif
+#endif
+
+#if defined OTR30 || defined OTR31 || defined OTR32
+ err = otrl_message_sending(us, &ops, NULL,
+ our_account, protocol, account, message, NULL, &new_message,
+ NULL, NULL);
+#endif
+
+ if (new_message) {
+ char *ourm = strdup(new_message);
+
+ write_query_response(id, new_message); /* send modified message back */
+
+ otrl_message_free(new_message);
+ new_message = ourm;
+ }
+
+ if (err) {
+ /* Do not send out plain text */
+ char *ourm = strdup("");
+ new_message = ourm;
+ }
+
+ return new_message;
+}
+
+void parse_outgoing_msg_otr(uint32_t id, uint32_t *size, unsigned char** buf_ptr) {
+ unsigned char* buf = *buf_ptr;
+ unsigned char account_len;
+ char* account;
+ unsigned char protocol_len;
+ char* protocol;
+ uint32_t msg_len;
+ char* msg;
+ char* new_msg;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "About to parse outgoing payload for otr\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ account_len = buf[0];
+ buf++;
+ account = malloc(account_len+1);
+ strncpy(account, buf, account_len);
+ account[account_len] = '\0';
+ buf += account_len;
+
+ protocol_len = buf[0];
+ buf++;
+ protocol = malloc(protocol_len+1);
+ strncpy(protocol, buf, protocol_len);
+ protocol[protocol_len] = '\0';
+ buf += protocol_len;
+
+ msg_len = *((uint32_t*)buf);
+ msg_len = ntohl(msg_len);
+ buf += 4;
+
+ if (msg_len) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Outgoing payload contained msg length %u\n", msg_len);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ msg = malloc(msg_len+1);
+ strncpy(msg, buf, msg_len);
+ msg[msg_len] = '\0';
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Outgoing msg %s\n", msg);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ new_msg = pass_otr_out_msg(id, account, protocol, msg);
+ if (new_msg) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "New msg from OTR: %s len: %u\n", new_msg, strlen(new_msg));
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ }
+ }
+
+ if (new_msg) {
+ uint32_t new_msg_len = strlen(new_msg);
+ uint32_t new_buf_len = 1 + account_len + 1 + protocol_len + 4 + new_msg_len;
+ unsigned char* new_buf = malloc(new_buf_len);
+ unsigned char* new_buf_head = new_buf;
+
+ *size = new_buf_len;
+ memcpy(new_buf, *buf_ptr, 1 + account_len + 1 + protocol_len);
+ new_buf += 1 + account_len + 1 + protocol_len;
+ *((uint32_t*)new_buf) = htonl(new_msg_len);
+ new_buf += 4;
+ strncpy(new_buf, new_msg, new_msg_len);
+ free(new_msg);
+ free(*buf_ptr);
+ *buf_ptr = new_buf_head;
+ if (msg) free(msg);
+ } else {
+ *size = strlen(msg);
+ *buf_ptr = msg;
+ }
+
+ free(account);
+ free(protocol);
+}
+
+
+
+int send_msg(uint32_t size, char* payload) {
+ int sent = -1;
+ if (sent = send(sockfd, payload, size, 0) != size) {
+ fprintf(stderr, "Error sending data (send_msg)\n");
+ }
+ return sent;
+}
+
+int send_msg_otr(uint32_t id, uint32_t size, char** payload) {
+ int sent = -1;
+
+ if (!size || !*payload) return sent;
+
+ parse_outgoing_msg_otr(id, &size, (unsigned char**) payload);
+
+ if (!size || !*payload) return sent;
+
+ sent = send_msg(size, *payload);
+
+ return sent;
+}
+
+process_command_privkey(uint32_t size, char* payload) {
+ char path[size+1];
+ strncpy(path, payload, size);
+ path[size] = '\0';
+ read_privkey(path);
+}
+
+#ifdef OTR40
+process_command_instag(uint32_t size, char* payload) {
+ char path[size+1];
+ strncpy(path, payload, size);
+ path[size] = '\0';
+ read_instag(path);
+}
+#endif
+
+void finish_up() {
+ close((int)logfd);
+ close(sockfd);
+ exit(0);
+}
+
+void check_finished(uint16_t command) {
+ if (command == 0xFFFF) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "%s", "Read command 0xFFFF, assuming parent is finished\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ finish_up();
+ }
+}
+
+void write_all_contexts(uint32_t id) {
+ unsigned char* buf = NULL;
+ uint32_t buf_size = all_contexts_to_buf(&buf);
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "%s", "***************** dumping all contexts ********************\n");
+ fwrite(buf, 1, buf_size, logfd);
+ fprintf(logfd, "%s", "\n***************** done dumping contexts ********************\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ write_query_response_s(id, buf, buf_size);
+ free(buf);
+}
+
+void gone_secure(void *opdata, ConnContext *context) {
+ unsigned char* buf = NULL;
+ uint32_t buf_size = 0;
+
+ if (!context) return;
+
+ #if defined OTR30 || defined OTR31 || defined OTR32
+ buf_size = context_to_buf(&buf, context->protocol_version, context->username,
+ context->accountname, context->protocol, context->otr_offer, context->msgstate,
+ context->auth.authstate, 0, 0);
+ #endif
+
+ #if defined OTR40
+ buf_size = context_to_buf(&buf, context->protocol_version, context->username,
+ context->accountname, context->protocol, context->otr_offer, context->msgstate,
+ context->auth.authstate, context->our_instance, context->their_instance);
+ #endif
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Context %s %s %s has gone secure (printing data below)\n", context->username, context->accountname, context->protocol);
+ fwrite(buf, 1, buf_size, logfd);
+ fprintf(logfd, "%s", "\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ write_query_response_s(Q_ID_GONE_SECURE, buf, buf_size);
+ free(buf);
+}
+
+void process_command(uint16_t command,
+ uint32_t id, uint32_t size, char** payload) {
+ switch (command) {
+ case 0x0000: /* send msg (no OTR) */
+ send_msg(size, *payload);
+ break;
+ case 0x0001: /* send msg */
+ #ifdef __DEBUGGING
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "%s", "Sending message...\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ #endif
+
+ send_msg_otr(id, size, payload);
+ break;
+ case 0x0002: /* read privkey */
+ process_command_privkey(size, *payload);
+ break;
+ case 0x0003: /* write all contexts */
+ write_all_contexts(id);
+ break;
+ #ifdef OTR40
+ case 0x004:
+ process_command_instag(size, *payload);
+ break;
+ #endif
+ }
+ write_query_response(id, PROCESSING_DONE);
+}
+
+process_and_write_msg(unsigned char account_len, char* account,
+ unsigned char protocol_len, char* protocol,
+ uint32_t msg_len, char* msg, uint32_t q_id) {
+ /* re-assemble message as single buffer */
+ unsigned char* buf;
+ unsigned char* buf_head;
+
+ if (msg == NULL || msg_len == 0) return;
+
+ uint32_t total_len = 4 + 4 + 1 + account_len + 1 + protocol_len + 4 + msg_len;
+ buf = malloc(total_len);
+ buf_head = buf;
+
+ *((uint32_t*)buf) = htonl(q_id);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl(total_len - 8);
+ buf += 4;
+
+ buf[0] = account_len;
+ buf++;
+ strncpy(buf, account, account_len);
+ buf += account_len;
+
+ buf[0] = protocol_len;
+ buf++;
+ strncpy(buf, protocol, protocol_len);
+ buf += protocol_len;
+
+ *((uint32_t*)buf) = htonl(msg_len);
+ buf += 4;
+ strncpy(buf, msg, msg_len);
+
+ uint32_t written = write_stdout(buf_head, total_len);
+
+ if (written == total_len) {
+
+ #ifdef __DEBUGGING
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "%s", "Msg written to stdout\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ #endif
+
+ } else {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Only wrote %u bytes to stdout, expected %u\n", written, total_len);
+ fprintf(logfd, "Failed on message from account %s protocol %s contents %s\n", account, protocol, msg);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ fprintf(stderr, "Only wrote %u bytes to stdout, expected %u\n", written, total_len);
+ }
+
+ free(buf_head);
+}
+
+void* receive_msgs(void* data) {
+ int recvd = 0;
+ int finished = 0;
+
+ while (1) {
+ unsigned char account_len;
+ char* account = 0;
+ unsigned char protocol_len;
+ char* protocol = 0;
+ uint32_t msg_len;
+ char* msg = 0;
+ uint32_t new_msg_len;
+ char* new_msg = NULL;
+ #ifdef __DEBUGGING
+ char print_buf[1024];
+ #endif
+
+ if (recvd = recv(sockfd, &account_len, 1, 0) != 1) {
+ fprintf(stderr, "account_len: Failed to receive from socket\n");
+ break;
+ }
+
+ account = malloc(account_len+1);
+
+ if (recvd = recv(sockfd, account, account_len, 0) != account_len) {
+ fprintf(stderr, "account: Failed to receive from socket\n");
+ break;
+ }
+
+ if (recvd = recv(sockfd, &protocol_len, 1, 0) != 1) {
+ fprintf(stderr, "protocol_len: Failed to receive from socket\n");
+ break;
+ }
+
+ protocol = malloc(protocol_len+1);
+
+ if (recvd = recv(sockfd, protocol, protocol_len, 0) != protocol_len) {
+ fprintf(stderr, "protocol: Failed to receive from socket\n");
+ break;
+ }
+
+ if (recvd = recv(sockfd, &msg_len, 4, 0) != 4) {
+ fprintf(stderr, "msg_len: Failed to receive from socket\n");
+ break;
+ }
+
+ msg_len = ntohl(msg_len);
+ msg = malloc(msg_len+1);
+
+ if (recvd = recv(sockfd, msg, msg_len, 0) != msg_len) {
+ fprintf(stderr, "msg: Failed to receive from socket. length: %u, recvd: %u, %s\n", msg_len, recvd, strerror( errno ));
+ break;
+ }
+
+ #ifdef __DEBUGGING
+ sprintf(print_buf, "Received msg: %u %%.%us, from account %u %%.%us protocol %u %%.%us\n", msg_len, msg_len, account_len, account_len, protocol_len, protocol_len);
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, print_buf, msg, account, protocol);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ #endif
+
+ account[account_len] = '\0';
+ protocol[protocol_len] = '\0';
+ msg[msg_len] = '\0';
+
+ process_and_write_msg(account_len, account, protocol_len, protocol, msg_len, msg, Q_ID_RECEIVED);
+
+ new_msg = pass_otr_in_msg(account, protocol, msg);
+
+ if (new_msg) {
+ new_msg_len = strlen(new_msg);
+ process_and_write_msg(account_len, account, protocol_len, protocol, new_msg_len, new_msg, Q_ID_RECEIVED_OTR);
+ free(new_msg);
+ }
+
+ free(msg);
+ free(account);
+ free(protocol);
+ }
+}
+
+uint32_t context_to_buf(unsigned char** buf_p, uint32_t protocol_version, char* username, char * accountname,
+ char * protocol, uint32_t otr_offer, uint32_t msg_state, uint32_t auth_state,
+ uint32_t our_instance, uint32_t their_instance) {
+ uint32_t username_len = strlen(username);
+ uint32_t accountname_len = strlen(accountname);
+ uint32_t protocol_len = strlen(protocol);
+ uint32_t buf_size = 4 + 1 + username_len + 1 + accountname_len + 1 + protocol_len + 4 + 4 + 4 + 4 + 4;
+ unsigned char* buf = malloc(buf_size);
+ *buf_p = buf;
+
+ *((uint32_t*)buf) = htonl(protocol_version);
+ buf += 4;
+
+ *buf = username_len;
+ buf++;
+
+ strncpy(buf, username, username_len);
+ buf += username_len;
+
+ *buf = accountname_len;
+ buf++;
+
+ strncpy(buf, accountname, accountname_len);
+ buf += accountname_len;
+
+ *buf = protocol_len;
+ buf++;
+
+ strncpy(buf, protocol, protocol_len);
+ buf += accountname_len;
+
+ *((uint32_t*)buf) = htonl(otr_offer);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl(msg_state);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl(auth_state);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl(our_instance);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl(their_instance);
+ buf += 4;
+
+
+ return buf_size;
+}
+
+uint32_t all_contexts_to_buf(unsigned char** buf_p) {
+ ConnContext *context = us->context_root;
+ unsigned char* buf = NULL;
+ uint32_t buf_size = 0;
+ uint32_t num_contexts = 0;
+
+ while (context != NULL) {
+ unsigned char* temp_buf;
+
+ uint32_t temp_buf_size = 0;
+
+ #if defined OTR30 || defined OTR31 || defined OTR32
+ temp_buf_size = context_to_buf(&temp_buf, context->protocol_version, context->username,
+ context->accountname, context->protocol, context->otr_offer, context->msgstate,
+ context->auth.authstate, 0, 0);
+ #endif
+
+ #if defined OTR40
+ temp_buf_size = context_to_buf(&temp_buf, context->protocol_version, context->username,
+ context->accountname, context->protocol, context->otr_offer, context->msgstate,
+ context->auth.authstate, context->our_instance, context->their_instance);
+ #endif
+
+ unsigned char* new_buf = malloc(buf_size + temp_buf_size);
+ if (buf != NULL) memcpy(new_buf, buf, buf_size);
+ memcpy(new_buf + buf_size, temp_buf, temp_buf_size);
+
+ free(buf);
+ free(temp_buf);
+ buf = new_buf;
+ buf_size += temp_buf_size;
+
+ context = context->next;
+ num_contexts++;
+ }
+
+ /* precede serialized contexts by number of contexts */
+ if (num_contexts) {
+ unsigned char* new_buf = malloc(4 + buf_size);
+ *((uint32_t *)new_buf) = htonl(num_contexts);
+ memcpy(new_buf + 4, buf, buf_size);
+ free(buf);
+ buf = new_buf;
+ buf_size += 4;
+ }
+
+ *buf_p = buf;
+ return buf_size;
+}
+
+void op_inject(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient, const char *message) {
+ unsigned char recipient_len = strlen(recipient);
+ unsigned char protocol_len = strlen(protocol);
+ uint32_t message_len = strlen(message);
+ uint32_t total_len = 1 + recipient_len + 1 + protocol_len + 4 + message_len;
+ unsigned char *buf = malloc(total_len);
+ unsigned char *buf_head = buf;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Inject called with message %s for account %s protocol %s from account %s\n", message, recipient, protocol, accountname);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ buf[0] = recipient_len;
+ buf++;
+ strncpy(buf, recipient, recipient_len);
+ buf += recipient_len;
+
+ buf[0] = protocol_len;
+ buf++;
+ strncpy(buf, protocol, protocol_len);
+ buf += protocol_len;
+
+ *((uint32_t*)buf) = htonl(message_len);
+ buf += 4;
+ strncpy(buf, message, message_len);
+
+ send_msg(total_len, buf_head);
+ free(buf_head);
+}
+
+#ifdef OTR40
+const char* otr_error_message(void *opdata, ConnContext *context, OtrlErrorCode err_code) {
+ char* msg = "";
+ char* result;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "otr_error_message called with err_code %u\n", err_code);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ switch(err_code) {
+ case OTRL_ERRCODE_ENCRYPTION_ERROR:
+ msg = "OTRL_ERRCODE_ENCRYPTION_ERROR";
+ break;
+ case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE:
+ msg = "OTRL_ERRCODE_MSG_NOT_IN_PRIVATE";
+ break;
+ case OTRL_ERRCODE_MSG_UNREADABLE:
+ msg = "OTRL_ERRCODE_MSG_UNREADABLE";
+ break;
+ case OTRL_ERRCODE_MSG_MALFORMED:
+ msg = "OTRL_ERRCODE_MSG_MALFORMED";
+ break;
+ default:
+ break;
+ }
+
+ /* copy any info from context? */
+
+ result = malloc(strlen(msg)+1);
+ strcpy(result, msg);
+
+ return result;
+}
+#endif
+
+void otr_error_message_free(void *opdata, const char *err_msg) {
+ free((char*)err_msg);
+}
+
+/* msg should be a null-terminated string */
+void write_query_response(uint32_t id, const char* msg) {
+ write_query_response_s(id, (unsigned char*)msg, strlen(msg));
+}
+
+void write_query_response_s(uint32_t id, unsigned char* msg, uint32_t msg_size) {
+ uint32_t buf_size = 0;
+ unsigned char* buf;
+ unsigned char* buf_head;
+
+ buf_size = 4 + 4 + msg_size;
+ buf = malloc(buf_size);
+ buf_head = buf;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Writing query response to stdout -- id %u size %u msg size %u msg %s\n", id, buf_size, msg_size, msg);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ *((uint32_t*)buf) = htonl(id);
+ buf += 4;
+
+ *((uint32_t*)buf) = htonl((uint32_t)msg_size);
+ buf += 4;
+
+ memcpy(buf, msg, msg_size);
+
+ uint32_t written = buf_size;
+ written = write_stdout(buf_head, buf_size);
+
+ if (written == buf_size) {
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "%s", "Msg written to stdout\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ } else {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Only wrote %u bytes to stdout, expected %u\n", written, buf_size);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ fprintf(stderr, "Only wrote %u bytes to stdout, expected %u\n", written, buf_size);
+ }
+
+ free(buf_head);
+}
+
+#if defined OTR30 || defined OTR31 || defined OTR32
+int display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) {
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "display_otr_message called with msg %s\n", msg);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ write_query_response(Q_ID_ERR, msg);
+}
+#endif
+
+#ifdef OTR40
+void handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err) {
+ char* msg = "";
+
+ switch(msg_event) {
+ case OTRL_MSGEVENT_NONE:
+ msg = "OTRL_MSGEVENT_NONE";
+ break;
+ case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
+ msg = "OTRL_MSGEVENT_ENCRYPTION_REQUIRED";
+ break;
+ case OTRL_MSGEVENT_ENCRYPTION_ERROR:
+ msg = "OTRL_MSGEVENT_ENCRYPTION_ERROR";
+ break;
+ case OTRL_MSGEVENT_CONNECTION_ENDED:
+ msg = "OTRL_MSGEVENT_CONNECTION_ENDED";
+ break;
+ case OTRL_MSGEVENT_SETUP_ERROR:
+ msg = "OTRL_MSGEVENT_SETUP_ERROR";
+ break;
+ case OTRL_MSGEVENT_MSG_REFLECTED:
+ msg = "OTRL_MSGEVENT_MSG_REFLECTED";
+ break;
+ case OTRL_MSGEVENT_MSG_RESENT:
+ msg = "OTRL_MSGEVENT_MSG_RESENT";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
+ msg = "OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
+ msg = "OTRL_MSGEVENT_RCVDMSG_UNREADABLE";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
+ msg = "OTRL_MSGEVENT_RCVDMSG_MALFORMED";
+ break;
+ case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
+ msg = "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD";
+ break;
+ case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
+ msg = "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
+ msg = "OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
+ msg = "OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
+ msg = "OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED";
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
+ msg = "OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE";
+ break;
+ }
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Handle_msg_event called with msg_event %s errcode %i message %s \n", msg, gcry_err_code(err), message);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ write_query_response(Q_ID_ERR, msg);
+
+}
+#endif
+
+char* pass_otr_in_msg(char* account, char* protocol, char* msg) {
+ char *new_message = NULL;
+ OtrlTLV *tlvs = NULL;
+ uint32_t ignore = 0;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Passing incoming msg to OTR from account %s protocol %s payload %s\n", account, protocol, msg);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ #if defined OTR40
+ ignore = otrl_message_receiving(us, &ops, NULL,
+ our_account, protocol, account, msg,
+ &new_message, &tlvs, NULL, NULL, NULL);
+ #endif
+
+
+ #if defined OTR30 || defined OTR31 || defined OTR32
+ ignore = otrl_message_receiving(us, &ops, NULL,
+ our_account, protocol, account, msg,
+ &new_message, &tlvs, NULL, NULL);
+ #endif
+
+ if (new_message) {
+ char *ourm = malloc(strlen(new_message) + 1);
+ if (ourm) {
+ strcpy(ourm, new_message);
+ }
+
+ otrl_message_free(new_message);
+ new_message = ourm;
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "New message from OTR message_receiving %s\n", new_message);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+ }
+
+ if (ignore) {
+ free(new_message);
+ new_message = NULL;
+ }
+
+ return new_message;
+}
+
+int main(int argc, char *argv[]) {
+ char* remote_ip = DEFAULT_IP;
+ uint32_t remote_port = DEFAULT_PORT;
+ struct sockaddr_in addr;
+
+ OTRL_INIT;
+
+ us = otrl_userstate_create();
+
+ if (argc > 1) {
+ our_account = argv[1];
+ }
+
+ if (argc > 2) {
+ our_protocol = argv[2];
+ }
+
+ if (argc > 3) {
+ remote_ip = argv[3];
+ }
+
+ if (argc > 4) {
+ remote_port = atoi(argv[4]);
+ }
+
+ if (argc > 5) {
+ logfd = fopen(argv[5], "w");
+ } else {
+ logfd = fopen(our_account, "w");
+ }
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Connecting to dummy_im: %s:%u\n", remote_ip, remote_port);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ fprintf(stderr, "Cannot create socket\n");
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(remote_ip);
+ addr.sin_port = htons(remote_port);
+
+ if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Cannot connect to server\n");
+ exit(1);
+ }
+
+ pthread_mutex_init(&stdout_mutex, NULL);
+ pthread_mutex_init(&log_mutex, NULL);
+
+
+ /* pthread launch thread that receives from socket and wrties to stdout */
+ pthread_t t_socker_reader;
+ pthread_create(&t_socker_reader, NULL, receive_msgs, (void*) &sockfd);
+
+ /* the main thread will receive commands from stdin and write to socket */
+ while(1) {
+ unsigned char buf[10];
+ uint16_t command = 0;
+ uint32_t id = 0;
+ uint32_t size = 0;
+ char* payload = 0;
+
+ if (read_stdin(buf, 10) != 10) {
+ fprintf(stderr, "Error reading from stdin\n");
+ return;
+ }
+
+ command = ntohs(*((uint16_t*)(buf)));
+ id = ntohl(*((uint32_t*)(buf+2)));
+ size = ntohl(*((uint32_t*)(buf+6)));
+
+ pthread_mutex_lock(&log_mutex);
+ fprintf(logfd, "Received command msg: %X id: %u size: %u\n", command, id, size);
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ check_finished(command);
+ payload = malloc(size);
+
+ if (read_stdin(payload, size) != size) {
+ fprintf(stderr, "Error reading from stdin\n");
+ return;
+ }
+
+ pthread_mutex_lock(&log_mutex);
+
+ fprintf(logfd, "Received payload\n");
+ fflush(logfd);
+ pthread_mutex_unlock(&log_mutex);
+
+ process_command(command, id, size, &payload);
+
+ free(payload);
+ }
+
+}
diff --git a/test_suite/otr_subprocess.py b/test_suite/otr_subprocess.py
new file mode 100644
index 0000000..1161ccc
--- /dev/null
+++ b/test_suite/otr_subprocess.py
@@ -0,0 +1,482 @@
+#!/usr/bin/python
+
+import re
+import sys
+import threading
+import signal
+import select
+import time
+import Queue
+from subprocess import *
+from struct import *
+
+otr_tab_tag = " \t \t\t\t\t \t \t \t "
+
+otr_query = "?OTR?"
+
+otr_key_prefix_regex = ".*\\?OTR:...K"
+otr_commit_prefix_regex = ".*\\?OTR:...C"
+otr_sign_prefix_regex = ".*\\?OTR:...S"
+otr_reveal_prefix_regex = ".*\\?OTR:...R"
+
+otr_auth_regexs = [otr_key_prefix_regex, otr_commit_prefix_regex, otr_sign_prefix_regex, otr_reveal_prefix_regex]
+
+otr_data_prefix_regex = ".*\\?OTR:...D"
+
+im_ip = "127.0.0.1"
+im_port = "1536"
+
+q_raw_msg = 1 #The queue id for received messages that have not gone through OTR
+q_otr_msg = 2 #The queue id for received messages that have passed through OTR
+q_err = 3 #The queue id for error-type messages from OTR
+q_gone_secure = 4 #The queue id for signal of contexts that have gone secure
+q_raw_auth_msg = 5
+q_raw_data_msg = 6
+
+class otr_context:
+ def __init__(self):
+ self.protocol_version = None
+ self.username = None
+ self.accountname = None
+ self.protocol = None
+ self.offer_state = None
+ self.msg_state = None
+ self.auth_state = None
+ self.our_instance = None
+ self.their_instance = None
+
+ def __init__(self, protocol_version, username, accountname, protocol, offer_state, msg_state, auth_state, our_instance, their_instance):
+ self.protocol_version = protocol_version
+ self.username = username
+ self.accountname = accountname
+ self.protocol = protocol
+ self.offer_state = offer_state
+ self.msg_state = msg_state
+ self.auth_state = auth_state
+ self.our_instance = our_instance
+ self.their_instance = their_instance
+
+ def __str__(self):
+ return "Protocol version: " + str(self.protocol_version) + "\nUsername: " + str(self.username) + "\nAccount name: "+ \
+ str(self.accountname) + "\nProtocol: " + str(self.protocol) + "\nOffer state: " + str(self.offer_state) + \
+ "\nMsg state: " + str(self.msg_state) + "\nAuth state: " + str(self.auth_state) + "\nOur instance: " + \
+ str(self.our_instance) + "\nTheir instance: " + str(self.their_instance)
+
+class subprocess_exception(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class otr_subprocess_read_thread(threading.Thread):
+ def __init__(self, otr_subprocess):
+ threading.Thread.__init__(self)
+ self.subprocess = otr_subprocess
+
+ def run(self):
+ while True:
+ recv_msg_id = read_4b_val(self.subprocess.stdout)
+ recv_msg_len = read_4b_val(self.subprocess.stdout)
+ recv_msg = self.subprocess.stdout.read(int(recv_msg_len))
+
+ if recv_msg_id == q_raw_msg:
+ msg = deserialize_msg(recv_msg)
+ if re.match(otr_data_prefix_regex, msg[2]) != None:
+ self.subprocess.write_query(q_raw_data_msg, recv_msg)
+ else:
+ for auth_regex in otr_auth_regexs:
+ if re.match(auth_regex, msg[2]) != None:
+ self.subprocess.write_query(q_raw_auth_msg, recv_msg)
+ break
+
+
+ #print("Msg len " + str(recv_msg_len) + " contents " + recv_msg)
+
+ self.subprocess.write_query(recv_msg_id, recv_msg)
+
+class otr_subprocess(Popen):
+ def __init__(self, args, offset=0):
+ #The test client takes parameters like accountname, and protocol, and will be found
+ #in the args list. 'offset' points to the the location in the list just before the
+ #accountname and protocol are listed (and should itself point to where the actual
+ #client_location is)
+
+ self.client_location = args[offset]
+ self.accountname = args[offset+1]
+ self.protocol = args[offset+2]
+
+ print(args)
+ Popen.__init__(self, args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True, universal_newlines=True, bufsize=16384)
+
+ self.querycount = 16 # Unique IDs for async queries to C
+ self.querycount_lock = threading.Semaphore()
+
+ self.querymap = {} # Query ID --> Queues
+ self.querymap_lock = threading.Semaphore() # Locks the above map (intended to make the map operations safe, not the underlying queues)
+
+ self.querymap[q_raw_msg] = Queue.Queue() # For incoming messages to this process. This data structure is synchronized
+ self.querymap[q_otr_msg] = Queue.Queue() # For incoming messages that were process by OTR
+ self.querymap[q_err] = Queue.Queue() # For OTR error messages
+ self.querymap[q_gone_secure] = Queue.Queue()
+ self.querymap[q_raw_auth_msg] = Queue.Queue()
+ self.querymap[q_raw_data_msg] = Queue.Queue()
+
+ self.read_thread = otr_subprocess_read_thread(self)
+ self.read_thread.daemon = True # Python will exit when only daemonic threads are left
+ self.read_thread.start()
+
+ def get_query_id(self):
+ result = 0
+ self.querycount_lock.acquire()
+ result = self.querycount
+ self.querycount += 1
+ self.querycount_lock.release()
+ return result
+
+ def reset(self):
+ while not self.querymap[q_raw_msg].empty():
+ self.querymap[q_raw_msg].get(False)
+ while not self.querymap[q_otr_msg].empty():
+ self.querymap[q_otr_msg].get(False)
+ while not self.querymap[q_err].empty():
+ self.querymap[q_err].get(False)
+
+ def write_query(self, id, contents):
+ self.querymap_lock.acquire()
+
+ queue = self.querymap.get(id)
+
+ if queue is None:
+ queue = Queue.Queue()
+ self.querymap[id] = queue
+
+ queue.put(contents)
+
+ self.querymap_lock.release()
+
+ def get_query_blocking(self, id):
+ self.querymap_lock.acquire()
+
+ result = None
+ queue = self.querymap.get(id)
+
+ if queue is None:
+ queue = Queue.Queue()
+ self.querymap[id] = queue
+
+ self.querymap_lock.release()
+ result = queue.get()
+
+ return result
+
+ def check_error(self):
+ inputready,outputready,exceptready = select.select([self.stderr],[],[], 0) #non-blocking poll
+
+ for s in inputready:
+ raise subprocess_exception(s.readline())
+
+ if self.poll():
+ raise subprocess_exception('Subprocess terminated with return code: ' + str(self.returncode))
+
+ def send_init(self):
+ new_msg = bytearray()
+ acc_len = len(self.accountname)
+ proto_len = len(self.protocol)
+ full_len = 1 + acc_len + 1 + proto_len
+
+ cmd_type = 0 #cmd type 0 (init)
+
+ for b in pack('!H', cmd_type):
+ new_msg.append(ord(b))
+
+ msg_id = self.get_query_id()
+ for b in pack('!I', msg_id):
+ new_msg.append(ord(b))
+
+ for b in pack('!I', full_len):
+ new_msg.append(ord(b))
+
+ new_msg.append(acc_len)
+ new_msg.extend(self.accountname)
+
+ new_msg.append(proto_len)
+ new_msg.extend(self.protocol)
+
+ self.stdin.write(new_msg)
+ self.stdin.flush()
+ time.sleep(0.1)
+ self.check_error()
+
+ return msg_id
+
+ def send_msg(self, dst_acc, dst_proto, msg):
+ new_msg = bytearray()
+ acc_len = len(dst_acc)
+ proto_len = len(dst_proto)
+ msg_len = len(msg)
+ full_len = 1 + acc_len + 1 + proto_len + 4 + msg_len
+
+ cmd_type = 1 #cmd type 1 (message)
+ for b in pack('!H', cmd_type):
+ new_msg.append(ord(b))
+
+ msg_id = self.get_query_id()
+ for b in pack('!I', msg_id):
+ new_msg.append(ord(b))
+
+ for b in pack('!I', full_len):
+ new_msg.append(ord(b))
+
+ new_msg.append(acc_len)
+ new_msg.extend(dst_acc)
+
+ new_msg.append(proto_len)
+ new_msg.extend(dst_proto)
+
+ for b in pack('!I', msg_len):
+ new_msg.append(ord(b))
+ new_msg.extend(msg)
+
+ self.stdin.write(new_msg)
+ self.stdin.flush()
+
+ time.sleep(0.1)
+ self.check_error()
+
+ return msg_id
+
+ def send_read_privkey(self, path):
+ new_msg = bytearray()
+
+ cmd_type = 2 #cmd type 2 (read privkey)
+
+ for b in pack('!H', cmd_type):
+ new_msg.append(ord(b))
+
+ msg_id = self.get_query_id()
+ for b in pack('!I', msg_id):
+ new_msg.append(ord(b))
+
+ full_len = len(path)
+
+ for b in pack('!I', full_len):
+ new_msg.append(ord(b))
+
+ new_msg.extend(path)
+ self.stdin.write(new_msg)
+ self.stdin.flush()
+
+ time.sleep(0.1)
+ self.check_error()
+
+ return msg_id
+
+ def send_read_instag(self, path):
+ new_msg = bytearray()
+
+ cmd_type = 4 #cmd type 4 (read instag)
+
+ for b in pack('!H', cmd_type):
+ new_msg.append(ord(b))
+
+ msg_id = self.get_query_id()
+ for b in pack('!I', msg_id):
+ new_msg.append(ord(b))
+
+ full_len = len(path)
+
+ for b in pack('!I', full_len):
+ new_msg.append(ord(b))
+
+ new_msg.extend(path)
+ self.stdin.write(new_msg)
+ self.stdin.flush()
+
+ time.sleep(0.1)
+ self.check_error()
+
+ return msg_id
+
+
+ def send_get_contexts(self):
+ new_msg = bytearray()
+
+ cmd_type = 3 #cmd type 3 (get contexts)
+
+ for b in pack('!H', cmd_type):
+ new_msg.append(ord(b))
+
+ msg_id = self.get_query_id()
+ for b in pack('!I', msg_id):
+ new_msg.append(ord(b))
+
+ full_len = 1
+
+ for b in pack('!I', full_len):
+ new_msg.append(ord(b))
+
+ new_msg.append(ord('\0')) #We don't support zero len so this is dummy data
+ self.stdin.write(new_msg)
+ self.stdin.flush()
+
+ time.sleep(0.1)
+ self.check_error()
+
+ return msg_id
+
+
+ def sigint(self):
+ self.send_signal(signal.SIGINT)
+
+#TODO: convert following methods to class methods or static methods?
+
+def deserialize_msg(raw_msg):
+ i = 0
+
+ account_len = (unpack('B', raw_msg[i:i+1]))[0]
+ i += 1
+
+ account = raw_msg[i:i+account_len]
+ i += account_len
+
+ protocol_len = (unpack('B', raw_msg[i:i+1]))[0]
+ i += 1
+
+ protocol = raw_msg[i:i+protocol_len]
+ i += protocol_len
+
+ msg_len = (unpack('!I',raw_msg[i:i+4]))[0]
+ i += 4
+
+ msg = raw_msg[i:i+msg_len]
+
+ return [account, protocol, msg]
+
+def deserialize_context(contexts, serialized_context):
+ i = 0
+
+ protocol_version = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ username_len = ord(serialized_context[i])
+ i += 1
+
+ username = serialized_context[i:i+username_len]
+ i += username_len
+
+ accountname_len = ord(serialized_context[i])
+ i += 1
+
+ accountname = serialized_context[i:i+accountname_len]
+ i += accountname_len
+
+ protocol_len = ord(serialized_context[i])
+ i += 1
+
+ protocol = serialized_context[i:i+protocol_len]
+ i += protocol_len
+
+ otr_offer = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ msg_state = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ auth_state = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ our_instance = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ their_instance = (unpack('!I',serialized_context[i:i+4]))[0]
+ i += 4
+
+ #if protocol_version < 3:
+ # our_instance = None
+ # their_instance = None
+
+ context = otr_context(protocol_version, username, accountname, protocol, otr_offer, msg_state, auth_state, our_instance, their_instance)
+ contexts.append(context)
+
+ return serialized_context[i:]
+
+def deserialize_contexts(serialized_contexts):
+ num_contexts = (unpack('!I',serialized_contexts[0:4]))[0]
+
+ serialized_contexts = serialized_contexts[4:]
+ contexts = []
+
+ for i in range(num_contexts):
+ serialized_contexts = deserialize_context(contexts, serialized_contexts)
+
+ return contexts
+
+def get_n_messages_blocking(queue, n):
+ result = []
+
+ for i in range(n):
+ result.append(queue.get())
+
+ return result
+
+def read_bytes(stream, num_bytes):
+ msg = ''
+ while len(msg) < num_bytes:
+ chunk = stream.read(num_bytes-len(msg))
+ if chunk is None:
+ return
+ msg = msg + chunk
+ return msg
+
+def read_1b_val(stream):
+ byte = read_bytes(stream, 1)
+
+ if len(byte) == 1:
+ return ord(byte)
+ else:
+ print("Read unexpected length: " + str(len(byte)))
+ return
+
+def read_4b_val(stream):
+ bytes = read_bytes(stream, 4)
+ if len(bytes) == 4:
+ val = unpack('!I',bytes) #network byte order
+ return int(val[0])
+ else:
+ print("Read unexpected length: " + str(len(byte)))
+ return
+
+def dump_msg_queue(queue):
+ while not queue.empty():
+ item = queue.get(False)
+ if item is not None:
+ item = deserialize_msg(item)
+ print('###############')
+ print(item)
+ print('###############')
+
+def dump_queue(queue):
+ while not queue.empty():
+ item = queue.get(False)
+ print('###############')
+ print(str(item))
+ print('###############')
+
+def dump_contexts(contexts):
+ for context in contexts:
+ print('***************')
+ print(str(context))
+ print('***************')
+
+
+def get_many_instances(location, account, protocol, num, log_tag):
+ result = []
+ for i in range(num):
+ #result.append(otr_subprocess(['/usr/bin/valgrind', '--tool=memcheck', '--leak-check=yes', '--show-reachable=yes', '--num-callers=20', '--track-fds=yes', '--log-file=valgrind_log' + account + protocol + str(i) + '.txt', location, account, protocol, im_ip, im_port, log_tag+str(i)+".txt"], 7))
+ result.append(otr_subprocess([location, account, protocol, im_ip, im_port, log_tag+str(i)+".txt"], 0))
+
+ return result
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/test_suite/otr_test.py b/test_suite/otr_test.py
new file mode 100644
index 0000000..483952d
--- /dev/null
+++ b/test_suite/otr_test.py
@@ -0,0 +1,250 @@
+#!/usr/bin/python
+
+import sys
+import itertools
+import re
+import Queue
+
+from dummy_im import *
+from otr_subprocess import *
+
+client_location_30 = "./otr_c_client/dummy_client_30"
+client_location_31 = "./otr_c_client/dummy_client_31"
+client_location_32 = "./otr_c_client/dummy_client_32"
+client_location_40 = "./otr_c_client/dummy_client_40"
+
+client_locations = [client_location_30, client_location_31, client_location_32, client_location_40]
+
+
+class otr_test_failed_exception(Exception):
+ def __init__(self, value, p_list=None):
+ self.value = value
+ self.p_list = p_list
+ if p_list is not None:
+ for p in p_list:
+ q_id = p.send_get_contexts()
+ serialized_contexts = p.get_query_blocking(q_id)
+ p_contexts = deserialize_contexts(serialized_contexts)
+ print("Dumping contexts:")
+ dump_contexts(p_contexts)
+ print("Dumping raw messages:")
+ dump_msg_queue(p.querymap[q_raw_msg])
+ print("Dumping otr-processed messages:")
+ dump_msg_queue(p.querymap[q_otr_msg])
+ print("Dumping error messages:")
+ dump_queue(p.querymap[q_err])
+
+ def __str__(self):
+ return "Test Failed: " + repr(self.value)
+
+class otr_test:
+ def __init__(self, alices, bobs):
+
+ self.alices = alices
+ self.bobs = bobs
+ self.subprocesses = alices + bobs
+
+ #no default tests -- always defined by child class
+ def run_test(self, options={}):
+ return
+
+ def reset_processes(self):
+ for p in self.subprocesses:
+ p.sigint()
+
+ #Give them a moment to terminate on their own if they are willing and able
+ time.sleep(0.5)
+
+ for p in self.subprocesses:
+ p.reset()
+
+ def check_error_all(self, allowed_events=[]):
+ for p in self.subprocesses:
+ p.check_error()
+ if not p.querymap[q_err].empty():
+ tmp_stack = []
+
+ while not p.querymap[q_err].empty():
+ item = p.querymap[q_err].get(False)
+ if allowed_events.count(item) < 1:
+ raise otr_test_failed_exception(item, [p])
+ tmp_stack.append(item)
+
+ while len(tmp_stack) > 0:
+ p.querymap[q_err].put(tmp_stack.pop())
+
+
+ def wait_all_gone_encrypted(self):
+ for p in self.subprocesses:
+ p.get_query_blocking(q_gone_secure) #Wait for "gone encrypted" signal
+ print(p.accountname + " " + p.protocol + " went encrypted")
+
+ def send_encrypted_and_check(self, senders, receivers, out_msg, options={}):
+ for p in list(set(senders)):
+ print("About to send encrypted message")
+ msg_id = p.send_msg(receivers[0].accountname, receivers[0].protocol, out_msg)
+ modified_msg = p.get_query_blocking(msg_id)
+ print("Encrypted message sent")
+
+ if modified_msg.find(out_msg) >= 0:
+ raise otr_test_failed_exception("Sent message not encrypted", self.subprocesses)
+
+ receiver_msgs = []
+
+ for p in list(set(receivers)): #remove duplicates
+ print("About to receive raw encrypted message")
+ receiver_msgs.append(get_n_messages_blocking(p.querymap[q_raw_data_msg], len(list(set(senders)))))
+
+ for msg in receiver_msgs:
+ check_contains_message_matches_ex(msg, otr_data_prefix_regex, len(list(set(senders))), self.subprocesses)
+
+ for i in range(len(list(set(senders)))):
+ print("About to receive decrypted message")
+ receiver_recvd = find_first_msg_blocking(receivers, q_otr_msg)
+
+ if not check_message_matches(receiver_recvd, re.escape(out_msg)):
+ raise otr_test_failed_exception("Received message not decrypted", self.subprocesses)
+
+ def check_all_contexts_encrypted(self, options={}):
+ for p in self.alices:
+ c_id = p.send_get_contexts()
+ serialized_contexts = p.get_query_blocking(c_id)
+ p_contexts = deserialize_contexts(serialized_contexts)
+ check_contexts_for_encrypted(p_contexts, self.num_bob, p)
+
+ for p in self.bobs:
+ c_id = p.send_get_contexts()
+ serialized_contexts = p.get_query_blocking(c_id)
+ p_contexts = deserialize_contexts(serialized_contexts)
+ check_contexts_for_encrypted(p_contexts, self.num_alice, p)
+
+ def init_processes(self, options={}):
+ for p in self.subprocesses:
+ p.check_error()
+
+ privkey = options.get('privkey', 'otr.private_key')
+
+ for p in self.subprocesses:
+ c_id = p.send_read_privkey(privkey)
+ p.get_query_blocking(c_id)
+
+ for i, p in enumerate(self.alices):
+ c_id = p.send_read_instag("instance_tags" + str(i) + ".txt")
+ p.get_query_blocking(c_id)
+
+ for i, p in enumerate(self.bobs):
+ c_id = p.send_read_instag("instance_tags" + str(i) + ".txt")
+ p.get_query_blocking(c_id)
+
+ for p in self.subprocesses:
+ c_id = p.send_init()
+ p.get_query_blocking(c_id)
+
+ for p in self.subprocesses:
+ p.check_error()
+
+ def otr_init_msg(self, options={}):
+ alice_init_idx = options.get('alice_init_idx', 0)
+ msg_id = self.alices[alice_init_idx].send_msg(self.bob_account, self.bob_proto, self.msg1)
+ modified_msg = self.alices[alice_init_idx].get_query_blocking(msg_id)
+ done = self.alices[alice_init_idx].get_query_blocking(msg_id)
+
+ for p in self.bobs:
+ msg = p.get_query_blocking(q_otr_msg)
+ if check_message_matches(msg, re.escape(self.msg1) + otr_tab_tag):
+ raise otr_test_failed_exception("Query tabs not removed", self.subprocesses)
+
+ check_message_matches_ex(msg, re.escape(self.msg1), self.subprocesses)
+
+ def otr_init_query(self, options={}):
+ msg_id = self.alices[0].send_msg(self.bob_account, self.bob_proto, otr_query)
+ modified_msg = self.alices[0].get_query_blocking(msg_id)
+ done = ""
+ while done.find("DONE") < 0:
+ done = self.alices[0].get_query_blocking(msg_id)
+
+ #More to check?
+
+#TODO: Make these methods class methods / static methods?
+def chomp_auth_msgs(ps):
+ for p in ps:
+ msgs = get_n_messages_blocking(p.querymap[q_raw_msg], p.querymap[q_raw_msg].qsize())
+ for msg in msgs:
+ parsed_msg = deserialize_msg(msg)[2]
+ if parsed_msg.find("?OTR") != 0:
+ p.querymap[q_raw_msg].put(msg)
+
+def chomp_msgs(ps, q_idx):
+ for p in ps:
+ get_n_messages_blocking(p.querymap[q_idx], p.querymap[q_idx].qsize())
+
+
+def wait_gone_encrypted(ps):
+ for p in ps:
+ p.get_query_blocking(q_gone_secure) #Wait for "gone encrypted" signal
+
+def find_first_msg_blocking(ps, q_idx):
+ #We have to spin here until this is implemented:
+ # http://bugs.python.org/issue3831
+ result = None
+
+ while result is None:
+ for p in ps:
+ try:
+ result = p.querymap[q_idx].get(True, 0.05) #block for 50ms
+ break
+ except (Queue.Empty) as e:
+ continue
+
+ return result
+
+def check_message_matches(msg, regex):
+ msg = deserialize_msg(msg)
+ return re.match(regex, msg[2]) is not None
+
+def check_message_matches_ex(msg, regex, p_list):
+ if not check_message_matches(msg, regex):
+ raise otr_test_failed_exception("msg failed to match regex -- regex: " + regex + " msg: " + msg[2], p_list)
+
+def check_contains_message_matches_ex(msgs, regex, n, p_list):
+ matches = 0
+ for msg in msgs:
+ if check_message_matches(msg, regex):
+ matches += 1
+
+ if matches < n:
+ raise otr_test_failed_exception("msg failed to match regex " + str(n) + " times -- regex: " + regex + " matches: " + str(matches) + " msgs: " + str(msgs), p_list)
+
+
+#Given a process's list of contexts, check that num_expected_encrypted are encrypted
+def check_contexts_for_encrypted(contexts, num_expected_encrypted, p):
+ num_good = 0
+
+ if num_expected_encrypted > 1 and p.client_location != client_location_40: #XXX: support future versions
+ raise otr_test_failed_exception("Expected > 1 good contexts on protocol version < 2", [p])
+
+ if len(contexts) == 0:
+ raise otr_test_failed_exception("Failed: No contexts", [p])
+
+ if p.client_location != client_location_40 and num_expected_encrypted == 1 and len(contexts) == 1:
+ if not check_context_encrypted(contexts[0], p):
+ raise otr_test_failed_exception("Failed: Not encrypted", [p])
+
+ else:
+ for context in contexts:
+ if check_context_encrypted(context, p):
+ num_good += 1
+
+ if num_good < num_expected_encrypted:
+ msg = "Failed: fewer than expected encrypted contexts. Found " + str(num_good) + " Expected " + str(num_expected_encrypted)
+ raise otr_test_failed_exception(msg, [p])
+
+def check_offer_accepted(context):
+ if context.offer_state != 3:
+ raise otr_test_failed_exception("Failed: Offer not given or not accepted")
+
+def check_context_encrypted(context, p):
+ if context.msg_state == 1:
+ return True
+ else:
+ return False
diff --git a/test_suite/otr_test_general.py b/test_suite/otr_test_general.py
new file mode 100644
index 0000000..9928c10
--- /dev/null
+++ b/test_suite/otr_test_general.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+
+import sys
+import itertools
+import re
+import Queue
+
+from otr_test import *
+
+class otr_test_general(otr_test):
+ #Supports multiple 4.0 clients or single 3.X clients
+
+ def __init__(self, alices, bobs):
+ otr_test.__init__(self, alices, bobs)
+
+ self.alice_account = alices[0].accountname
+ self.alice_proto = alices[0].protocol
+
+ self.bob_account = bobs[0].accountname
+ self.bob_proto = bobs[0].protocol
+
+ self.num_alice = len(self.alices)
+ self.num_bob = len(self.bobs)
+
+ def run_test(self, options={}):
+ try:
+ self.msg1 = options.get('msg1', '%$sup?')
+ self.msg2 = options.get('msg2', '^¬ much')
+ self.msg3 = options.get('msg3', '*(cool')
+ self.init_processes(options)
+ time.sleep(1)
+ print("Processes initialized")
+ if options.get('otr_init_method', 'msg') == 'msg':
+ self.otr_init_msg(options)
+ elif options.get('otr_init_method', 'msg') == 'query':
+ self.otr_init_query()
+ print("About to wait for all to go encrypted")
+ self.wait_all_gone_encrypted()
+ print("All went encrypted")
+ #self.analyze_otr_init(options) #hasn't been updated to support fragments
+ time.sleep(5)
+ self.check_all_contexts_encrypted(options)
+ print("Verified encrypted contexts")
+ self.send_encrypted_and_check(self.bobs, self.alices, self.msg2, options)
+ print("Sent and verified encrypted message from bobs to alices")
+ self.send_encrypted_and_check(self.alices, self.bobs, self.msg3, options)
+ print("Sent and verified encrypted message from alices to bobs")
+ self.check_error_all(options.get('allowed_msg_events', []))
+ print("Test succeeded")
+ self.reset_processes()
+
+ except (subprocess_exception, otr_test_failed_exception) as e:
+ print '***Exception: ', e, e.value, sys.exc_info()
+ self.reset_processes()
+ print("Test failed")
+
+ def analyze_otr_init(self, options={}):
+ alice_msgs = []
+ bob_msgs = []
+
+ for p in self.bobs:
+ init_msg = p.querymap[q_raw_msg].get()
+
+ if options.get('otr_init_method', 'msg') == 'msg':
+ check_contains_message_matches_ex([init_msg], re.escape(self.msg1) + otr_tab_tag, 1, self.subprocesses)
+ elif options.get('otr_init_method', 'msg') == 'query':
+ check_contains_message_matches_ex([init_msg], re.escape(otr_query), 1, self.subprocesses)
+
+ bob_msgs.append(get_n_messages_blocking(p.querymap[q_raw_auth_msg], self.num_alice*self.num_bob*2))
+
+ for p in self.alices:
+ alice_msgs.append(get_n_messages_blocking(p.querymap[q_raw_auth_msg], self.num_bob + self.num_bob*self.num_alice))
+
+ for msg in bob_msgs:
+ check_contains_message_matches_ex(msg, otr_key_prefix_regex, self.num_alice*self.num_bob, self.subprocesses)
+ check_contains_message_matches_ex(msg, otr_sign_prefix_regex, self.num_alice*self.num_bob, self.subprocesses)
+
+ for msg in alice_msgs:
+ check_contains_message_matches_ex(msg, otr_commit_prefix_regex, self.num_bob, self.subprocesses)
+ check_contains_message_matches_ex(msg, otr_reveal_prefix_regex, self.num_bob*self.num_alice, self.subprocesses)
+
+
+def test_all_vers_1_1():
+ alice_accountname = "otrtest3"
+ bob_accountname = "otrtest1"
+ protocol = "prpl-aim"
+
+ test_combos = []
+
+ for loc1, loc2 in itertools.product(client_locations, repeat=2):
+ test_combos.append((loc1, loc2))
+
+ for i, (loc1, loc2) in enumerate(test_combos): #otr_subprocess([location, account, protocol, im_ip, im_port, log_tag+str(i)+".txt"], 0)
+ alice = otr_subprocess([loc1, alice_accountname, protocol, im_ip, im_port, "alice"+str(i)+".txt"], 0)
+ bob = otr_subprocess([loc2, bob_accountname, protocol, im_ip, im_port, "bob"+str(i)+".txt"], 0)
+
+ print('Testing ' + loc1 + ' and ' + loc2)
+
+ the_test = otr_test_general([alice], [bob])
+ options={}
+ options['otr_init_method'] = 'msg'
+ the_test.run_test(options)
+ time.sleep(1)
+
+ print('Test complete!')
+
+def test_basic_40():
+ #tests single 4.0s
+ alice_accountname = "otrtest3"
+ bob_accountname = "otrtest1"
+ protocol = "prpl-aim"
+
+ alices = get_many_instances(client_location_40, alice_accountname, protocol, 1, "alice")
+ bobs = get_many_instances(client_location_40, bob_accountname, protocol, 1, "bob")
+
+ the_test = otr_test_general(alices, bobs)
+ options={}
+ options['otr_init_method'] = 'query'
+ options['allowed_msg_events'] = ["OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"]
+ the_test.run_test(options)
+
+ print('Test complete!')
+
+
+def test_multi_40():
+ #tests multiple 4.0 versions
+ alice_accountname = "otrtest3"
+ bob_accountname = "otrtest1"
+ protocol = "prpl-aim"
+
+ alices = get_many_instances(client_location_40, alice_accountname, protocol, 3, "alice")
+ bobs = get_many_instances(client_location_40, bob_accountname, protocol, 2, "bob")
+
+ the_test = otr_test_general(alices, bobs)
+ options={}
+ options['otr_init_method'] = 'query'
+ options['allowed_msg_events'] = ["OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE", "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"]
+ the_test.run_test(options)
+
+ print('Test complete!')
+
+
+def main(args):
+ server = im_server(int(im_port))
+ server.daemon = True
+ server.start()
+
+ print('Testing basic 4.0 to 4.0')
+ test_basic_40()
+
+ print('Testing all versions 1 client to 1 client')
+ test_all_vers_1_1()
+
+ print('Testing multi 4.0 to multi 4.0')
+ test_multi_40()
+
+ server.set_finished()
+ print("Shutting down...")
+
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/test_suite/otr_test_mixed.py b/test_suite/otr_test_mixed.py
new file mode 100644
index 0000000..7bef2c3
--- /dev/null
+++ b/test_suite/otr_test_mixed.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+import sys
+import itertools
+import re
+import Queue
+
+from otr_test import *
+
+class otr_test_mixed(otr_test):
+ #Supports multiple 4.0 clients or single 3.X clients
+
+ def __init__(self, alices, bobs):
+ otr_test.__init__(self, alices, bobs)
+
+ self.alice_account = alices[0].accountname
+ self.alice_proto = alices[0].protocol
+
+ self.bob_account = bobs[0].accountname
+ self.bob_proto = bobs[0].protocol
+
+ self.num_alice = len(self.alices)
+ self.num_bob = len(self.bobs)
+
+ def run_test(self, options={}):
+ try:
+ self.msg1 = options.get('msg1', '%$sup?')
+ self.msg2 = options.get('msg2', '^¬ much')
+ self.msg3 = options.get('msg3', '*(cool')
+
+ self.init_processes(options)
+
+ if options.get('otr_init_method', 'msg') == 'msg':
+ self.otr_init_msg(options)
+ elif options.get('otr_init_method', 'msg') == 'query':
+ self.otr_init_query()
+
+ alice_encrypted_idx = options.get('alice_expected_encrypted_idx', range(len(self.alices)))
+ bob_encrypted_idx = options.get('bob_expected_encrypted_idx', range(len(self.bobs)))
+
+ alice_encrypted = []
+ for i in alice_encrypted_idx:
+ alice_encrypted.append(self.alices[i])
+
+ bob_encrypted = []
+ for i in bob_encrypted_idx:
+ bob_encrypted.append(self.bobs[i])
+
+
+ wait_gone_encrypted(alice_encrypted + bob_encrypted)
+ time.sleep(1) #Ensure all messages received and processed
+
+ chomp_msgs(alice_encrypted + bob_encrypted, q_raw_msg)
+
+ #Check contexts
+ for p in list(set(alice_encrypted)):
+ c_id = p.send_get_contexts()
+ serialized_contexts = p.get_query_blocking(c_id)
+ p_contexts = deserialize_contexts(serialized_contexts)
+ check_contexts_for_encrypted(p_contexts, alice_encrypted.count(p), p)
+
+
+ for p in list(set(bob_encrypted)):
+ c_id = p.send_get_contexts()
+ serialized_contexts = p.get_query_blocking(c_id)
+ p_contexts = deserialize_contexts(serialized_contexts)
+ check_contexts_for_encrypted(p_contexts, bob_encrypted.count(p), p)
+
+
+ #We only send messages between the 4.0s because the 3.X will be paired with only one partner, and we don't know
+ #for sure which one.
+ self.send_encrypted_and_check(return_only_40(bob_encrypted), return_only_40(alice_encrypted), self.msg2, options)
+
+ self.send_encrypted_and_check(return_only_40(alice_encrypted), return_only_40(bob_encrypted), self.msg3, options)
+
+ #Non-4.0 clients will error from unexpected messages, but we are expecting this
+ for p in self.subprocesses:
+ p.check_error()
+ if not p.querymap[q_err].empty() and p.client_location == client_location_40:
+ errors = get_n_messages_blocking(p.querymap[q_err], p.querymap[q_err].qsize())
+ for error in errors:
+ print("Warning: setup errors detected: " + error)
+
+ print("Test succeeded")
+ self.reset_processes()
+
+ except (subprocess_exception, otr_test_failed_exception) as e:
+ print '***Exception: ', e.value, sys.exc_info()
+ self.reset_processes()
+ print("Test failed")
+
+def return_only_40(processes):
+ result = []
+ for p in processes:
+ if p.client_location == client_location_40:
+ result.append(p)
+
+ return result
+
+def test_40_mixed(num_alice_40, num_bob_40, alice_extra_location=None, bob_extra_location=None, options={}):
+ alice_accountname = "otrtest3"
+ bob_accountname = "otrtest1"
+ protocol = "prpl-aim"
+
+ alices = get_many_instances(client_location_40, alice_accountname, protocol, num_alice_40, "alice")
+ bobs = get_many_instances(client_location_40, bob_accountname, protocol, num_bob_40, "bob")
+
+ if alice_extra_location is not None:
+ alices.append(otr_subprocess([alice_extra_location, alice_accountname, protocol, im_ip, im_port, "alice" + str(num_alice_40) + ".txt"], 0))
+
+ if bob_extra_location is not None:
+ bobs.append(otr_subprocess([bob_extra_location, bob_accountname, protocol, im_ip, im_port, "bob" + str(num_bob_40) + ".txt"], 0))
+
+ the_test = otr_test_mixed(alices, bobs)
+
+ options['otr_init_method'] = 'msg'
+ options['allowed_msg_events'] = ["OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE", "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"]
+ the_test.run_test(options)
+
+
+def main(args):
+ print('Testing 4.0 and other to 4.0')
+ server = im_server(int(im_port))
+ server.daemon = True
+ server.start()
+
+ options = {}
+ options['alice_init_idx'] = 0
+ options['alice_expected_encrypted_idx'] = [0, 0, 1, 1, 2, 2]
+ options['bob_expected_encrypted_idx'] = [0, 0, 0, 1, 1, 1, 2]
+
+ for location in client_locations:
+ if location == client_location_40: continue
+ print("Testing with extra Bob location: " + location)
+ test_40_mixed(3, 2, None, location, options)
+ time.sleep(1)
+
+
+ options['alice_expected_encrypted_idx'] = [0, 1]
+ options['bob_expected_encrypted_idx'] = [0, 0, 1, 1]
+
+ for location in client_locations:
+ if location == client_location_40: continue
+ print("Testing with extra Alice location: " + location)
+ test_40_mixed(2, 2, location, None, options)
+ time.sleep(1)
+
+ server.set_finished()
+ print("Shutting down...")
+
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/toolkit/aes.c b/toolkit/aes.c
index 24b9b56..046eed7 100644
--- a/toolkit/aes.c
+++ b/toolkit/aes.c
@@ -36,10 +36,10 @@
/* forward S-box & tables */
uint32 FSb[256];
-uint32 FT0[256];
-uint32 FT1[256];
-uint32 FT2[256];
-uint32 FT3[256];
+uint32 FT0[256];
+uint32 FT1[256];
+uint32 FT2[256];
+uint32 FT3[256];
/* reverse S-box & tables */
@@ -60,7 +60,7 @@ int do_init = 1;
/* tables generation routine */
#define ROTR8(x) ( ( ( x << 24 ) & 0xFFFFFFFF ) | \
- ( ( x & 0xFFFFFFFF ) >> 8 ) )
+ ( ( x & 0xFFFFFFFF ) >> 8 ) )
#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) )
#define MUL(x,y) ( ( x && y ) ? pow[(log[x] + log[y]) % 255] : 0 )
@@ -76,15 +76,15 @@ void aes_gen_tables( void )
for( i = 0, x = 1; i < 256; i++, x ^= XTIME( x ) )
{
- pow[i] = x;
- log[x] = i;
+ pow[i] = x;
+ log[x] = i;
}
/* calculate the round constants */
for( i = 0, x = 1; i < 10; i++, x = XTIME( x ) )
{
- RCON[i] = (uint32) x << 24;
+ RCON[i] = (uint32) x << 24;
}
/* generate the forward and reverse S-boxes */
@@ -94,47 +94,47 @@ void aes_gen_tables( void )
for( i = 1; i < 256; i++ )
{
- x = pow[255 - log[i]];
+ x = pow[255 - log[i]];
- y = x; y = ( y << 1 ) | ( y >> 7 );
- x ^= y; y = ( y << 1 ) | ( y >> 7 );
- x ^= y; y = ( y << 1 ) | ( y >> 7 );
- x ^= y; y = ( y << 1 ) | ( y >> 7 );
- x ^= y ^ 0x63;
+ y = x; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y ^ 0x63;
- FSb[i] = x;
- RSb[x] = i;
+ FSb[i] = x;
+ RSb[x] = i;
}
/* generate the forward and reverse tables */
for( i = 0; i < 256; i++ )
{
- x = (unsigned char) FSb[i]; y = XTIME( x );
+ x = (unsigned char) FSb[i]; y = XTIME( x );
- FT0[i] = (uint32) ( x ^ y ) ^
- ( (uint32) x << 8 ) ^
- ( (uint32) x << 16 ) ^
- ( (uint32) y << 24 );
+ FT0[i] = (uint32) ( x ^ y ) ^
+ ( (uint32) x << 8 ) ^
+ ( (uint32) x << 16 ) ^
+ ( (uint32) y << 24 );
- FT0[i] &= 0xFFFFFFFF;
+ FT0[i] &= 0xFFFFFFFF;
- FT1[i] = ROTR8( FT0[i] );
- FT2[i] = ROTR8( FT1[i] );
- FT3[i] = ROTR8( FT2[i] );
+ FT1[i] = ROTR8( FT0[i] );
+ FT2[i] = ROTR8( FT1[i] );
+ FT3[i] = ROTR8( FT2[i] );
- y = (unsigned char) RSb[i];
+ y = (unsigned char) RSb[i];
- RT0[i] = ( (uint32) MUL( 0x0B, y ) ) ^
- ( (uint32) MUL( 0x0D, y ) << 8 ) ^
- ( (uint32) MUL( 0x09, y ) << 16 ) ^
- ( (uint32) MUL( 0x0E, y ) << 24 );
+ RT0[i] = ( (uint32) MUL( 0x0B, y ) ) ^
+ ( (uint32) MUL( 0x0D, y ) << 8 ) ^
+ ( (uint32) MUL( 0x09, y ) << 16 ) ^
+ ( (uint32) MUL( 0x0E, y ) << 24 );
- RT0[i] &= 0xFFFFFFFF;
+ RT0[i] &= 0xFFFFFFFF;
- RT1[i] = ROTR8( RT0[i] );
- RT2[i] = ROTR8( RT1[i] );
- RT3[i] = ROTR8( RT2[i] );
+ RT1[i] = ROTR8( RT0[i] );
+ RT2[i] = ROTR8( RT1[i] );
+ RT3[i] = ROTR8( RT2[i] );
}
}
@@ -412,9 +412,9 @@ void aes_gen_tables( void )
#define GET_UINT32(n,b,i) \
{ \
(n) = ( (uint32) (b)[(i) ] << 24 ) \
- | ( (uint32) (b)[(i) + 1] << 16 ) \
- | ( (uint32) (b)[(i) + 2] << 8 ) \
- | ( (uint32) (b)[(i) + 3] ); \
+ | ( (uint32) (b)[(i) + 1] << 16 ) \
+ | ( (uint32) (b)[(i) + 2] << 8 ) \
+ | ( (uint32) (b)[(i) + 3] ); \
}
#define PUT_UINT32(n,b,i) \
@@ -443,24 +443,24 @@ int aes_set_key( aes_context *ctx, uint8 *key, int nbits )
if( do_init )
{
- aes_gen_tables();
+ aes_gen_tables();
- do_init = 0;
+ do_init = 0;
}
switch( nbits )
{
- case 128: ctx->nr = 10; break;
- case 192: ctx->nr = 12; break;
- case 256: ctx->nr = 14; break;
- default : return( 1 );
+ case 128: ctx->nr = 10; break;
+ case 192: ctx->nr = 12; break;
+ case 256: ctx->nr = 14; break;
+ default : return( 1 );
}
RK = ctx->erk;
for( i = 0; i < (nbits >> 5); i++ )
{
- GET_UINT32( RK[i], key, i * 4 );
+ GET_UINT32( RK[i], key, i * 4 );
}
/* setup encryption round keys */
@@ -469,78 +469,78 @@ int aes_set_key( aes_context *ctx, uint8 *key, int nbits )
{
case 128:
- for( i = 0; i < 10; i++, RK += 4 )
- {
- RK[4] = RK[0] ^ RCON[i] ^
- ( FSb[ (uint8) ( RK[3] >> 16 ) ] << 24 ) ^
- ( FSb[ (uint8) ( RK[3] >> 8 ) ] << 16 ) ^
- ( FSb[ (uint8) ( RK[3] ) ] << 8 ) ^
- ( FSb[ (uint8) ( RK[3] >> 24 ) ] );
+ for( i = 0; i < 10; i++, RK += 4 )
+ {
+ RK[4] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[3] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[3] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[3] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[3] >> 24 ) ] );
- RK[5] = RK[1] ^ RK[4];
- RK[6] = RK[2] ^ RK[5];
- RK[7] = RK[3] ^ RK[6];
- }
- break;
+ RK[5] = RK[1] ^ RK[4];
+ RK[6] = RK[2] ^ RK[5];
+ RK[7] = RK[3] ^ RK[6];
+ }
+ break;
case 192:
- for( i = 0; i < 8; i++, RK += 6 )
- {
- RK[6] = RK[0] ^ RCON[i] ^
- ( FSb[ (uint8) ( RK[5] >> 16 ) ] << 24 ) ^
- ( FSb[ (uint8) ( RK[5] >> 8 ) ] << 16 ) ^
- ( FSb[ (uint8) ( RK[5] ) ] << 8 ) ^
- ( FSb[ (uint8) ( RK[5] >> 24 ) ] );
-
- RK[7] = RK[1] ^ RK[6];
- RK[8] = RK[2] ^ RK[7];
- RK[9] = RK[3] ^ RK[8];
- RK[10] = RK[4] ^ RK[9];
- RK[11] = RK[5] ^ RK[10];
- }
- break;
+ for( i = 0; i < 8; i++, RK += 6 )
+ {
+ RK[6] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[5] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[5] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[5] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[5] >> 24 ) ] );
+
+ RK[7] = RK[1] ^ RK[6];
+ RK[8] = RK[2] ^ RK[7];
+ RK[9] = RK[3] ^ RK[8];
+ RK[10] = RK[4] ^ RK[9];
+ RK[11] = RK[5] ^ RK[10];
+ }
+ break;
case 256:
- for( i = 0; i < 7; i++, RK += 8 )
- {
- RK[8] = RK[0] ^ RCON[i] ^
- ( FSb[ (uint8) ( RK[7] >> 16 ) ] << 24 ) ^
- ( FSb[ (uint8) ( RK[7] >> 8 ) ] << 16 ) ^
- ( FSb[ (uint8) ( RK[7] ) ] << 8 ) ^
- ( FSb[ (uint8) ( RK[7] >> 24 ) ] );
-
- RK[9] = RK[1] ^ RK[8];
- RK[10] = RK[2] ^ RK[9];
- RK[11] = RK[3] ^ RK[10];
-
- RK[12] = RK[4] ^
- ( FSb[ (uint8) ( RK[11] >> 24 ) ] << 24 ) ^
- ( FSb[ (uint8) ( RK[11] >> 16 ) ] << 16 ) ^
- ( FSb[ (uint8) ( RK[11] >> 8 ) ] << 8 ) ^
- ( FSb[ (uint8) ( RK[11] ) ] );
-
- RK[13] = RK[5] ^ RK[12];
- RK[14] = RK[6] ^ RK[13];
- RK[15] = RK[7] ^ RK[14];
- }
- break;
+ for( i = 0; i < 7; i++, RK += 8 )
+ {
+ RK[8] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[7] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[7] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[7] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[7] >> 24 ) ] );
+
+ RK[9] = RK[1] ^ RK[8];
+ RK[10] = RK[2] ^ RK[9];
+ RK[11] = RK[3] ^ RK[10];
+
+ RK[12] = RK[4] ^
+ ( FSb[ (uint8) ( RK[11] >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[11] >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[11] >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[11] ) ] );
+
+ RK[13] = RK[5] ^ RK[12];
+ RK[14] = RK[6] ^ RK[13];
+ RK[15] = RK[7] ^ RK[14];
+ }
+ break;
}
/* setup decryption round keys */
if( KT_init )
{
- for( i = 0; i < 256; i++ )
- {
- KT0[i] = RT0[ FSb[i] ];
- KT1[i] = RT1[ FSb[i] ];
- KT2[i] = RT2[ FSb[i] ];
- KT3[i] = RT3[ FSb[i] ];
- }
-
- KT_init = 0;
+ for( i = 0; i < 256; i++ )
+ {
+ KT0[i] = RT0[ FSb[i] ];
+ KT1[i] = RT1[ FSb[i] ];
+ KT2[i] = RT2[ FSb[i] ];
+ KT3[i] = RT3[ FSb[i] ];
+ }
+
+ KT_init = 0;
}
SK = ctx->drk;
@@ -552,27 +552,27 @@ int aes_set_key( aes_context *ctx, uint8 *key, int nbits )
for( i = 1; i < ctx->nr; i++ )
{
- RK -= 8;
-
- *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
- KT1[ (uint8) ( *RK >> 16 ) ] ^
- KT2[ (uint8) ( *RK >> 8 ) ] ^
- KT3[ (uint8) ( *RK ) ]; RK++;
-
- *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
- KT1[ (uint8) ( *RK >> 16 ) ] ^
- KT2[ (uint8) ( *RK >> 8 ) ] ^
- KT3[ (uint8) ( *RK ) ]; RK++;
-
- *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
- KT1[ (uint8) ( *RK >> 16 ) ] ^
- KT2[ (uint8) ( *RK >> 8 ) ] ^
- KT3[ (uint8) ( *RK ) ]; RK++;
-
- *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
- KT1[ (uint8) ( *RK >> 16 ) ] ^
- KT2[ (uint8) ( *RK >> 8 ) ] ^
- KT3[ (uint8) ( *RK ) ]; RK++;
+ RK -= 8;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
}
RK -= 8;
@@ -601,26 +601,26 @@ void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
RK += 4; \
- \
+ \
X0 = RK[0] ^ FT0[ (uint8) ( Y0 >> 24 ) ] ^ \
- FT1[ (uint8) ( Y1 >> 16 ) ] ^ \
- FT2[ (uint8) ( Y2 >> 8 ) ] ^ \
- FT3[ (uint8) ( Y3 ) ]; \
- \
+ FT1[ (uint8) ( Y1 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y2 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y3 ) ]; \
+ \
X1 = RK[1] ^ FT0[ (uint8) ( Y1 >> 24 ) ] ^ \
- FT1[ (uint8) ( Y2 >> 16 ) ] ^ \
- FT2[ (uint8) ( Y3 >> 8 ) ] ^ \
- FT3[ (uint8) ( Y0 ) ]; \
- \
+ FT1[ (uint8) ( Y2 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y3 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y0 ) ]; \
+ \
X2 = RK[2] ^ FT0[ (uint8) ( Y2 >> 24 ) ] ^ \
- FT1[ (uint8) ( Y3 >> 16 ) ] ^ \
- FT2[ (uint8) ( Y0 >> 8 ) ] ^ \
- FT3[ (uint8) ( Y1 ) ]; \
- \
+ FT1[ (uint8) ( Y3 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y0 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y1 ) ]; \
+ \
X3 = RK[3] ^ FT0[ (uint8) ( Y3 >> 24 ) ] ^ \
- FT1[ (uint8) ( Y0 >> 16 ) ] ^ \
- FT2[ (uint8) ( Y1 >> 8 ) ] ^ \
- FT3[ (uint8) ( Y2 ) ]; \
+ FT1[ (uint8) ( Y0 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y1 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y2 ) ]; \
}
AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 1 */
@@ -635,14 +635,14 @@ void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
if( ctx->nr > 10 )
{
- AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
- AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
}
if( ctx->nr > 12 )
{
- AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
- AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
}
/* last round */
@@ -650,24 +650,24 @@ void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
RK += 4;
X0 = RK[0] ^ ( FSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
- ( FSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
- ( FSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
- ( FSb[ (uint8) ( Y3 ) ] );
+ ( FSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y3 ) ] );
X1 = RK[1] ^ ( FSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
- ( FSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
- ( FSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
- ( FSb[ (uint8) ( Y0 ) ] );
+ ( FSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y0 ) ] );
X2 = RK[2] ^ ( FSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
- ( FSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
- ( FSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
- ( FSb[ (uint8) ( Y1 ) ] );
+ ( FSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y1 ) ] );
X3 = RK[3] ^ ( FSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
- ( FSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
- ( FSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
- ( FSb[ (uint8) ( Y2 ) ] );
+ ( FSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y2 ) ] );
PUT_UINT32( X0, output, 0 );
PUT_UINT32( X1, output, 4 );
@@ -691,26 +691,26 @@ void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
RK += 4; \
- \
+ \
X0 = RK[0] ^ RT0[ (uint8) ( Y0 >> 24 ) ] ^ \
- RT1[ (uint8) ( Y3 >> 16 ) ] ^ \
- RT2[ (uint8) ( Y2 >> 8 ) ] ^ \
- RT3[ (uint8) ( Y1 ) ]; \
- \
+ RT1[ (uint8) ( Y3 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y2 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y1 ) ]; \
+ \
X1 = RK[1] ^ RT0[ (uint8) ( Y1 >> 24 ) ] ^ \
- RT1[ (uint8) ( Y0 >> 16 ) ] ^ \
- RT2[ (uint8) ( Y3 >> 8 ) ] ^ \
- RT3[ (uint8) ( Y2 ) ]; \
- \
+ RT1[ (uint8) ( Y0 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y3 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y2 ) ]; \
+ \
X2 = RK[2] ^ RT0[ (uint8) ( Y2 >> 24 ) ] ^ \
- RT1[ (uint8) ( Y1 >> 16 ) ] ^ \
- RT2[ (uint8) ( Y0 >> 8 ) ] ^ \
- RT3[ (uint8) ( Y3 ) ]; \
- \
+ RT1[ (uint8) ( Y1 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y0 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y3 ) ]; \
+ \
X3 = RK[3] ^ RT0[ (uint8) ( Y3 >> 24 ) ] ^ \
- RT1[ (uint8) ( Y2 >> 16 ) ] ^ \
- RT2[ (uint8) ( Y1 >> 8 ) ] ^ \
- RT3[ (uint8) ( Y0 ) ]; \
+ RT1[ (uint8) ( Y2 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y1 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y0 ) ]; \
}
AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 1 */
@@ -725,14 +725,14 @@ void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
if( ctx->nr > 10 )
{
- AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
- AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
}
if( ctx->nr > 12 )
{
- AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
- AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
}
/* last round */
@@ -740,24 +740,24 @@ void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
RK += 4;
X0 = RK[0] ^ ( RSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
- ( RSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
- ( RSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
- ( RSb[ (uint8) ( Y1 ) ] );
+ ( RSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y1 ) ] );
X1 = RK[1] ^ ( RSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
- ( RSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
- ( RSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
- ( RSb[ (uint8) ( Y2 ) ] );
+ ( RSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y2 ) ] );
X2 = RK[2] ^ ( RSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
- ( RSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
- ( RSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
- ( RSb[ (uint8) ( Y3 ) ] );
+ ( RSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y3 ) ] );
X3 = RK[3] ^ ( RSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
- ( RSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
- ( RSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
- ( RSb[ (uint8) ( Y0 ) ] );
+ ( RSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y0 ) ] );
PUT_UINT32( X0, output, 0 );
PUT_UINT32( X1, output, 4 );
@@ -784,7 +784,7 @@ static unsigned char AES_enc_test[3][16] =
{ 0x1F, 0x67, 0x63, 0xDF, 0x80, 0x7A, 0x7E, 0x70,
0x96, 0x0D, 0x4C, 0xD3, 0x11, 0x8E, 0x60, 0x1A }
};
-
+
static unsigned char AES_dec_test[3][16] =
{
{ 0xF5, 0xBF, 0x8B, 0x37, 0x13, 0x6F, 0x2E, 0x1F,
@@ -794,7 +794,7 @@ static unsigned char AES_dec_test[3][16] =
{ 0x4D, 0xE0, 0xC6, 0xDF, 0x7C, 0xB1, 0x69, 0x72,
0x84, 0x60, 0x4D, 0x60, 0x27, 0x1B, 0xC5, 0x9A }
};
-
+
int main( void )
{
int m, n, i, j;
@@ -804,57 +804,57 @@ int main( void )
for( m = 0; m < 2; m++ )
{
- printf( "\n Rijndael Monte Carlo Test (ECB mode) - " );
-
- if( m == 0 ) printf( "encryption\n\n" );
- if( m == 1 ) printf( "decryption\n\n" );
-
- for( n = 0; n < 3; n++ )
- {
- printf( " Test %d, key size = %3d bits: ",
- n + 1, 128 + n * 64 );
-
- fflush( stdout );
-
- memset( buf, 0, 16 );
- memset( key, 0, 16 + n * 8 );
-
- for( i = 0; i < 400; i++ )
- {
- aes_set_key( &ctx, key, 128 + n * 64 );
-
- for( j = 0; j < 9999; j++ )
- {
- if( m == 0 ) aes_encrypt( &ctx, buf, buf );
- if( m == 1 ) aes_decrypt( &ctx, buf, buf );
- }
-
- if( n > 0 )
- {
- for( j = 0; j < (n << 3); j++ )
- {
- key[j] ^= buf[j + 16 - (n << 3)];
- }
- }
-
- if( m == 0 ) aes_encrypt( &ctx, buf, buf );
- if( m == 1 ) aes_decrypt( &ctx, buf, buf );
-
- for( j = 0; j < 16; j++ )
- {
- key[j + (n << 3)] ^= buf[j];
- }
- }
-
- if( ( m == 0 && memcmp( buf, AES_enc_test[n], 16 ) != 0 ) ||
- ( m == 1 && memcmp( buf, AES_dec_test[n], 16 ) != 0 ) )
- {
- printf( "failed!\n" );
- return( 1 );
- }
-
- printf( "passed.\n" );
- }
+ printf( "\n Rijndael Monte Carlo Test (ECB mode) - " );
+
+ if( m == 0 ) printf( "encryption\n\n" );
+ if( m == 1 ) printf( "decryption\n\n" );
+
+ for( n = 0; n < 3; n++ )
+ {
+ printf( " Test %d, key size = %3d bits: ",
+ n + 1, 128 + n * 64 );
+
+ fflush( stdout );
+
+ memset( buf, 0, 16 );
+ memset( key, 0, 16 + n * 8 );
+
+ for( i = 0; i < 400; i++ )
+ {
+ aes_set_key( &ctx, key, 128 + n * 64 );
+
+ for( j = 0; j < 9999; j++ )
+ {
+ if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+ if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+ }
+
+ if( n > 0 )
+ {
+ for( j = 0; j < (n << 3); j++ )
+ {
+ key[j] ^= buf[j + 16 - (n << 3)];
+ }
+ }
+
+ if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+ if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+
+ for( j = 0; j < 16; j++ )
+ {
+ key[j + (n << 3)] ^= buf[j];
+ }
+ }
+
+ if( ( m == 0 && memcmp( buf, AES_enc_test[n], 16 ) != 0 ) ||
+ ( m == 1 && memcmp( buf, AES_dec_test[n], 16 ) != 0 ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
}
printf( "\n" );
diff --git a/toolkit/ctrmode.c b/toolkit/ctrmode.c
index f89cfbd..443fd27 100644
--- a/toolkit/ctrmode.c
+++ b/toolkit/ctrmode.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/ctrmode.h b/toolkit/ctrmode.h
index a1e049e..fae8558 100644
--- a/toolkit/ctrmode.h
+++ b/toolkit/ctrmode.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/otr_mackey.c b/toolkit/otr_mackey.c
index 214d59b..5417c0d 100644
--- a/toolkit/otr_mackey.c
+++ b/toolkit/otr_mackey.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -48,7 +48,7 @@ int main(int argc, char **argv)
if (!argbuf) {
usage(argv[0]);
}
-
+
if (argbuflen != 16) {
fprintf(stderr, "The AES key must be 32 hex chars long.\n");
usage(argv[0]);
diff --git a/toolkit/otr_modify.c b/toolkit/otr_modify.c
index c6d045f..acb1071 100644
--- a/toolkit/otr_modify.c
+++ b/toolkit/otr_modify.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -63,7 +63,7 @@ int main(int argc, char **argv)
if (!mackey) {
usage(argv[0]);
}
-
+
if (mackeylen != 20) {
fprintf(stderr, "The MAC key must be 40 hex chars long.\n");
usage(argv[0]);
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
fprintf(stderr, "No OTR Data Message found on stdin.\n");
exit(1);
}
-
+
if (otrl_proto_message_type(otrmsg) != OTRL_MSGTYPE_DATA) {
fprintf(stderr, "OTR Non-Data Message found on stdin.\n");
exit(1);
diff --git a/toolkit/otr_parse.c b/toolkit/otr_parse.c
index f3bcee4..0112437 100644
--- a/toolkit/otr_parse.c
+++ b/toolkit/otr_parse.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -48,7 +49,15 @@ static void parse(const char *msg)
printf("Invalid D-H Commit Message\n\n");
break;
}
+
printf("D-H Commit Message:\n");
+
+ dump_data(stdout, "\tVersion", &(cmsg->version), 1);
+ if (cmsg->version == 3) {
+ dump_int(stdout, "\tSender instance", cmsg->sender_instance);
+ dump_int(stdout, "\tReceiver instance",
+ cmsg->receiver_instance);
+ }
dump_data(stdout, "\tEncrypted Key", cmsg->enckey,
cmsg->enckeylen);
dump_data(stdout, "\tHashed Key", cmsg->hashkey,
@@ -63,6 +72,12 @@ static void parse(const char *msg)
break;
}
printf("D-H Key Message:\n");
+ dump_data(stdout, "\tVersion", &(kmsg->version), 1);
+ if (kmsg->version == 3) {
+ dump_int(stdout, "\tSender instance", kmsg->sender_instance);
+ dump_int(stdout, "\tReceiver instance",
+ kmsg->receiver_instance);
+ }
dump_mpi(stdout, "\tD-H Key", kmsg->y);
printf("\n");
free_key(kmsg);
@@ -74,6 +89,12 @@ static void parse(const char *msg)
break;
}
printf("Reveal Signature Message:\n");
+ dump_data(stdout, "\tVersion", &(rmsg->version), 1);
+ if (rmsg->version == 3) {
+ dump_int(stdout, "\tSender instance", rmsg->sender_instance);
+ dump_int(stdout, "\tReceiver instance",
+ rmsg->receiver_instance);
+ }
dump_data(stdout, "\tKey", rmsg->key, rmsg->keylen);
dump_data(stdout, "\tEncrypted Signature",
rmsg->encsig, rmsg->encsiglen);
@@ -88,6 +109,12 @@ static void parse(const char *msg)
break;
}
printf("Signature Message:\n");
+ dump_data(stdout, "\tVersion", &(smsg->version), 1);
+ if (smsg->version == 3) {
+ dump_int(stdout, "\tSender instance", smsg->sender_instance);
+ dump_int(stdout, "\tReceiver instance",
+ smsg->receiver_instance);
+ }
dump_data(stdout, "\tEncrypted Signature",
smsg->encsig, smsg->encsiglen);
dump_data(stdout, "\tMAC", smsg->mac, 20);
@@ -120,9 +147,18 @@ static void parse(const char *msg)
break;
}
printf("Data Message:\n");
+
+ dump_data(stdout, "\tVersion", &(datamsg->version), 1);
if (datamsg->flags >= 0) {
dump_int(stdout, "\tFlags", datamsg->flags);
}
+
+ if (datamsg->version == 3) {
+ dump_int(stdout, "\tSender instance", datamsg->sender_instance);
+ dump_int(stdout, "\tReceiver instance",
+ datamsg->receiver_instance);
+ }
+
dump_int(stdout, "\tSender keyid", datamsg->sender_keyid);
dump_int(stdout, "\tRcpt keyid", datamsg->rcpt_keyid);
dump_mpi(stdout, "\tDH y", datamsg->y);
diff --git a/toolkit/otr_readforge.c b/toolkit/otr_readforge.c
index 203001c..6c54a1d 100644
--- a/toolkit/otr_readforge.c
+++ b/toolkit/otr_readforge.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -59,7 +59,7 @@ int main(int argc, char **argv)
if (!aeskey) {
usage(argv[0]);
}
-
+
if (aeskeylen != 16) {
fprintf(stderr, "The AES key must be 32 hex chars long.\n");
usage(argv[0]);
@@ -70,7 +70,7 @@ int main(int argc, char **argv)
fprintf(stderr, "No OTR Data Message found on stdin.\n");
exit(1);
}
-
+
if (otrl_proto_message_type(otrmsg) != OTRL_MSGTYPE_DATA) {
fprintf(stderr, "OTR Non-Data Message found on stdin.\n");
exit(1);
@@ -119,7 +119,7 @@ int main(int argc, char **argv)
free(datamsg->encmsg);
datamsg->encmsg = ciphertext;
datamsg->encmsglen = newlen;
-
+
newdatamsg = remac_datamsg(datamsg, mackey);
printf("%s\n", newdatamsg);
diff --git a/toolkit/otr_remac.c b/toolkit/otr_remac.c
index 4ae04fc..fbad4d4 100644
--- a/toolkit/otr_remac.c
+++ b/toolkit/otr_remac.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -30,12 +31,13 @@
static void usage(const char *progname)
{
- fprintf(stderr, "Usage: %s mackey flags snd_keyid rcp_keyid pubkey "
- "counter encdata revealed_mackeys\n"
+ fprintf(stderr, "Usage: %s mackey sender_instance receiver_instance "
+ "flags snd_keyid rcp_keyid pubkey counter encdata revealed_mackeys\n"
"Make a new Data message, with the given pieces (note that the\n"
"data part is already encrypted). MAC it with the given mackey.\n"
"mackey, pubkey, counter, encdata, and revealed_mackeys are given\n"
-"as strings of hex chars. snd_keyid and rcp_keyid are decimal integers.\n", progname);
+"as strings of hex chars. snd_keyid and rcp_keyid are decimal integers.\n",
+ progname);
exit(1);
}
@@ -45,6 +47,9 @@ int main(int argc, char **argv)
size_t mackeylen;
unsigned int snd_keyid, rcp_keyid;
int flags;
+ unsigned char version = 3;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
unsigned char *pubkey;
size_t pubkeylen;
gcry_mpi_t pubv;
@@ -56,7 +61,7 @@ int main(int argc, char **argv)
size_t mackeyslen;
char *newdatamsg;
- if (argc != 9) {
+ if (argc != 11) {
usage(argv[0]);
}
@@ -64,35 +69,45 @@ int main(int argc, char **argv)
if (!mackey) {
usage(argv[0]);
}
-
+
if (mackeylen != 20) {
fprintf(stderr, "The MAC key must be 40 hex chars long.\n");
usage(argv[0]);
}
- if (sscanf(argv[2], "%d", &flags) != 1) {
+ if (sscanf(argv[2], "%u", &sender_instance) != 1) {
+ fprintf(stderr, "Unparseable sender_instance given.\n");
+ usage(argv[0]);
+ }
+
+ if (sscanf(argv[3], "%u", &receiver_instance) != 1) {
+ fprintf(stderr, "Unparseable receiver_instance given.\n");
+ usage(argv[0]);
+ }
+
+ if (sscanf(argv[4], "%d", &flags) != 1) {
fprintf(stderr, "Unparseable flags given.\n");
usage(argv[0]);
}
- if (sscanf(argv[3], "%u", &snd_keyid) != 1) {
+ if (sscanf(argv[5], "%u", &snd_keyid) != 1) {
fprintf(stderr, "Unparseable snd_keyid given.\n");
usage(argv[0]);
}
- if (sscanf(argv[4], "%u", &rcp_keyid) != 1) {
+ if (sscanf(argv[6], "%u", &rcp_keyid) != 1) {
fprintf(stderr, "Unparseable rcp_keyid given.\n");
usage(argv[0]);
}
- argv_to_buf(&pubkey, &pubkeylen, argv[5]);
+ argv_to_buf(&pubkey, &pubkeylen, argv[7]);
if (!pubkey) {
usage(argv[0]);
}
gcry_mpi_scan(&pubv, GCRYMPI_FMT_USG, pubkey, pubkeylen, NULL);
free(pubkey);
-
- argv_to_buf(&ctr, &ctrlen, argv[6]);
+
+ argv_to_buf(&ctr, &ctrlen, argv[8]);
if (!ctr) {
usage(argv[0]);
}
@@ -102,18 +117,19 @@ int main(int argc, char **argv)
usage(argv[0]);
}
- argv_to_buf(&encdata, &encdatalen, argv[7]);
+ argv_to_buf(&encdata, &encdatalen, argv[9]);
if (!encdata) {
usage(argv[0]);
}
- argv_to_buf(&mackeys, &mackeyslen, argv[8]);
+ argv_to_buf(&mackeys, &mackeyslen, argv[10]);
if (!mackeys) {
usage(argv[0]);
}
- newdatamsg = assemble_datamsg(mackey, flags, snd_keyid, rcp_keyid,
- pubv, ctr, encdata, encdatalen, mackeys, mackeyslen);
+ newdatamsg = assemble_datamsg(mackey, version, sender_instance,
+ receiver_instance, flags, snd_keyid, rcp_keyid, pubv, ctr, encdata,
+ encdatalen, mackeys, mackeyslen);
printf("%s\n", newdatamsg);
free(newdatamsg);
diff --git a/toolkit/otr_sesskeys.c b/toolkit/otr_sesskeys.c
index 5c5583f..1cb262d 100644
--- a/toolkit/otr_sesskeys.c
+++ b/toolkit/otr_sesskeys.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/otr_toolkit.1 b/toolkit/otr_toolkit.1
index 45a8e72..9b074f9 100644
--- a/toolkit/otr_toolkit.1
+++ b/toolkit/otr_toolkit.1
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
-.TH OTR_PARSE 1 "October 27, 2005"
+.TH OTR_PARSE 1 "March 14, 2012"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@@ -33,7 +33,7 @@ otr_parse, otr_sesskeys, otr_mackey, otr_readforge, otr_modify, otr_remac \- Pro
.I mackey old_text new_text offset
.br
.B otr_remac
-.I mackey flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
+.I mackey sender_instance receiver_instance flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
.SH DESCRIPTION
Off-the-Record (OTR) Messaging allows you to have private conversations
over IM by providing:
@@ -95,9 +95,10 @@ Here are the six programs in the toolkit:
you can still forge messages of your choice using the
otr_readforge command, above.
- - otr_remac mackey flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
- - Make a new OTR Data Message, with the given pieces (note that the
- data part is already encrypted). MAC it with the given mackey.
+ - otr_remac mackey sender_instance receiver_instance flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
+ - Make a new OTR protocol version 3 Data Message, with the given
+ pieces (note that the data part is already encrypted). MAC it
+ with the given mackey.
.SH SEE ALSO
.BR "Off-the-Record Messaging" ,
diff --git a/toolkit/parse.c b/toolkit/parse.c
index 5f357fc..ecefafb 100644
--- a/toolkit/parse.c
+++ b/toolkit/parse.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -39,7 +40,7 @@ void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val)
{
size_t plen;
unsigned char *d;
-
+
gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &plen, val);
d = malloc(plen);
gcry_mpi_print(GCRYMPI_FMT_USG, d, plen, NULL, val);
@@ -64,7 +65,7 @@ static unsigned char *decode(const char *msg, size_t *lenp)
{
const char *header, *footer;
unsigned char *raw;
-
+
/* Find the header */
header = strstr(msg, "?OTR:");
if (!header) return NULL;
@@ -200,8 +201,18 @@ CommitMsg parse_commit(const char *msg)
cmsg->raw = raw;
require_len(3);
- if (memcmp(bufp, "\x00\x02\x02", 3)) goto inv;
- bufp += 3; lenp -= 3;
+
+ cmsg->version = bufp[1];
+
+ if (!memcmp(bufp, "\x00\x03\x02", 3)) {
+ bufp += 3; lenp -= 3;
+ read_int(cmsg->sender_instance);
+ read_int(cmsg->receiver_instance);
+ } else if (!memcmp(bufp, "\x00\x02\x02", 3)) {
+ bufp += 3; lenp -= 3;
+ cmsg->sender_instance = 0;
+ cmsg->receiver_instance = 0;
+ } else goto inv;
read_int(cmsg->enckeylen);
cmsg->enckey = malloc(cmsg->enckeylen);
@@ -249,8 +260,18 @@ KeyMsg parse_key(const char *msg)
kmsg->raw = raw;
require_len(3);
- if (memcmp(bufp, "\x00\x02\x0a", 3)) goto inv;
- bufp += 3; lenp -= 3;
+
+ kmsg->version = bufp[1];
+
+ if (!memcmp(bufp, "\x00\x03\x0a", 3)) {
+ bufp += 3; lenp -= 3;
+ read_int(kmsg->sender_instance);
+ read_int(kmsg->receiver_instance);
+ } else if (!memcmp(bufp, "\x00\x02\x0a", 3)) {
+ bufp += 3; lenp -= 3;
+ kmsg->sender_instance = 0;
+ kmsg->receiver_instance = 0;
+ } else goto inv;
read_mpi(kmsg->y);
@@ -290,8 +311,18 @@ RevealSigMsg parse_revealsig(const char *msg)
rmsg->raw = raw;
require_len(3);
- if (memcmp(bufp, "\x00\x02\x11", 3)) goto inv;
- bufp += 3; lenp -= 3;
+
+ rmsg->version = bufp[1];
+
+ if (!memcmp(bufp, "\x00\x03\x11", 3)) {
+ bufp += 3; lenp -= 3;
+ read_int(rmsg->sender_instance);
+ read_int(rmsg->receiver_instance);
+ } else if (!memcmp(bufp, "\x00\x02\x11", 3)) {
+ bufp += 3; lenp -= 3;
+ rmsg->sender_instance = 0;
+ rmsg->receiver_instance = 0;
+ } else goto inv;
read_int(rmsg->keylen);
rmsg->key = malloc(rmsg->keylen);
@@ -341,8 +372,18 @@ SignatureMsg parse_signature(const char *msg)
smsg->raw = raw;
require_len(3);
- if (memcmp(bufp, "\x00\x02\x12", 3)) goto inv;
- bufp += 3; lenp -= 3;
+
+ smsg->version = bufp[1];
+
+ if (!memcmp(bufp, "\x00\x03\x12", 3)) {
+ bufp += 3; lenp -= 3;
+ read_int(smsg->sender_instance);
+ read_int(smsg->receiver_instance);
+ } else if (!memcmp(bufp, "\x00\x02\x12", 3)) {
+ bufp += 3; lenp -= 3;
+ smsg->sender_instance = 0;
+ smsg->receiver_instance = 0;
+ } else goto inv;
read_int(smsg->encsiglen);
smsg->encsig = malloc(smsg->encsiglen);
@@ -389,18 +430,28 @@ DataMsg parse_datamsg(const char *msg)
datam->macstart = bufp;
require_len(3);
- if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3))
- goto inv;
+ if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x03\x03", 3) &&
+ memcmp(bufp, "\x00\x02\x03", 3)) goto inv;
+
version = bufp[1];
+
+ datam->sender_instance = 0;
+ datam->receiver_instance = 0;
+ datam->version = version;
+ datam->flags = -1;
bufp += 3; lenp -= 3;
- if (version == 2) {
+ if (version == 3) {
+ read_int(datam->sender_instance);
+ read_int(datam->receiver_instance);
+ }
+
+ if (version == 2 || version == 3) {
require_len(1);
datam->flags = bufp[0];
bufp += 1; lenp -= 1;
- } else {
- datam->flags = -1;
}
+
read_int(datam->sender_keyid);
read_int(datam->rcpt_keyid);
read_mpi(datam->y);
@@ -413,9 +464,10 @@ DataMsg parse_datamsg(const char *msg)
read_raw(datam->mac, 20);
read_int(datam->mackeyslen);
datam->mackeys = malloc(datam->mackeyslen);
+
if (!datam->mackeys && datam->mackeyslen > 0) goto inv;
- read_raw(datam->mackeys, datam->mackeyslen);
+ read_raw(datam->mackeys, datam->mackeyslen);
if (lenp != 0) goto inv;
return datam;
@@ -435,11 +487,12 @@ char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
size_t base64len;
char *outmsg;
unsigned char *raw, *bufp;
- unsigned char version = (datamsg->flags >= 0 ? 2 : 1);
-
+ unsigned char version = datamsg->version;
+
/* Calculate the size of the message that will result */
gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &ylen, datamsg->y);
- rawlen = 3 + (version == 2 ? 1 : 0) + 4 + 4 + 4 + ylen + 8 + 4 +
+ rawlen = 3 + (version == 3 ? 8 : 0) + (version == 2 ||
+ version == 3 ? 1 : 0) + 4 + 4 + 4 + ylen + 8 + 4 +
datamsg->encmsglen + 20 + 4 + datamsg->mackeyslen;
/* Construct the new raw message (note that some of the pieces may
@@ -457,16 +510,22 @@ char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
datamsg->raw = raw;
datamsg->rawlen = rawlen;
- if (version == 1) {
- memmove(bufp, "\x00\x01\x03", 3);
- } else {
- memmove(bufp, "\x00\x02\x03", 3);
- }
+
+ memmove(bufp, "\x00", 1);
+ memmove(bufp+1, &version, 1);
+ memmove(bufp+2, "\x03", 1);
bufp += 3; lenp -= 3;
- if (version == 2) {
+
+ if (version == 3) {
+ write_int(datamsg->sender_instance);
+ write_int(datamsg->receiver_instance);
+ }
+
+ if (version == 2 || version == 3) {
bufp[0] = datamsg->flags;
bufp += 1; lenp -= 1;
}
+
write_int(datamsg->sender_keyid);
write_int(datamsg->rcpt_keyid);
write_mpi(datamsg->y, ylen);
@@ -482,7 +541,7 @@ char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
write_raw(datamsg->mac, 20);
write_int(datamsg->mackeyslen);
write_raw(datamsg->mackeys, datamsg->mackeyslen);
-
+
if (lenp != 0) {
fprintf(stderr, "Error creating OTR Data Message.\n");
exit(1);
@@ -500,15 +559,20 @@ char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
/* Assemble a new Data Message from its pieces. Return a
* newly-allocated string containing the base64 representation. */
-char *assemble_datamsg(unsigned char mackey[20], int flags,
- unsigned int sender_keyid, unsigned int rcpt_keyid, gcry_mpi_t y,
+char *assemble_datamsg(unsigned char mackey[20],
+ unsigned char version, unsigned int sender_instance,
+ unsigned int receiver_instance, int flags, unsigned int sender_keyid,
+ unsigned int rcpt_keyid, gcry_mpi_t y,
unsigned char ctr[8], unsigned char *encmsg, size_t encmsglen,
unsigned char *mackeys, size_t mackeyslen)
{
DataMsg datam = calloc(1, sizeof(struct s_DataMsg));
char *newmsg = NULL;
if (!datam) goto inv;
+ datam->version = version;
datam->flags = flags;
+ datam->sender_instance = sender_instance;
+ datam->receiver_instance = receiver_instance;
datam->sender_keyid = sender_keyid;
datam->rcpt_keyid = rcpt_keyid;
datam->y = gcry_mpi_copy(y);
diff --git a/toolkit/parse.h b/toolkit/parse.h
index f98cb6a..d1f8750 100644
--- a/toolkit/parse.h
+++ b/toolkit/parse.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
@@ -37,6 +38,9 @@ typedef struct s_DataMsg {
unsigned char *raw; /* The base64-decoded data; must be free()d */
size_t rawlen;
int flags;
+ unsigned char version;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
unsigned int sender_keyid;
unsigned int rcpt_keyid;
gcry_mpi_t y;
@@ -52,6 +56,9 @@ typedef struct s_DataMsg {
typedef struct s_CommitMsg {
unsigned char *raw; /* The base64-decoded data; must be free()d */
+ unsigned char version;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
unsigned char *enckey;
size_t enckeylen;
unsigned char *hashkey;
@@ -60,11 +67,17 @@ typedef struct s_CommitMsg {
typedef struct s_KeyMsg {
unsigned char *raw; /* The base64-decoded data; must be free()d */
+ unsigned char version;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
gcry_mpi_t y;
} * KeyMsg;
typedef struct s_RevealSigMsg {
unsigned char *raw; /* The base64-decoded data; must be free()d */
+ unsigned char version;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
unsigned char *key;
size_t keylen;
unsigned char *encsig;
@@ -74,6 +87,9 @@ typedef struct s_RevealSigMsg {
typedef struct s_SignatureMsg {
unsigned char *raw; /* The base64-decoded data; must be free()d */
+ unsigned char version;
+ unsigned int sender_instance;
+ unsigned int receiver_instance;
unsigned char *encsig;
size_t encsiglen;
unsigned char mac[20];
@@ -131,8 +147,10 @@ char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20]);
/* Assemble a new Data Message from its pieces. Return a
* newly-allocated string containing the base64 representation. */
-char *assemble_datamsg(unsigned char mackey[20], int flags,
- unsigned int sender_keyid, unsigned int rcpt_keyid, gcry_mpi_t y,
+char *assemble_datamsg(unsigned char mackey[20],
+ unsigned char version, unsigned int sender_instance,
+ unsigned int receiver_instance, int flags, unsigned int sender_keyid,
+ unsigned int rcpt_keyid, gcry_mpi_t y,
unsigned char ctr[8], unsigned char *encmsg, size_t encmsglen,
unsigned char *mackeys, size_t mackeyslen);
diff --git a/toolkit/readotr.c b/toolkit/readotr.c
index 04e9fca..7f0f270 100644
--- a/toolkit/readotr.c
+++ b/toolkit/readotr.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/readotr.h b/toolkit/readotr.h
index 75a7e2f..86f1073 100644
--- a/toolkit/readotr.h
+++ b/toolkit/readotr.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sesskeys.c b/toolkit/sesskeys.c
index d823ebc..e7e06e7 100644
--- a/toolkit/sesskeys.c
+++ b/toolkit/sesskeys.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sesskeys.h b/toolkit/sesskeys.h
index 7a98ac8..617c05a 100644
--- a/toolkit/sesskeys.h
+++ b/toolkit/sesskeys.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sha1hmac.c b/toolkit/sha1hmac.c
index 272a4fb..d58caff 100644
--- a/toolkit/sha1hmac.c
+++ b/toolkit/sha1hmac.c
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sha1hmac.h b/toolkit/sha1hmac.h
index ad5159a..bb53829 100644
--- a/toolkit/sha1hmac.h
+++ b/toolkit/sha1hmac.h
@@ -1,6 +1,6 @@
/*
* Off-the-Record Messaging Toolkit
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov
* <otr at cypherpunks.ca>
*
* This program is free software; you can redistribute it and/or modify
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/libotr.git
More information about the Pkg-privacy-commits
mailing list