summaryrefslogtreecommitdiff
path: root/plugins/MirOTR/libotr/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/MirOTR/libotr/src')
-rw-r--r--plugins/MirOTR/libotr/src/auth.c2226
-rw-r--r--plugins/MirOTR/libotr/src/auth.h98
-rw-r--r--plugins/MirOTR/libotr/src/b64.c86
-rw-r--r--plugins/MirOTR/libotr/src/b64.h23
-rw-r--r--plugins/MirOTR/libotr/src/context.c609
-rw-r--r--plugins/MirOTR/libotr/src/context.h139
-rw-r--r--plugins/MirOTR/libotr/src/context_priv.c95
-rw-r--r--plugins/MirOTR/libotr/src/context_priv.h94
-rw-r--r--plugins/MirOTR/libotr/src/dh.c32
-rw-r--r--plugins/MirOTR/libotr/src/dh.h10
-rw-r--r--plugins/MirOTR/libotr/src/instag.c262
-rw-r--r--plugins/MirOTR/libotr/src/instag.h89
-rw-r--r--plugins/MirOTR/libotr/src/mem.c125
-rw-r--r--plugins/MirOTR/libotr/src/mem.h14
-rw-r--r--plugins/MirOTR/libotr/src/message.c2579
-rw-r--r--plugins/MirOTR/libotr/src/message.h328
-rw-r--r--plugins/MirOTR/libotr/src/privkey-t.h15
-rw-r--r--plugins/MirOTR/libotr/src/privkey.c931
-rw-r--r--plugins/MirOTR/libotr/src/privkey.h51
-rw-r--r--plugins/MirOTR/libotr/src/proto.c677
-rw-r--r--plugins/MirOTR/libotr/src/proto.h68
-rw-r--r--plugins/MirOTR/libotr/src/serial.h30
-rw-r--r--plugins/MirOTR/libotr/src/sm.c213
-rw-r--r--plugins/MirOTR/libotr/src/sm.h5
-rw-r--r--plugins/MirOTR/libotr/src/tests.c275
-rw-r--r--plugins/MirOTR/libotr/src/tlv.c5
-rw-r--r--plugins/MirOTR/libotr/src/tlv.h14
-rw-r--r--plugins/MirOTR/libotr/src/userstate.c14
-rw-r--r--plugins/MirOTR/libotr/src/userstate.h12
-rw-r--r--plugins/MirOTR/libotr/src/version.h12
30 files changed, 5913 insertions, 3218 deletions
diff --git a/plugins/MirOTR/libotr/src/auth.c b/plugins/MirOTR/libotr/src/auth.c
index 978c986613..d0c55057e1 100644
--- a/plugins/MirOTR/libotr/src/auth.c
+++ b/plugins/MirOTR/libotr/src/auth.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -27,33 +29,73 @@
#include "privkey.h"
#include "auth.h"
#include "serial.h"
+#include "proto.h"
+#include "context.h"
+#include "mem.h"
+
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+/* Dump the contents of an OtrlAuthInfo to the FILE *f. */
+void otrl_auth_dump(FILE *f, const OtrlAuthInfo *auth)
+{
+ int i;
+
+ fprintf(f, " Auth info %p:\n", auth);
+ fprintf(f, " State: %d (%s)\n", auth->authstate,
+ auth->authstate == OTRL_AUTHSTATE_NONE ? "NONE" :
+ auth->authstate == OTRL_AUTHSTATE_AWAITING_DHKEY ? "AWAITING_DHKEY" :
+ auth->authstate == OTRL_AUTHSTATE_AWAITING_REVEALSIG ?
+ "AWAITING_REVEALSIG" :
+ auth->authstate == OTRL_AUTHSTATE_AWAITING_SIG ? "AWAITING_SIG" :
+ auth->authstate == OTRL_AUTHSTATE_V1_SETUP ? "V1_SETUP" :
+ "INVALID");
+ fprintf(f, " Context: %p\n", auth->context);
+ fprintf(f, " Our keyid: %u\n", auth->our_keyid);
+ fprintf(f, " Their keyid: %u\n", auth->their_keyid);
+ fprintf(f, " Their fingerprint: ");
+ for (i=0;i<20;++i) {
+ fprintf(f, "%02x", auth->their_fingerprint[i]);
+ }
+ fprintf(f, "\n Initiated = %d\n", auth->initiated);
+ fprintf(f, "\n Proto version = %d\n", auth->protocol_version);
+ fprintf(f, "\n Lastauthmsg = %s\n",
+ auth->lastauthmsg ? auth->lastauthmsg : "(nil)");
+ fprintf(f, "\n Commit sent time = %ld\n",
+ (long) auth->commit_sent_time);
+}
+
+#endif
/*
* Initialize the fields of an OtrlAuthInfo (already allocated).
*/
-void otrl_auth_new(OtrlAuthInfo *auth)
+void otrl_auth_new(struct context *context)
{
- auth->authstate = OTRL_AUTHSTATE_NONE;
- otrl_dh_keypair_init(&(auth->our_dh));
- auth->our_keyid = 0;
- auth->encgx = NULL;
- auth->encgx_len = 0;
- memset(auth->r, 0, 16);
- memset(auth->hashgx, 0, 32);
- auth->their_pub = NULL;
- auth->their_keyid = 0;
- auth->enc_c = NULL;
- auth->enc_cp = NULL;
- auth->mac_m1 = NULL;
- auth->mac_m1p = NULL;
- auth->mac_m2 = NULL;
- auth->mac_m2p = NULL;
- memset(auth->their_fingerprint, 0, 20);
- auth->initiated = 0;
- auth->protocol_version = 0;
- memset(auth->secure_session_id, 0, 20);
- auth->secure_session_id_len = 0;
- auth->lastauthmsg = NULL;
+ OtrlAuthInfo *auth = &(context->auth);
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+ otrl_dh_keypair_init(&(auth->our_dh));
+ auth->our_keyid = 0;
+ auth->encgx = NULL;
+ auth->encgx_len = 0;
+ memset(auth->r, 0, 16);
+ memset(auth->hashgx, 0, 32);
+ auth->their_pub = NULL;
+ auth->their_keyid = 0;
+ auth->enc_c = NULL;
+ auth->enc_cp = NULL;
+ auth->mac_m1 = NULL;
+ auth->mac_m1p = NULL;
+ auth->mac_m2 = NULL;
+ auth->mac_m2p = NULL;
+ memset(auth->their_fingerprint, 0, 20);
+ auth->initiated = 0;
+ auth->protocol_version = 0;
+ memset(auth->secure_session_id, 0, 20);
+ auth->secure_session_id_len = 0;
+ auth->lastauthmsg = NULL;
+ auth->commit_sent_time = 0;
+ auth->context = context;
}
/*
@@ -61,135 +103,145 @@ void otrl_auth_new(OtrlAuthInfo *auth)
*/
void otrl_auth_clear(OtrlAuthInfo *auth)
{
- auth->authstate = OTRL_AUTHSTATE_NONE;
- otrl_dh_keypair_free(&(auth->our_dh));
- auth->our_keyid = 0;
- free(auth->encgx);
- auth->encgx = NULL;
- auth->encgx_len = 0;
- memset(auth->r, 0, 16);
- memset(auth->hashgx, 0, 32);
- gcry_mpi_release(auth->their_pub);
- auth->their_pub = NULL;
- auth->their_keyid = 0;
- gcry_cipher_close(auth->enc_c);
- gcry_cipher_close(auth->enc_cp);
- gcry_md_close(auth->mac_m1);
- gcry_md_close(auth->mac_m1p);
- gcry_md_close(auth->mac_m2);
- gcry_md_close(auth->mac_m2p);
- auth->enc_c = NULL;
- auth->enc_cp = NULL;
- auth->mac_m1 = NULL;
- auth->mac_m1p = NULL;
- auth->mac_m2 = NULL;
- auth->mac_m2p = NULL;
- memset(auth->their_fingerprint, 0, 20);
- auth->initiated = 0;
- auth->protocol_version = 0;
- memset(auth->secure_session_id, 0, 20);
- auth->secure_session_id_len = 0;
- free(auth->lastauthmsg);
- auth->lastauthmsg = NULL;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+ otrl_dh_keypair_free(&(auth->our_dh));
+ auth->our_keyid = 0;
+ free(auth->encgx);
+ auth->encgx = NULL;
+ auth->encgx_len = 0;
+ memset(auth->r, 0, 16);
+ memset(auth->hashgx, 0, 32);
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = NULL;
+ auth->their_keyid = 0;
+ gcry_cipher_close(auth->enc_c);
+ gcry_cipher_close(auth->enc_cp);
+ gcry_md_close(auth->mac_m1);
+ gcry_md_close(auth->mac_m1p);
+ gcry_md_close(auth->mac_m2);
+ gcry_md_close(auth->mac_m2p);
+ auth->enc_c = NULL;
+ auth->enc_cp = NULL;
+ auth->mac_m1 = NULL;
+ auth->mac_m1p = NULL;
+ auth->mac_m2 = NULL;
+ auth->mac_m2p = NULL;
+ memset(auth->their_fingerprint, 0, 20);
+ auth->initiated = 0;
+ auth->protocol_version = 0;
+ memset(auth->secure_session_id, 0, 20);
+ auth->secure_session_id_len = 0;
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = NULL;
+ auth->commit_sent_time = 0;
}
/*
- * 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;
- size_t npub;
- gcry_cipher_hd_t enc = NULL;
- unsigned char ctr[16];
- unsigned char *buf, *bufp;
- size_t buflen, lenp;
-
- /* Clear out this OtrlAuthInfo and start over */
- otrl_auth_clear(auth);
- auth->initiated = 1;
-
- otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
- auth->our_keyid = 1;
-
- /* Pick an encryption key */
- gcry_randomize(auth->r, 16, GCRY_STRONG_RANDOM);
-
- /* Allocate space for the encrypted g^x */
- gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
- auth->encgx = malloc(4+npub);
- if (auth->encgx == NULL) goto memerr;
- auth->encgx_len = 4+npub;
- bufp = auth->encgx;
- lenp = auth->encgx_len;
- write_mpi(auth->our_dh.pub, npub, "g^x");
- assert(lenp == 0);
-
- /* Hash g^x */
- gcry_md_hash_buffer(GCRY_MD_SHA256, auth->hashgx, auth->encgx,
- auth->encgx_len);
-
- /* Encrypt g^x using the key r */
- err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
- GCRY_CIPHER_SECURE);
- if (err) goto err;
-
- err = gcry_cipher_setkey(enc, auth->r, 16);
- if (err) goto err;
-
- memset(ctr, 0, 16);
- err = gcry_cipher_setctr(enc, ctr, 16);
- if (err) goto err;
-
- err = gcry_cipher_encrypt(enc, auth->encgx, auth->encgx_len, NULL, 0);
- if (err) goto err;
-
- gcry_cipher_close(enc);
- enc = NULL;
-
- /* Now serialize the message */
- lenp = 3 + 4 + auth->encgx_len + 4 + 32;
- bufp = malloc(lenp);
- if (bufp == NULL) goto memerr;
- buf = bufp;
- buflen = lenp;
-
- memcpy(bufp, "\x00\x02\x02", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
-
- /* Encrypted g^x */
- write_int(auth->encgx_len);
- debug_int("Enc gx len", bufp-4);
- memcpy(bufp, auth->encgx, auth->encgx_len);
- debug_data("Enc gx", bufp, auth->encgx_len);
- bufp += auth->encgx_len; lenp -= auth->encgx_len;
-
- /* Hashed g^x */
- write_int(32);
- debug_int("hashgx len", bufp-4);
- memcpy(bufp, auth->hashgx, 32);
- debug_data("hashgx", bufp, 32);
- bufp += 32; lenp -= 32;
-
- assert(lenp == 0);
-
- auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
- free(buf);
- if (auth->lastauthmsg == NULL) goto memerr;
- auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
-
- return err;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t npub;
+ gcry_cipher_hd_t enc = NULL;
+ unsigned char ctr[16];
+ unsigned char *buf, *bufp;
+ size_t buflen, lenp;
+
+ /* 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;
+
+ /* Pick an encryption key */
+ gcry_randomize(auth->r, 16, GCRY_STRONG_RANDOM);
+
+ /* Allocate space for the encrypted g^x */
+ gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
+ auth->encgx = malloc(4+npub);
+ if (auth->encgx == NULL) goto memerr;
+ auth->encgx_len = 4+npub;
+ bufp = auth->encgx;
+ lenp = auth->encgx_len;
+ write_mpi(auth->our_dh.pub, npub, "g^x");
+ assert(lenp == 0);
+
+ /* Hash g^x */
+ gcry_md_hash_buffer(GCRY_MD_SHA256, auth->hashgx, auth->encgx,
+ auth->encgx_len);
+
+ /* Encrypt g^x using the key r */
+ err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
+ GCRY_CIPHER_SECURE);
+ if (err) goto err;
+
+ err = gcry_cipher_setkey(enc, auth->r, 16);
+ if (err) goto err;
+
+ memset(ctr, 0, 16);
+ err = gcry_cipher_setctr(enc, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_encrypt(enc, auth->encgx, auth->encgx_len, NULL, 0);
+ if (err) goto err;
+
+ gcry_cipher_close(enc);
+ enc = NULL;
+
+ /* Now serialize the message */
+ 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;
+
+ /* 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);
+ debug_int("Enc gx len", bufp-4);
+ memmove(bufp, auth->encgx, auth->encgx_len);
+ debug_data("Enc gx", bufp, auth->encgx_len);
+ bufp += auth->encgx_len; lenp -= auth->encgx_len;
+
+ /* Hashed g^x */
+ write_int(32);
+ debug_int("hashgx len", bufp-4);
+ memmove(bufp, auth->hashgx, 32);
+ debug_data("hashgx", bufp, 32);
+ bufp += 32; lenp -= 32;
+
+ assert(lenp == 0);
+
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ free(buf);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
+
+ return err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- otrl_auth_clear(auth);
- gcry_cipher_close(enc);
- return err;
+ otrl_auth_clear(auth);
+ gcry_cipher_close(enc);
+ return err;
}
/*
@@ -198,38 +250,44 @@ err:
*/
static gcry_error_t create_key_message(OtrlAuthInfo *auth)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- unsigned char *buf, *bufp;
- size_t buflen, lenp;
- size_t npub;
-
- gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
- buflen = 3 + 4 + npub;
- buf = malloc(buflen);
- if (buf == NULL) goto memerr;
- bufp = buf;
- lenp = buflen;
-
- memcpy(bufp, "\x00\x02\x0a", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
-
- /* g^y */
- write_mpi(auth->our_dh.pub, npub, "g^y");
-
- assert(lenp == 0);
-
- free(auth->lastauthmsg);
- auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
- free(buf);
- if (auth->lastauthmsg == NULL) goto memerr;
-
- return err;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ unsigned char *buf, *bufp;
+ size_t buflen, lenp;
+ size_t npub;
+
+ gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
+ buflen = OTRL_HEADER_LEN + (auth->protocol_version == 3 ? 8 : 0) + 4 + npub;
+ buf = malloc(buflen);
+ if (buf == NULL) goto memerr;
+ bufp = buf;
+ lenp = buflen;
+
+ /* 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");
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ free(buf);
+ if (auth->lastauthmsg == NULL) goto memerr;
+
+ return err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
- return err;
+ err = gcry_error(GPG_ERR_ENOMEM);
+ return err;
}
/*
@@ -238,112 +296,131 @@ 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;
- unsigned char hashbuf[32];
- size_t buflen, lenp, enclen, hashlen;
- int res;
-
- res = otrl_base64_otr_decode(commitmsg, &buf, &buflen);
- if (res == -1) goto memerr;
- if (res == -2) goto invval;
-
- bufp = buf;
- lenp = buflen;
-
- /* Header */
- require_len(3);
- if (memcmp(bufp, "\x00\x02\x02", 3)) goto invval;
- bufp += 3; lenp -= 3;
-
- /* Encrypted g^x */
- read_int(enclen);
- require_len(enclen);
- encbuf = malloc(enclen);
- if (encbuf == NULL && enclen > 0) goto memerr;
- memcpy(encbuf, bufp, enclen);
- bufp += enclen; lenp -= enclen;
-
- /* Hashed g^x */
- read_int(hashlen);
- if (hashlen != 32) goto invval;
- require_len(32);
- memcpy(hashbuf, bufp, 32);
- bufp += 32; lenp -= 32;
-
- if (lenp != 0) goto invval;
- free(buf);
- buf = NULL;
-
- switch(auth->authstate) {
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL, *encbuf = NULL;
+ unsigned char hashbuf[32];
+ size_t buflen, lenp, enclen, hashlen;
+ int res;
+
+ /* Are we the auth for the master context? */
+ int is_master = (auth->context->m_context == auth->context);
+
+ res = otrl_base64_otr_decode(commitmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ 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);
+ require_len(enclen);
+ encbuf = malloc(enclen);
+ if (encbuf == NULL && enclen > 0) goto memerr;
+ memmove(encbuf, bufp, enclen);
+ bufp += enclen; lenp -= enclen;
+
+ /* Hashed g^x */
+ read_int(hashlen);
+ if (hashlen != 32) goto invval;
+ require_len(32);
+ memmove(hashbuf, bufp, 32);
+ bufp += 32; lenp -= 32;
+
+ if (lenp != 0) goto invval;
+ free(buf);
+ buf = NULL;
+
+ switch(auth->authstate) {
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;
- /* Store the incoming information */
- otrl_auth_clear(auth);
- otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
- auth->our_keyid = 1;
- auth->encgx = encbuf;
- encbuf = NULL;
- auth->encgx_len = enclen;
- memcpy(auth->hashgx, hashbuf, 32);
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
- /* Create a D-H Key Message */
- err = create_key_message(auth);
- if (err) goto err;
- auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
+ auth->our_keyid = 1;
+ auth->encgx = encbuf;
+ encbuf = NULL;
+ auth->encgx_len = enclen;
+ memmove(auth->hashgx, hashbuf, 32);
- break;
+ /* Create a D-H Key Message */
+ err = create_key_message(auth);
+ if (err) goto err;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
+ break;
case OTRL_AUTHSTATE_AWAITING_DHKEY:
- /* We sent a D-H Commit Message, and we also received one
- * back. Compare the hashgx values to see which one wins. */
- if (memcmp(auth->hashgx, hashbuf, 32) > 0) {
+ /* We sent a D-H Commit Message, and we also received one
+ * back. If we're the master context, then the keypair in here
+ * is probably stale; we just kept it around for a little
+ * while in case some other logged in instance of our buddy
+ * replied with a DHKEY message. In that case, use the
+ * incoming parameters. Otherwise, compare the hashgx
+ * values to see which one wins.
+ *
+ * This does NOT use constant time comparison because these
+ * are two public values thus don't need it. Also, this checks
+ * which pubkey is larger and not if they are the same. */
+ if (!is_master && memcmp(auth->hashgx, hashbuf, 32) > 0) {
/* Ours wins. Ignore the message we received, and just
* resend the same D-H Commit message again. */
free(encbuf);
encbuf = NULL;
- } else {
+ } 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;
encbuf = NULL;
auth->encgx_len = enclen;
- memcpy(auth->hashgx, hashbuf, 32);
+ memmove(auth->hashgx, hashbuf, 32);
/* Create a D-H Key Message */
err = create_key_message(auth);
if (err) goto err;
auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
- }
- break;
+ }
+ break;
case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
- /* Use the incoming parameters, but just retransmit the old
- * D-H Key Message. */
- free(auth->encgx);
- auth->encgx = encbuf;
- encbuf = NULL;
- auth->encgx_len = enclen;
- memcpy(auth->hashgx, hashbuf, 32);
- break;
- }
-
- return err;
+ /* Use the incoming parameters, but just retransmit the old
+ * D-H Key Message. */
+ free(auth->encgx);
+ auth->encgx = encbuf;
+ encbuf = NULL;
+ auth->encgx_len = enclen;
+ memmove(auth->hashgx, hashbuf, 32);
+ break;
+ }
+
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(encbuf);
- return err;
+ free(buf);
+ free(encbuf);
+ return err;
}
/*
@@ -358,92 +435,92 @@ static gcry_error_t calculate_pubkey_auth(unsigned char **authbufp,
gcry_mpi_t our_dh_pub, gcry_mpi_t their_dh_pub,
OtrlPrivKey *privkey, unsigned int keyid)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- size_t ourpublen, theirpublen, totallen, lenp;
- unsigned char *buf = NULL, *bufp = NULL;
- unsigned char macbuf[32];
- unsigned char *sigbuf = NULL;
- size_t siglen;
-
- /* How big are the DH public keys? */
- gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
- gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
-
- /* How big is the total structure to be MAC'd? */
- totallen = 4 + ourpublen + 4 + theirpublen + 2 + privkey->pubkey_datalen
- + 4;
- buf = malloc(totallen);
- if (buf == NULL) goto memerr;
-
- bufp = buf;
- lenp = totallen;
-
- /* Write the data to be MAC'd */
- write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
- write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
- bufp[0] = ((privkey->pubkey_type) >> 8) & 0xff;
- bufp[1] = (privkey->pubkey_type) & 0xff;
- bufp += 2; lenp -= 2;
- memcpy(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
- debug_data("Pubkey", bufp, privkey->pubkey_datalen);
- bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
- write_int(keyid);
- debug_int("Keyid", bufp-4);
-
- assert(lenp == 0);
-
- /* Do the MAC */
- gcry_md_reset(mackey);
- gcry_md_write(mackey, buf, totallen);
- memcpy(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
-
- free(buf);
- buf = NULL;
-
- /* Sign the MAC */
- err = otrl_privkey_sign(&sigbuf, &siglen, privkey, macbuf, 32);
- if (err) goto err;
-
- /* Calculate the total size of the structure to be encrypted */
- totallen = 2 + privkey->pubkey_datalen + 4 + siglen;
- buf = malloc(totallen);
- if (buf == NULL) goto memerr;
- bufp = buf;
- lenp = totallen;
-
- /* Write the data to be encrypted */
- bufp[0] = ((privkey->pubkey_type) >> 8) & 0xff;
- bufp[1] = (privkey->pubkey_type) & 0xff;
- bufp += 2; lenp -= 2;
- memcpy(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
- debug_data("Pubkey", bufp, privkey->pubkey_datalen);
- bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
- write_int(keyid);
- debug_int("Keyid", bufp-4);
- memcpy(bufp, sigbuf, siglen);
- debug_data("Signature", bufp, siglen);
- bufp += siglen; lenp -= siglen;
- free(sigbuf);
- sigbuf = NULL;
-
- assert(lenp == 0);
-
- /* Now do the encryption */
- err = gcry_cipher_encrypt(enckey, buf, totallen, NULL, 0);
- if (err) goto err;
-
- *authbufp = buf;
- buf = NULL;
- *authlenp = totallen;
-
- return err;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t ourpublen, theirpublen, totallen, lenp;
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char macbuf[32];
+ unsigned char *sigbuf = NULL;
+ size_t siglen;
+
+ /* How big are the DH public keys? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
+ gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
+
+ /* How big is the total structure to be MAC'd? */
+ totallen = 4 + ourpublen + 4 + theirpublen + 2 + privkey->pubkey_datalen
+ + 4;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = totallen;
+
+ /* Write the data to be MAC'd */
+ write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
+ write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
+ bufp[0] = ((privkey->pubkey_type) >> 8) & 0xff;
+ bufp[1] = (privkey->pubkey_type) & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+ write_int(keyid);
+ debug_int("Keyid", bufp-4);
+
+ assert(lenp == 0);
+
+ /* Do the MAC */
+ gcry_md_reset(mackey);
+ gcry_md_write(mackey, buf, totallen);
+ memmove(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
+
+ free(buf);
+ buf = NULL;
+
+ /* Sign the MAC */
+ err = otrl_privkey_sign(&sigbuf, &siglen, privkey, macbuf, 32);
+ if (err) goto err;
+
+ /* Calculate the total size of the structure to be encrypted */
+ totallen = 2 + privkey->pubkey_datalen + 4 + siglen;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+ bufp = buf;
+ lenp = totallen;
+
+ /* Write the data to be encrypted */
+ bufp[0] = ((privkey->pubkey_type) >> 8) & 0xff;
+ bufp[1] = (privkey->pubkey_type) & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+ write_int(keyid);
+ debug_int("Keyid", bufp-4);
+ memmove(bufp, sigbuf, siglen);
+ debug_data("Signature", bufp, siglen);
+ bufp += siglen; lenp -= siglen;
+ free(sigbuf);
+ sigbuf = NULL;
+
+ assert(lenp == 0);
+
+ /* Now do the encryption */
+ err = gcry_cipher_encrypt(enckey, buf, totallen, NULL, 0);
+ if (err) goto err;
+
+ *authbufp = buf;
+ buf = NULL;
+ *authlenp = totallen;
+
+ return err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(sigbuf);
- return err;
+ free(buf);
+ free(sigbuf);
+ return err;
}
/*
@@ -458,107 +535,107 @@ static gcry_error_t check_pubkey_auth(unsigned char fingerprintbufp[20],
gcry_md_hd_t mackey, gcry_cipher_hd_t enckey,
gcry_mpi_t our_dh_pub, gcry_mpi_t their_dh_pub)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- size_t ourpublen, theirpublen, totallen, lenp;
- unsigned char *buf = NULL, *bufp = NULL;
- unsigned char macbuf[32];
- unsigned short pubkey_type;
- gcry_mpi_t p,q,g,y;
- gcry_sexp_t pubs = NULL;
- unsigned int received_keyid;
- unsigned char *fingerprintstart, *fingerprintend, *sigbuf;
- size_t siglen;
-
- /* Start by decrypting it */
- err = gcry_cipher_decrypt(enckey, authbuf, authlen, NULL, 0);
- if (err) goto err;
-
- bufp = authbuf;
- lenp = authlen;
-
- /* Get the public key and calculate its fingerprint */
- require_len(2);
- pubkey_type = (bufp[0] << 8) + bufp[1];
- bufp += 2; lenp -= 2;
- if (pubkey_type != OTRL_PUBKEY_TYPE_DSA) goto invval;
- fingerprintstart = bufp;
- read_mpi(p);
- read_mpi(q);
- read_mpi(g);
- read_mpi(y);
- fingerprintend = bufp;
- gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbufp,
- fingerprintstart, fingerprintend-fingerprintstart);
- gcry_sexp_build(&pubs, NULL,
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t ourpublen, theirpublen, totallen, lenp;
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char macbuf[32];
+ unsigned short pubkey_type;
+ gcry_mpi_t p,q,g,y;
+ gcry_sexp_t pubs = NULL;
+ unsigned int received_keyid;
+ unsigned char *fingerprintstart, *fingerprintend, *sigbuf;
+ size_t siglen;
+
+ /* Start by decrypting it */
+ err = gcry_cipher_decrypt(enckey, authbuf, authlen, NULL, 0);
+ if (err) goto err;
+
+ bufp = authbuf;
+ lenp = authlen;
+
+ /* Get the public key and calculate its fingerprint */
+ require_len(2);
+ pubkey_type = (bufp[0] << 8) + bufp[1];
+ bufp += 2; lenp -= 2;
+ if (pubkey_type != OTRL_PUBKEY_TYPE_DSA) goto invval;
+ fingerprintstart = bufp;
+ read_mpi(p);
+ read_mpi(q);
+ read_mpi(g);
+ read_mpi(y);
+ fingerprintend = bufp;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbufp,
+ fingerprintstart, fingerprintend-fingerprintstart);
+ gcry_sexp_build(&pubs, NULL,
"(public-key (dsa (p %m)(q %m)(g %m)(y %m)))", p, q, g, y);
- gcry_mpi_release(p);
- gcry_mpi_release(q);
- gcry_mpi_release(g);
- gcry_mpi_release(y);
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(g);
+ gcry_mpi_release(y);
- /* Get the keyid */
- read_int(received_keyid);
- if (received_keyid == 0) goto invval;
+ /* Get the keyid */
+ read_int(received_keyid);
+ if (received_keyid == 0) goto invval;
- /* Get the signature */
- sigbuf = bufp;
- siglen = lenp;
+ /* Get the signature */
+ sigbuf = bufp;
+ siglen = lenp;
- /* How big are the DH public keys? */
- gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
- gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
+ /* How big are the DH public keys? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
+ gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
- /* Now calculate the message to be MAC'd. */
- totallen = 4 + ourpublen + 4 + theirpublen + 2 +
+ /* Now calculate the message to be MAC'd. */
+ totallen = 4 + ourpublen + 4 + theirpublen + 2 +
(fingerprintend - fingerprintstart) + 4;
- buf = malloc(totallen);
- if (buf == NULL) goto memerr;
-
- bufp = buf;
- lenp = totallen;
-
- write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
- write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
- bufp[0] = (pubkey_type >> 8) & 0xff;
- bufp[1] = pubkey_type & 0xff;
- bufp += 2; lenp -= 2;
- memcpy(bufp, fingerprintstart, fingerprintend - fingerprintstart);
- debug_data("Pubkey", bufp, fingerprintend - fingerprintstart);
- bufp += fingerprintend - fingerprintstart;
- lenp -= fingerprintend - fingerprintstart;
- write_int(received_keyid);
- debug_int("Keyid", bufp-4);
-
- assert(lenp == 0);
-
- /* Do the MAC */
- gcry_md_reset(mackey);
- gcry_md_write(mackey, buf, totallen);
- memcpy(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
-
- free(buf);
- buf = NULL;
-
- /* Verify the signature on the MAC */
- err = otrl_privkey_verify(sigbuf, siglen, pubkey_type, pubs, macbuf, 32);
- if (err) goto err;
- gcry_sexp_release(pubs);
- pubs = NULL;
-
- /* Everything checked out */
- *keyidp = received_keyid;
-
- return err;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = totallen;
+
+ write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
+ write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
+ bufp[0] = (pubkey_type >> 8) & 0xff;
+ bufp[1] = pubkey_type & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, fingerprintstart, fingerprintend - fingerprintstart);
+ debug_data("Pubkey", bufp, fingerprintend - fingerprintstart);
+ bufp += fingerprintend - fingerprintstart;
+ lenp -= fingerprintend - fingerprintstart;
+ write_int(received_keyid);
+ debug_int("Keyid", bufp-4);
+
+ assert(lenp == 0);
+
+ /* Do the MAC */
+ gcry_md_reset(mackey);
+ gcry_md_write(mackey, buf, totallen);
+ memmove(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
+
+ free(buf);
+ buf = NULL;
+
+ /* Verify the signature on the MAC */
+ err = otrl_privkey_verify(sigbuf, siglen, pubkey_type, pubs, macbuf, 32);
+ if (err) goto err;
+ gcry_sexp_release(pubs);
+ pubs = NULL;
+
+ /* Everything checked out */
+ *keyidp = received_keyid;
+
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- gcry_sexp_release(pubs);
- return err;
+ free(buf);
+ gcry_sexp_release(pubs);
+ return err;
}
/*
@@ -569,67 +646,74 @@ err:
static gcry_error_t create_revealsig_message(OtrlAuthInfo *auth,
OtrlPrivKey *privkey)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp, *startmac;
- size_t buflen, lenp;
-
- unsigned char *authbuf = NULL;
- size_t authlen;
-
- /* Get the encrypted authenticator */
- err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1, auth->enc_c,
- auth->our_dh.pub, auth->their_pub, privkey, auth->our_keyid);
- if (err) goto err;
-
- buflen = 3 + 4 + 16 + 4 + authlen + 20;
- buf = malloc(buflen);
- if (buf == NULL) goto memerr;
-
- bufp = buf;
- lenp = buflen;
-
- memcpy(bufp, "\x00\x02\x11", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
-
- /* r */
- write_int(16);
- memcpy(bufp, auth->r, 16);
- debug_data("r", bufp, 16);
- bufp += 16; lenp -= 16;
-
- /* Encrypted authenticator */
- startmac = bufp;
- write_int(authlen);
- memcpy(bufp, authbuf, authlen);
- debug_data("auth", bufp, authlen);
- bufp += authlen; lenp -= authlen;
- free(authbuf);
- authbuf = NULL;
-
- /* MAC it, but only take the first 20 bytes */
- gcry_md_reset(auth->mac_m2);
- gcry_md_write(auth->mac_m2, startmac, bufp - startmac);
- memcpy(bufp, gcry_md_read(auth->mac_m2, GCRY_MD_SHA256), 20);
- debug_data("MAC", bufp, 20);
- bufp += 20; lenp -= 20;
-
- assert(lenp == 0);
-
- free(auth->lastauthmsg);
- auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
- if (auth->lastauthmsg == NULL) goto memerr;
- free(buf);
- buf = NULL;
-
- return err;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp, *startmac;
+ size_t buflen, lenp;
+
+ unsigned char *authbuf = NULL;
+ size_t authlen;
+
+ /* Get the encrypted authenticator */
+ err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1, auth->enc_c,
+ auth->our_dh.pub, auth->their_pub, privkey, auth->our_keyid);
+ if (err) goto err;
+
+ 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;
+
+ /* 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);
+ memmove(bufp, auth->r, 16);
+ debug_data("r", bufp, 16);
+ bufp += 16; lenp -= 16;
+
+ /* Encrypted authenticator */
+ startmac = bufp;
+ write_int(authlen);
+ memmove(bufp, authbuf, authlen);
+ debug_data("auth", bufp, authlen);
+ bufp += authlen; lenp -= authlen;
+ free(authbuf);
+ authbuf = NULL;
+
+ /* MAC it, but only take the first 20 bytes */
+ gcry_md_reset(auth->mac_m2);
+ gcry_md_write(auth->mac_m2, startmac, bufp - startmac);
+ memmove(bufp, gcry_md_read(auth->mac_m2, GCRY_MD_SHA256), 20);
+ debug_data("MAC", bufp, 20);
+ bufp += 20; lenp -= 20;
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
+
+ return err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(authbuf);
- return err;
+ free(buf);
+ free(authbuf);
+ return err;
}
/*
@@ -640,62 +724,69 @@ err:
static gcry_error_t create_signature_message(OtrlAuthInfo *auth,
OtrlPrivKey *privkey)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp, *startmac;
- size_t buflen, lenp;
-
- unsigned char *authbuf = NULL;
- size_t authlen;
-
- /* Get the encrypted authenticator */
- err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1p,
- auth->enc_cp, auth->our_dh.pub, auth->their_pub, privkey,
- auth->our_keyid);
- if (err) goto err;
-
- buflen = 3 + 4 + authlen + 20;
- buf = malloc(buflen);
- if (buf == NULL) goto memerr;
-
- bufp = buf;
- lenp = buflen;
-
- memcpy(bufp, "\x00\x02\x12", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
-
- /* Encrypted authenticator */
- startmac = bufp;
- write_int(authlen);
- memcpy(bufp, authbuf, authlen);
- debug_data("auth", bufp, authlen);
- bufp += authlen; lenp -= authlen;
- free(authbuf);
- authbuf = NULL;
-
- /* MAC it, but only take the first 20 bytes */
- gcry_md_reset(auth->mac_m2p);
- gcry_md_write(auth->mac_m2p, startmac, bufp - startmac);
- memcpy(bufp, gcry_md_read(auth->mac_m2p, GCRY_MD_SHA256), 20);
- debug_data("MAC", bufp, 20);
- bufp += 20; lenp -= 20;
-
- assert(lenp == 0);
-
- free(auth->lastauthmsg);
- auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
- if (auth->lastauthmsg == NULL) goto memerr;
- free(buf);
- buf = NULL;
-
- return err;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp, *startmac;
+ size_t buflen, lenp;
+
+ unsigned char *authbuf = NULL;
+ size_t authlen;
+
+ /* Get the encrypted authenticator */
+ err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1p,
+ auth->enc_cp, auth->our_dh.pub, auth->their_pub, privkey,
+ auth->our_keyid);
+ if (err) goto err;
+
+ 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;
+
+ /* 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;
+ write_int(authlen);
+ memmove(bufp, authbuf, authlen);
+ debug_data("auth", bufp, authlen);
+ bufp += authlen; lenp -= authlen;
+ free(authbuf);
+ authbuf = NULL;
+
+ /* MAC it, but only take the first 20 bytes */
+ gcry_md_reset(auth->mac_m2p);
+ gcry_md_write(auth->mac_m2p, startmac, bufp - startmac);
+ memmove(bufp, gcry_md_read(auth->mac_m2p, GCRY_MD_SHA256), 20);
+ debug_data("MAC", bufp, 20);
+ bufp += 20; lenp -= 20;
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
+
+ return err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(authbuf);
- return err;
+ free(buf);
+ free(authbuf);
+ return err;
}
/*
@@ -706,85 +797,99 @@ err:
gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
int *havemsgp, OtrlPrivKey *privkey)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp = NULL;
- size_t buflen, lenp;
- gcry_mpi_t incoming_pub = NULL;
- int res;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ size_t buflen, lenp;
+ gcry_mpi_t incoming_pub = NULL;
+ int res;
+ unsigned int msg_version;
- *havemsgp = 0;
+ *havemsgp = 0;
- res = otrl_base64_otr_decode(keymsg, &buf, &buflen);
- if (res == -1) goto memerr;
- if (res == -2) goto invval;
+ msg_version = otrl_proto_message_version(keymsg);
- bufp = buf;
- lenp = buflen;
+ res = otrl_base64_otr_decode(keymsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
- /* Header */
- if (memcmp(bufp, "\x00\x02\x0a", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ bufp = buf;
+ lenp = buflen;
- /* g^y */
- read_mpi(incoming_pub);
+ /* Header */
+ skip_header('\x0a');
- if (lenp != 0) goto invval;
- free(buf);
- buf = NULL;
+ if (msg_version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
- switch(auth->authstate) {
- case OTRL_AUTHSTATE_AWAITING_DHKEY:
- /* Store the incoming public key */
- gcry_mpi_release(auth->their_pub);
- auth->their_pub = incoming_pub;
- incoming_pub = NULL;
-
- /* Compute the encryption and MAC keys */
- err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
- auth->their_pub, auth->secure_session_id,
- &(auth->secure_session_id_len),
- &(auth->enc_c), &(auth->enc_cp),
- &(auth->mac_m1), &(auth->mac_m1p),
- &(auth->mac_m2), &(auth->mac_m2p));
- if (err) goto err;
+ /* g^y */
+ read_mpi(incoming_pub);
- /* Create the Reveal Signature Message */
- err = create_revealsig_message(auth, privkey);
- if (err) goto err;
- *havemsgp = 1;
- auth->authstate = OTRL_AUTHSTATE_AWAITING_SIG;
+ if (lenp != 0) goto invval;
+ free(buf);
+ buf = NULL;
- break;
+ 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;
+ incoming_pub = NULL;
+
+ /* Compute the encryption and MAC keys */
+ err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->enc_c), &(auth->enc_cp),
+ &(auth->mac_m1), &(auth->mac_m1p),
+ &(auth->mac_m2), &(auth->mac_m2p));
+ if (err) goto err;
+
+ /* Create the Reveal Signature Message */
+ err = create_revealsig_message(auth, privkey);
+ if (err) goto err;
+ *havemsgp = 1;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_SIG;
+
+ break;
case OTRL_AUTHSTATE_AWAITING_SIG:
- if (gcry_mpi_cmp(incoming_pub, auth->their_pub) == 0) {
+ if (gcry_mpi_cmp(incoming_pub, auth->their_pub) == 0) {
/* Retransmit the Reveal Signature Message */
*havemsgp = 1;
- } else {
+ } else {
/* Ignore this message */
*havemsgp = 0;
- }
- break;
+ }
+ break;
case OTRL_AUTHSTATE_NONE:
case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
case OTRL_AUTHSTATE_V1_SETUP:
- /* Ignore this message */
- *havemsgp = 0;
- break;
- }
+ /* Ignore this message */
+ *havemsgp = 0;
+ break;
+ }
- gcry_mpi_release(incoming_pub);
- return err;
+ gcry_mpi_release(incoming_pub);
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- gcry_mpi_release(incoming_pub);
- return err;
+ free(buf);
+ gcry_mpi_release(incoming_pub);
+ return err;
}
/*
@@ -799,162 +904,172 @@ gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp = NULL, *gxbuf = NULL;
- unsigned char *authstart, *authend, *macstart;
- size_t buflen, lenp, rlen, authlen;
- gcry_cipher_hd_t enc = NULL;
- gcry_mpi_t incoming_pub = NULL;
- unsigned char ctr[16], hashbuf[32];
- int res;
-
- *havemsgp = 0;
-
- res = otrl_base64_otr_decode(revealmsg, &buf, &buflen);
- if (res == -1) goto memerr;
- if (res == -2) goto invval;
-
- bufp = buf;
- lenp = buflen;
-
- /* Header */
- if (memcmp(bufp, "\x00\x02\x11", 3)) goto invval;
- bufp += 3; lenp -= 3;
-
- /* r */
- read_int(rlen);
- if (rlen != 16) goto invval;
- require_len(rlen);
- memcpy(auth->r, bufp, rlen);
- bufp += rlen; lenp -= rlen;
-
- /* auth */
- authstart = bufp;
- read_int(authlen);
- require_len(authlen);
- bufp += authlen; lenp -= authlen;
- authend = bufp;
-
- /* MAC */
- require_len(20);
- macstart = bufp;
- bufp += 20; lenp -= 20;
-
- if (lenp != 0) goto invval;
-
- switch(auth->authstate) {
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL, *gxbuf = NULL;
+ unsigned char *authstart, *authend, *macstart;
+ size_t buflen, lenp, rlen, authlen;
+ gcry_cipher_hd_t enc = NULL;
+ gcry_mpi_t incoming_pub = NULL;
+ unsigned char ctr[16], hashbuf[32];
+ int res;
+ unsigned char version;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(revealmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ require_len(3);
+ version = bufp[1];
+
+ /* Header */
+ skip_header('\x11');
+
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
+
+ /* r */
+ read_int(rlen);
+ if (rlen != 16) goto invval;
+ require_len(rlen);
+ memmove(auth->r, bufp, rlen);
+ bufp += rlen; lenp -= rlen;
+
+ /* auth */
+ authstart = bufp;
+ read_int(authlen);
+ require_len(authlen);
+ bufp += authlen; lenp -= authlen;
+ authend = bufp;
+
+ /* MAC */
+ require_len(20);
+ macstart = bufp;
+ bufp += 20; lenp -= 20;
+
+ if (lenp != 0) goto invval;
+
+ switch(auth->authstate) {
case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
- gxbuf = malloc(auth->encgx_len);
- if (auth->encgx_len && gxbuf == NULL) goto memerr;
-
- /* Use r to decrypt the value of g^x we received earlier */
- err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
- GCRY_CIPHER_SECURE);
- if (err) goto err;
-
- err = gcry_cipher_setkey(enc, auth->r, 16);
- if (err) goto err;
-
- memset(ctr, 0, 16);
- err = gcry_cipher_setctr(enc, ctr, 16);
- if (err) goto err;
-
- err = gcry_cipher_decrypt(enc, gxbuf, auth->encgx_len,
- auth->encgx, auth->encgx_len);
- if (err) goto err;
-
- gcry_cipher_close(enc);
- enc = NULL;
-
- /* Check the hash */
- gcry_md_hash_buffer(GCRY_MD_SHA256, hashbuf, gxbuf,
- auth->encgx_len);
- if (memcmp(hashbuf, auth->hashgx, 32)) goto decfail;
-
- /* Extract g^x */
- bufp = gxbuf;
- lenp = auth->encgx_len;
-
- read_mpi(incoming_pub);
- free(gxbuf);
- gxbuf = NULL;
-
- if (lenp != 0) goto invval;
-
- gcry_mpi_release(auth->their_pub);
- auth->their_pub = incoming_pub;
- incoming_pub = NULL;
-
- /* Compute the encryption and MAC keys */
- err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
- auth->their_pub, auth->secure_session_id,
- &(auth->secure_session_id_len),
- &(auth->enc_c), &(auth->enc_cp),
- &(auth->mac_m1), &(auth->mac_m1p),
- &(auth->mac_m2), &(auth->mac_m2p));
- if (err) goto err;
-
- /* Check the MAC */
- gcry_md_reset(auth->mac_m2);
- gcry_md_write(auth->mac_m2, authstart, authend - authstart);
- if (memcmp(macstart,
+ gxbuf = malloc(auth->encgx_len);
+ if (auth->encgx_len && gxbuf == NULL) goto memerr;
+
+ /* Use r to decrypt the value of g^x we received earlier */
+ err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
+ GCRY_CIPHER_SECURE);
+ if (err) goto err;
+
+ err = gcry_cipher_setkey(enc, auth->r, 16);
+ if (err) goto err;
+
+ memset(ctr, 0, 16);
+ err = gcry_cipher_setctr(enc, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_decrypt(enc, gxbuf, auth->encgx_len,
+ auth->encgx, auth->encgx_len);
+ if (err) goto err;
+
+ gcry_cipher_close(enc);
+ enc = NULL;
+
+ /* Check the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashbuf, gxbuf,
+ auth->encgx_len);
+ /* This isn't comparing secret data, but may as well use the
+ * constant-time version. */
+ if (otrl_mem_differ(hashbuf, auth->hashgx, 32)) goto decfail;
+
+ /* Extract g^x */
+ bufp = gxbuf;
+ lenp = auth->encgx_len;
+
+ read_mpi(incoming_pub);
+ free(gxbuf);
+ gxbuf = NULL;
+
+ if (lenp != 0) goto invval;
+
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = incoming_pub;
+ incoming_pub = NULL;
+
+ /* Compute the encryption and MAC keys */
+ err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->enc_c), &(auth->enc_cp),
+ &(auth->mac_m1), &(auth->mac_m1p),
+ &(auth->mac_m2), &(auth->mac_m2p));
+ if (err) goto err;
+
+ /* Check the MAC */
+ gcry_md_reset(auth->mac_m2);
+ gcry_md_write(auth->mac_m2, authstart, authend - authstart);
+
+ if (otrl_mem_differ(macstart,
gcry_md_read(auth->mac_m2, GCRY_MD_SHA256),
20)) goto invval;
- /* Check the auth */
- err = check_pubkey_auth(auth->their_fingerprint,
- &(auth->their_keyid), authstart + 4,
- authend - authstart - 4, auth->mac_m1, auth->enc_c,
- auth->our_dh.pub, auth->their_pub);
- if (err) goto err;
-
- authstart = NULL;
- authend = NULL;
- macstart = NULL;
- free(buf);
- buf = NULL;
-
- /* Create the Signature Message */
- err = create_signature_message(auth, privkey);
- if (err) goto err;
-
- /* 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;
- auth->our_keyid = 0;
- auth->authstate = OTRL_AUTHSTATE_NONE;
-
- break;
+ /* Check the auth */
+ err = check_pubkey_auth(auth->their_fingerprint,
+ &(auth->their_keyid), authstart + 4,
+ authend - authstart - 4, auth->mac_m1, auth->enc_c,
+ auth->our_dh.pub, auth->their_pub);
+ if (err) goto err;
+
+ authstart = NULL;
+ authend = NULL;
+ macstart = NULL;
+ free(buf);
+ buf = NULL;
+
+ /* Create the Signature Message */
+ err = create_signature_message(auth, privkey);
+ if (err) goto err;
+
+ /* No error? Then we've completed our end of the
+ * authentication. */
+ auth->session_id_half = OTRL_SESSIONID_SECOND_HALF_BOLD;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ *havemsgp = 1;
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+
+ break;
case OTRL_AUTHSTATE_NONE:
case OTRL_AUTHSTATE_AWAITING_DHKEY:
case OTRL_AUTHSTATE_AWAITING_SIG:
case OTRL_AUTHSTATE_V1_SETUP:
- /* Ignore this message */
- *havemsgp = 0;
- free(buf);
- buf = NULL;
- break;
- }
+ /* Ignore this message */
+ *havemsgp = 0;
+ free(buf);
+ buf = NULL;
+ break;
+ }
- return err;
+ return err;
decfail:
- err = gcry_error(GPG_ERR_NO_ERROR);
- goto err;
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(gxbuf);
- gcry_cipher_close(enc);
- gcry_mpi_release(incoming_pub);
- return err;
+ free(buf);
+ free(gxbuf);
+ gcry_cipher_close(enc);
+ gcry_mpi_release(incoming_pub);
+ return err;
}
/*
@@ -968,92 +1083,101 @@ gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp = NULL;
- unsigned char *authstart, *authend, *macstart;
- size_t buflen, lenp, authlen;
- int res;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char *authstart, *authend, *macstart;
+ size_t buflen, lenp, authlen;
+ int res;
+ unsigned char version;
- *havemsgp = 0;
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(sigmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
- res = otrl_base64_otr_decode(sigmsg, &buf, &buflen);
- if (res == -1) goto memerr;
- if (res == -2) goto invval;
+ require_len(3);
+ version = bufp[1];
- bufp = buf;
- lenp = buflen;
+ /* Header */
+ skip_header('\x12');
- /* Header */
- if (memcmp(bufp, "\x00\x02\x12", 3)) goto invval;
- bufp += 3; lenp -= 3;
+ if (version == 3) {
+ require_len(8);
+ bufp += 8; lenp -= 8;
+ }
- /* auth */
- authstart = bufp;
- read_int(authlen);
- require_len(authlen);
- bufp += authlen; lenp -= authlen;
- authend = bufp;
+ /* auth */
+ authstart = bufp;
+ read_int(authlen);
+ require_len(authlen);
+ bufp += authlen; lenp -= authlen;
+ authend = bufp;
- /* MAC */
- require_len(20);
- macstart = bufp;
- bufp += 20; lenp -= 20;
+ /* MAC */
+ require_len(20);
+ macstart = bufp;
+ bufp += 20; lenp -= 20;
- if (lenp != 0) goto invval;
+ if (lenp != 0) goto invval;
- switch(auth->authstate) {
+ switch(auth->authstate) {
case OTRL_AUTHSTATE_AWAITING_SIG:
- /* Check the MAC */
- gcry_md_reset(auth->mac_m2p);
- gcry_md_write(auth->mac_m2p, authstart, authend - authstart);
- if (memcmp(macstart,
+ /* Check the MAC */
+ gcry_md_reset(auth->mac_m2p);
+ gcry_md_write(auth->mac_m2p, authstart, authend - authstart);
+ if (otrl_mem_differ(macstart,
gcry_md_read(auth->mac_m2p, GCRY_MD_SHA256),
20)) goto invval;
- /* Check the auth */
- err = check_pubkey_auth(auth->their_fingerprint,
- &(auth->their_keyid), authstart + 4,
- authend - authstart - 4, auth->mac_m1p, auth->enc_cp,
- auth->our_dh.pub, auth->their_pub);
- if (err) goto err;
-
- authstart = NULL;
- authend = NULL;
- macstart = NULL;
- free(buf);
- buf = NULL;
-
- /* 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);
- auth->lastauthmsg = NULL;
- *havemsgp = 1;
- auth->our_keyid = 0;
- auth->authstate = OTRL_AUTHSTATE_NONE;
-
- break;
+ /* Check the auth */
+ err = check_pubkey_auth(auth->their_fingerprint,
+ &(auth->their_keyid), authstart + 4,
+ authend - authstart - 4, auth->mac_m1p, auth->enc_cp,
+ auth->our_dh.pub, auth->their_pub);
+ if (err) goto err;
+
+ authstart = NULL;
+ authend = NULL;
+ macstart = NULL;
+ free(buf);
+ buf = NULL;
+
+ /* No error? Then we've completed our end of the
+ * authentication. */
+ auth->session_id_half = OTRL_SESSIONID_FIRST_HALF_BOLD;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = NULL;
+ *havemsgp = 0;
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+
+ break;
case OTRL_AUTHSTATE_NONE:
case OTRL_AUTHSTATE_AWAITING_DHKEY:
case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
case OTRL_AUTHSTATE_V1_SETUP:
- /* Ignore this message */
- *havemsgp = 0;
- break;
- }
+ /* Ignore this message */
+ *havemsgp = 0;
+ free(buf);
+ buf = NULL;
+ break;
+ }
- return err;
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- return err;
+ free(buf);
+ return err;
}
/* Version 1 routines, for compatibility */
@@ -1066,75 +1190,75 @@ err:
static gcry_error_t create_v1_key_exchange_message(OtrlAuthInfo *auth,
unsigned char reply, OtrlPrivKey *privkey)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- unsigned char *buf = NULL, *bufp = NULL, *sigbuf = NULL;
- size_t lenp, ourpublen, totallen, siglen;
- unsigned char hashbuf[20];
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ unsigned char *buf = NULL, *bufp = NULL, *sigbuf = NULL;
+ size_t lenp, ourpublen, totallen, siglen;
+ unsigned char hashbuf[20];
- if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA) {
+ if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA) {
return gpg_error(GPG_ERR_INV_VALUE);
- }
+ }
- /* How big is the DH public key? */
- gcry_mpi_print(format, NULL, 0, &ourpublen, auth->our_dh.pub);
+ /* How big is the DH public key? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, auth->our_dh.pub);
- totallen = 3 + 1 + privkey->pubkey_datalen + 4 + 4 + ourpublen + 40;
- buf = malloc(totallen);
- if (buf == NULL) goto memerr;
+ totallen = 3 + 1 + privkey->pubkey_datalen + 4 + 4 + ourpublen + 40;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
- bufp = buf;
- lenp = totallen;
+ bufp = buf;
+ lenp = totallen;
- memcpy(bufp, "\x00\x01\x0a", 3); /* header */
- debug_data("Header", bufp, 3);
- bufp += 3; lenp -= 3;
+ memmove(bufp, "\x00\x01\x0a", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
- bufp[0] = reply;
- debug_data("Reply", bufp, 1);
- bufp += 1; lenp -= 1;
+ bufp[0] = reply;
+ debug_data("Reply", bufp, 1);
+ bufp += 1; lenp -= 1;
- memcpy(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
- debug_data("Pubkey", bufp, privkey->pubkey_datalen);
- bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
- write_int(auth->our_keyid);
- debug_int("Keyid", bufp-4);
+ write_int(auth->our_keyid);
+ debug_int("Keyid", bufp-4);
- write_mpi(auth->our_dh.pub, ourpublen, "D-H y");
+ write_mpi(auth->our_dh.pub, ourpublen, "D-H y");
- /* Hash all the data written so far, and sign the hash */
- gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
+ /* Hash all the data written so far, and sign the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
- err = otrl_privkey_sign(&sigbuf, &siglen, privkey, hashbuf, 20);
- if (err) goto err;
+ err = otrl_privkey_sign(&sigbuf, &siglen, privkey, hashbuf, 20);
+ if (err) goto err;
- if (siglen != 40) goto invval;
- memcpy(bufp, sigbuf, 40);
- debug_data("Signature", bufp, 40);
- bufp += 40; lenp -= 40;
- free(sigbuf);
- sigbuf = NULL;
+ if (siglen != 40) goto invval;
+ memmove(bufp, sigbuf, 40);
+ debug_data("Signature", bufp, 40);
+ bufp += 40; lenp -= 40;
+ free(sigbuf);
+ sigbuf = NULL;
- assert(lenp == 0);
+ assert(lenp == 0);
- free(auth->lastauthmsg);
- auth->lastauthmsg = otrl_base64_otr_encode(buf, totallen);
- if (auth->lastauthmsg == NULL) goto memerr;
- free(buf);
- buf = NULL;
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, totallen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
- return err;
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- free(sigbuf);
- return err;
+ free(buf);
+ free(sigbuf);
+ return err;
}
/*
@@ -1147,27 +1271,28 @@ err:
gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh,
unsigned int our_keyid, OtrlPrivKey *privkey)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- /* Clear out this OtrlAuthInfo and start over */
- otrl_auth_clear(auth);
- auth->initiated = 1;
+ /* 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) {
+ /* Import the given DH keypair, or else create a fresh one */
+ if (our_dh) {
otrl_dh_keypair_copy(&(auth->our_dh), our_dh);
auth->our_keyid = our_keyid;
- } else {
+ } else {
otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
auth->our_keyid = 1;
- }
+ }
- err = create_v1_key_exchange_message(auth, 0, privkey);
- if (!err) {
+ err = create_v1_key_exchange_message(auth, 0, privkey);
+ if (!err) {
auth->authstate = OTRL_AUTHSTATE_V1_SETUP;
- }
+ }
- return err;
+ return err;
}
/*
@@ -1184,230 +1309,265 @@ gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata)
{
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- unsigned char *buf = NULL, *bufp = NULL;
- unsigned char *fingerprintstart, *fingerprintend;
- unsigned char fingerprintbuf[20], hashbuf[20];
- gcry_mpi_t p, q, g, y, received_pub = NULL;
- gcry_sexp_t pubs = NULL;
- size_t buflen, lenp;
- unsigned char received_reply;
- unsigned int received_keyid;
- int res;
-
- *havemsgp = 0;
-
- res = otrl_base64_otr_decode(keyexchmsg, &buf, &buflen);
- if (res == -1) goto memerr;
- if (res == -2) goto invval;
-
- bufp = buf;
- lenp = buflen;
-
- /* Header */
- require_len(3);
- if (memcmp(bufp, "\x00\x01\x0a", 3)) goto invval;
- bufp += 3; lenp -= 3;
-
- /* Reply */
- require_len(1);
- received_reply = bufp[0];
- bufp += 1; lenp -= 1;
-
- /* Public Key */
- fingerprintstart = bufp;
- read_mpi(p);
- read_mpi(q);
- read_mpi(g);
- read_mpi(y);
- fingerprintend = bufp;
- gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbuf,
- fingerprintstart, fingerprintend-fingerprintstart);
- gcry_sexp_build(&pubs, NULL,
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char *fingerprintstart, *fingerprintend;
+ unsigned char fingerprintbuf[20], hashbuf[20];
+ gcry_mpi_t p, q, g, y, received_pub = NULL;
+ gcry_sexp_t pubs = NULL;
+ size_t buflen, lenp;
+ unsigned char received_reply;
+ unsigned int received_keyid;
+ int res;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(keyexchmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ require_len(3);
+ if (memcmp(bufp, "\x00\x01\x0a", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* Reply */
+ require_len(1);
+ received_reply = bufp[0];
+ bufp += 1; lenp -= 1;
+
+ /* Public Key */
+ fingerprintstart = bufp;
+ read_mpi(p);
+ read_mpi(q);
+ read_mpi(g);
+ read_mpi(y);
+ fingerprintend = bufp;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbuf,
+ fingerprintstart, fingerprintend-fingerprintstart);
+ gcry_sexp_build(&pubs, NULL,
"(public-key (dsa (p %m)(q %m)(g %m)(y %m)))", p, q, g, y);
- gcry_mpi_release(p);
- gcry_mpi_release(q);
- gcry_mpi_release(g);
- gcry_mpi_release(y);
-
- /* keyid */
- read_int(received_keyid);
- if (received_keyid == 0) goto invval;
-
- /* D-H pubkey */
- read_mpi(received_pub);
-
- /* Verify the signature */
- if (lenp != 40) goto invval;
- gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
- err = otrl_privkey_verify(bufp, lenp, OTRL_PUBKEY_TYPE_DSA,
- pubs, hashbuf, 20);
- if (err) goto err;
- gcry_sexp_release(pubs);
- pubs = NULL;
- free(buf);
- buf = NULL;
-
- if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP && received_reply == 0x01) {
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(g);
+ gcry_mpi_release(y);
+
+ /* keyid */
+ read_int(received_keyid);
+ if (received_keyid == 0) goto invval;
+
+ /* D-H pubkey */
+ read_mpi(received_pub);
+
+ /* Verify the signature */
+ if (lenp != 40) goto invval;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
+ err = otrl_privkey_verify(bufp, lenp, OTRL_PUBKEY_TYPE_DSA,
+ pubs, hashbuf, 20);
+ if (err) goto err;
+ gcry_sexp_release(pubs);
+ 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. */
err = gpg_error(GPG_ERR_NO_ERROR);
goto err;
- }
+ }
- if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP) {
+ if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP) {
/* Clear the auth and start over */
otrl_auth_clear(auth);
- }
+ }
- /* Everything checked out */
- auth->their_keyid = received_keyid;
- gcry_mpi_release(auth->their_pub);
- auth->their_pub = received_pub;
- received_pub = NULL;
- memcpy(auth->their_fingerprint, fingerprintbuf, 20);
+ /* Everything checked out */
+ auth->their_keyid = received_keyid;
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = received_pub;
+ received_pub = NULL;
+ memmove(auth->their_fingerprint, fingerprintbuf, 20);
- if (received_reply == 0x01) {
+ if (received_reply == 0x01) {
/* Don't send a reply to this. */
*havemsgp = 0;
- } else {
+ } else {
/* Import the given DH keypair, or else create a fresh one */
if (our_dh) {
- otrl_dh_keypair_copy(&(auth->our_dh), our_dh);
- auth->our_keyid = our_keyid;
+ otrl_dh_keypair_copy(&(auth->our_dh), our_dh);
+ auth->our_keyid = our_keyid;
} else if (auth->our_keyid == 0) {
- otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
- auth->our_keyid = 1;
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
}
/* Reply with our own Key Exchange Message */
err = create_v1_key_exchange_message(auth, 1, privkey);
if (err) goto err;
*havemsgp = 1;
- }
+ }
- /* Compute the session id */
- err = otrl_dh_compute_v1_session_id(&(auth->our_dh),
- auth->their_pub, auth->secure_session_id,
- &(auth->secure_session_id_len),
- &(auth->session_id_half));
- if (err) goto err;
+ /* Compute the session id */
+ err = otrl_dh_compute_v1_session_id(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->session_id_half));
+ if (err) goto err;
- /* We've completed our end of the authentication */
- auth->protocol_version = 1;
- if (auth_succeeded) err = auth_succeeded(auth, asdata);
- auth->our_keyid = 0;
- auth->authstate = OTRL_AUTHSTATE_NONE;
+ /* We've completed our end of the authentication */
+ auth->protocol_version = 1;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
- return err;
+ return err;
invval:
- err = gcry_error(GPG_ERR_INV_VALUE);
- goto err;
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
memerr:
- err = gcry_error(GPG_ERR_ENOMEM);
+ err = gcry_error(GPG_ERR_ENOMEM);
err:
- free(buf);
- gcry_sexp_release(pubs);
- gcry_mpi_release(received_pub);
- return err;
+ free(buf);
+ gcry_sexp_release(pubs);
+ gcry_mpi_release(received_pub);
+ return err;
+}
+
+/*
+ * 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.
+ */
+void 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;
+ 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);
- fprintf(stderr, "\ntheir_pub (%d):", auth->their_keyid);
- gcry_mpi_dump(auth->their_pub);
+ fprintf(stderr, "\nour_dh (%d):", auth->our_keyid);
+ gcry_mpi_dump(auth->our_dh.pub);
+ fprintf(stderr, "\ntheir_pub (%d):", auth->their_keyid);
+ gcry_mpi_dump(auth->their_pub);
- debug_data("\nTheir fingerprint", auth->their_fingerprint, 20);
- debug_data("\nSecure session id", auth->secure_session_id,
- auth->secure_session_id_len);
- fprintf(stderr, "Sessionid half: %d\n\n", auth->session_id_half);
+ debug_data("\nTheir fingerprint", auth->their_fingerprint, 20);
+ debug_data("\nSecure session id", auth->secure_session_id,
+ auth->secure_session_id_len);
+ fprintf(stderr, "Sessionid half: %d\n\n", auth->session_id_half);
- return gpg_error(GPG_ERR_NO_ERROR);
+ return gpg_error(GPG_ERR_NO_ERROR);
}
int main(int argc, char **argv)
{
- OtrlAuthInfo alice, bob;
- gcry_error_t err;
- int havemsg;
- OtrlUserState us;
- OtrlPrivKey *alicepriv, *bobpriv;
-
- otrl_mem_init();
- otrl_dh_init();
- otrl_auth_new(&alice);
- otrl_auth_new(&bob);
-
- us = otrl_userstate_create();
- otrl_privkey_read(us, "/home/iang/.gaim/otr.private_key");
- alicepriv = otrl_privkey_find(us, "oneeyedian", "prpl-oscar");
- bobpriv = otrl_privkey_find(us, "otr4ian", "prpl-oscar");
-
- printf("\n\n ***** V2 *****\n\n");
-
- err = otrl_auth_start_v2(&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);
- CHECK_ERR
- printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
- err = otrl_auth_handle_key(&bob, alice.lastauthmsg, &havemsg, bobpriv);
- CHECK_ERR
- if (havemsg) {
+ OtrlAuthInfo alice, bob;
+ gcry_error_t err;
+ int havemsg;
+ OtrlUserState us;
+ OtrlPrivKey *alicepriv, *bobpriv;
+
+ otrl_mem_init();
+ otrl_dh_init();
+ otrl_auth_new(&alice);
+ otrl_auth_new(&bob);
+
+ us = otrl_userstate_create();
+ otrl_privkey_read(us, "/home/iang/.gaim/otr.private_key");
+ alicepriv = otrl_privkey_find(us, "oneeyedian", "prpl-oscar");
+ bobpriv = otrl_privkey_find(us, "otr4ian", "prpl-oscar");
+
+ printf("\n\n ***** V2 *****\n\n");
+
+ 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);
+ CHECK_ERR
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ err = otrl_auth_handle_key(&bob, alice.lastauthmsg, &havemsg, bobpriv);
+ CHECK_ERR
+ if (havemsg) {
printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
- } else {
+ } else {
printf("\nIGNORE\n\n");
- }
- err = otrl_auth_handle_revealsig(&alice, bob.lastauthmsg, &havemsg,
- alicepriv, starting, "Alice");
- CHECK_ERR
- if (havemsg) {
- printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
- } else {
+ }
+ err = otrl_auth_handle_revealsig(&alice, bob.lastauthmsg, &havemsg,
+ alicepriv, starting, "Alice");
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg),
+ alice.lastauthmsg);
+ } else {
printf("\nIGNORE\n\n");
- }
- err = otrl_auth_handle_signature(&bob, alice.lastauthmsg, &havemsg,
- starting, "Bob");
- CHECK_ERR
-
- printf("\n\n ***** V1 *****\n\n");
-
- err = otrl_auth_start_v1(&bob, NULL, 0, bobpriv);
- CHECK_ERR
- printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
- err = otrl_auth_handle_v1_key_exchange(&alice, bob.lastauthmsg,
- &havemsg, alicepriv, NULL, 0, starting, "Alice");
- CHECK_ERR
- if (havemsg) {
- printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
- } else {
+ }
+ err = otrl_auth_handle_signature(&bob, alice.lastauthmsg, &havemsg,
+ starting, "Bob");
+ CHECK_ERR
+
+ printf("\n\n ***** V1 *****\n\n");
+
+ err = otrl_auth_start_v1(&bob, NULL, 0, bobpriv);
+ CHECK_ERR
+ printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
+ err = otrl_auth_handle_v1_key_exchange(&alice, bob.lastauthmsg,
+ &havemsg, alicepriv, NULL, 0, starting, "Alice");
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg),
+ alice.lastauthmsg);
+ } else {
printf("\nIGNORE\n\n");
- }
- err = otrl_auth_handle_v1_key_exchange(&bob, alice.lastauthmsg,
- &havemsg, bobpriv, NULL, 0, starting, "Bob");
- CHECK_ERR
- if (havemsg) {
+ }
+ err = otrl_auth_handle_v1_key_exchange(&bob, alice.lastauthmsg,
+ &havemsg, bobpriv, NULL, 0, starting, "Bob");
+ CHECK_ERR
+ if (havemsg) {
printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
- } else {
+ } else {
printf("\nIGNORE\n\n");
- }
+ }
- otrl_userstate_free(us);
- otrl_auth_clear(&alice);
- otrl_auth_clear(&bob);
- return 0;
+ otrl_userstate_free(us);
+ otrl_auth_clear(&alice);
+ otrl_auth_clear(&bob);
+ return 0;
}
#endif
diff --git a/plugins/MirOTR/libotr/src/auth.h b/plugins/MirOTR/libotr/src/auth.h
index ac9c668d94..0b9db544aa 100644
--- a/plugins/MirOTR/libotr/src/auth.h
+++ b/plugins/MirOTR/libotr/src/auth.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,62 +15,73 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AUTH_H__
#define __AUTH_H__
#include <gcrypt.h>
+#include <time.h>
#include "dh.h"
+
typedef enum {
- OTRL_AUTHSTATE_NONE,
- OTRL_AUTHSTATE_AWAITING_DHKEY,
- OTRL_AUTHSTATE_AWAITING_REVEALSIG,
- OTRL_AUTHSTATE_AWAITING_SIG,
- OTRL_AUTHSTATE_V1_SETUP
+ OTRL_AUTHSTATE_NONE,
+ OTRL_AUTHSTATE_AWAITING_DHKEY,
+ OTRL_AUTHSTATE_AWAITING_REVEALSIG,
+ OTRL_AUTHSTATE_AWAITING_SIG,
+ OTRL_AUTHSTATE_V1_SETUP
} OtrlAuthState;
typedef struct {
- OtrlAuthState authstate; /* Our state */
+ 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 */
+
+ unsigned char *encgx; /* The encrypted value of g^x */
+ size_t encgx_len; /* ...and its length */
+ unsigned char r[16]; /* The encryption key */
- DH_keypair our_dh; /* Our D-H key */
- unsigned int our_keyid; /* ...and its keyid */
+ unsigned char hashgx[32]; /* SHA256(g^x) */
- unsigned char *encgx; /* The encrypted value of g^x */
- size_t encgx_len; /* ...and its length */
- unsigned char r[16]; /* The encryption key */
+ gcry_mpi_t their_pub; /* Their D-H public key */
+ unsigned int their_keyid; /* ...and its keyid */
- unsigned char hashgx[32]; /* SHA256(g^x) */
- 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 */
- 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 */
+ unsigned char their_fingerprint[20]; /* The fingerprint of their
+ long-term signing key */
- unsigned char their_fingerprint[20]; /* The fingerprint of their
- long-term signing key */
+ int initiated; /* Did we initiate this
+ authentication? */
- int initiated; /* Did we initiate this
- authentication? */
+ unsigned int protocol_version; /* The protocol version number
+ used to authenticate. */
- unsigned int protocol_version; /* The protocol version number
- used to authenticate. */
+ unsigned char secure_session_id[20]; /* The secure session id */
+ size_t secure_session_id_len; /* And its actual length,
+ which may be either 20 (for
+ v1) or 8 (for v2) */
+ OtrlSessionIdHalf session_id_half; /* Which half of the session
+ id gets shown in bold */
- unsigned char secure_session_id[20]; /* The secure session id */
- size_t secure_session_id_len; /* And its actual length,
- which may be either 20 (for
- v1) or 8 (for v2) */
- OtrlSessionIdHalf session_id_half; /* Which half of the session
- id gets shown in bold */
+ char *lastauthmsg; /* The last auth message
+ (base-64 encoded) we sent,
+ in case we need to
+ retransmit it. */
- char *lastauthmsg; /* The last auth message
- (base-64 encoded) we sent,
- in case we need to
- retransmit it. */
+ time_t commit_sent_time; /* The time we last sent the
+ lastauthmsg, if it was a
+ COMMIT message, and this is
+ a master context. 0
+ otherwise. */
} OtrlAuthInfo;
#include "privkey-t.h"
@@ -77,7 +89,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).
@@ -85,11 +97,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
@@ -97,7 +109,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
@@ -154,4 +166,12 @@ 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 Key with a new
+ * instance. The fields copied will depend on the state of the
+ * master auth.
+ */
+void otrl_auth_copy_on_key(OtrlAuthInfo *m_auth, OtrlAuthInfo *auth);
+
#endif
diff --git a/plugins/MirOTR/libotr/src/b64.c b/plugins/MirOTR/libotr/src/b64.c
index 222f56634c..a7d53aa2b1 100644
--- a/plugins/MirOTR/libotr/src/b64.c
+++ b/plugins/MirOTR/libotr/src/b64.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Modified from: */
@@ -27,35 +28,35 @@ 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
\******************************************************************* */
/* system headers */
-#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
/* libotr headers */
@@ -87,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 ]
- : '=';
+ : '=';
}
/*
@@ -119,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;
@@ -147,8 +148,9 @@ static size_t decode(unsigned char *out, const char *in, size_t b64len)
* base64 decode data. Skip non-base64 chars, and terminate at the
* first '=', or the end of the buffer.
*
- * The buffer data must contain at least (base64len / 4) * 3 bytes of
- * space. This function will return the number of bytes actually used.
+ * The buffer data must contain at least ((base64len+3) / 4) * 3 bytes
+ * of space. This function will return the number of bytes actually
+ * used.
*/
size_t otrl_base64_decode(unsigned char *data, const char *base64data,
size_t base64len)
@@ -195,6 +197,16 @@ char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen)
{
char *base64buf;
size_t base64len;
+ const size_t HALF_MAX_SIZE_T = ((size_t)-1) >> 1;
+
+ if (buflen > HALF_MAX_SIZE_T) {
+ /* You somehow have a buffer that's of size more than half of
+ * all addressable memory, and you now want a base64 version in
+ * a new buffer 33% larger? Not going to happen. Exit now,
+ * rather in the malloc below, to avoid integer overflowing the
+ * computation of base64len. */
+ return NULL;
+ }
/* Make the base64-encoding. */
base64len = ((buflen + 2) / 3) * 4;
@@ -202,7 +214,7 @@ char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen)
if (base64buf == NULL) {
return NULL;
}
- memcpy(base64buf, "?OTR:", 5);
+ memmove(base64buf, "?OTR:", 5);
otrl_base64_encode(base64buf+5, buf, buflen);
base64buf[5 + base64len] = '.';
base64buf[5 + base64len + 1] = '\0';
@@ -227,20 +239,26 @@ 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;
}
+ /* Skip over the "?OTR:" */
+ otrtag += 5;
+ msglen -= 5;
+
/* Base64-decode the message */
- rawlen = ((msglen-5) / 4) * 3; /* maximum possible */
+ rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen); /* maximum possible */
rawmsg = malloc(rawlen);
if (!rawmsg && rawlen > 0) {
return -1;
}
- rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
+
+ rawlen = otrl_base64_decode(rawmsg, otrtag, msglen); /* actual size */
*bufp = rawmsg;
*lenp = rawlen;
diff --git a/plugins/MirOTR/libotr/src/b64.h b/plugins/MirOTR/libotr/src/b64.h
index 34ef03f416..bdd584a45e 100644
--- a/plugins/MirOTR/libotr/src/b64.h
+++ b/plugins/MirOTR/libotr/src/b64.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,12 +15,25 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __B64_H__
#define __B64_H__
+#include <stdlib.h>
+
+/* Base64 encodes blocks of this many bytes: */
+#define OTRL_B64_DECODED_LEN 3
+/* into blocks of this many bytes: */
+#define OTRL_B64_ENCODED_LEN 4
+
+/* An encoded block of length encoded_len can turn into a maximum of
+ * this many decoded bytes: */
+#define OTRL_B64_MAX_DECODED_SIZE(encoded_len) \
+ (((encoded_len + OTRL_B64_ENCODED_LEN - 1) / OTRL_B64_ENCODED_LEN) \
+ * OTRL_B64_DECODED_LEN)
+
/*
* base64 encode data. Insert no linebreaks or whitespace.
*
@@ -33,8 +47,9 @@ size_t otrl_base64_encode(char *base64data, const unsigned char *data,
* base64 decode data. Skip non-base64 chars, and terminate at the
* first '=', or the end of the buffer.
*
- * The buffer data must contain at least (base64len / 4) * 3 bytes of
- * space. This function will return the number of bytes actually used.
+ * The buffer data must contain at least ((base64len+3) / 4) * 3 bytes
+ * of space. This function will return the number of bytes actually
+ * used.
*/
size_t otrl_base64_decode(unsigned char *data, const char *base64data,
size_t base64len);
diff --git a/plugins/MirOTR/libotr/src/context.c b/plugins/MirOTR/libotr/src/context.c
index d0d912f76a..44d8b860f7 100644
--- a/plugins/MirOTR/libotr/src/context.c
+++ b/plugins/MirOTR/libotr/src/context.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -26,117 +28,339 @@
/* libotr headers */
#include "context.h"
+#include "instag.h"
+
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+void otrl_auth_dump(FILE *f, const OtrlAuthInfo *auth);
+void otrl_sm_dump(FILE *f, const OtrlSMState *sm);
+
+/* Dump the contents of a context to the FILE *f. */
+void otrl_context_dump(FILE *f, const ConnContext *context)
+{
+ const Fingerprint *fing;
+
+ fprintf(f, "Context %p:\n\n", context);
+
+ fprintf(f, " Username: %s\n", context->username);
+ fprintf(f, " Accountname: %s\n", context->accountname);
+ fprintf(f, " Protocol: %s\n\n", context->protocol);
+ fprintf(f, " Master context: %p%s\n", context->m_context,
+ context->m_context == context ? " IS MASTER" : "");
+ fprintf(f, " Recent recv child: %p\n", context->recent_rcvd_child);
+ fprintf(f, " Recent sent child: %p\n", context->recent_sent_child);
+ fprintf(f, " Recent child: %p\n\n", context->recent_child);
+ fprintf(f, " Our instance: %08x\n", context->our_instance);
+ fprintf(f, " Their instance: %08x\n\n", context->their_instance);
+ fprintf(f, " Msgstate: %d (%s)\n\n", context->msgstate,
+ context->msgstate == OTRL_MSGSTATE_PLAINTEXT ? "PLAINTEXT" :
+ context->msgstate == OTRL_MSGSTATE_ENCRYPTED ? "ENCRYPTED" :
+ context->msgstate == OTRL_MSGSTATE_FINISHED ? "FINISHED" :
+ "INVALID");
+ otrl_auth_dump(f, &context->auth);
+ fprintf(f, "\n Fingerprints:\n");
+ for (fing = context->fingerprint_root.next; fing; fing = fing->next) {
+ fprintf(f, " %p ", fing);
+ if (fing->fingerprint == NULL) {
+ fprintf(f, "(null)");
+ } else {
+ int i;
+ for (i=0;i<20;++i) {
+ fprintf(f, "%02x", fing->fingerprint[i]);
+ }
+ }
+ fprintf(f, " %p", fing->context);
+ if (fing->trust && fing->trust[0]) {
+ fprintf(f, " %s", fing->trust);
+ }
+ fprintf(f, "\n");
+ }
+ fprintf(f, "\n Active fingerprint: %p\n\n", context->active_fingerprint);
+ fprintf(f, " Protocol version: %d\n", context->protocol_version);
+ fprintf(f, " OTR offer: %d (%s)\n\n", context->otr_offer,
+ context->otr_offer == OFFER_NOT ? "NOT" :
+ context->otr_offer == OFFER_SENT ? "SENT" :
+ context->otr_offer == OFFER_REJECTED ? "REJECTED" :
+ context->otr_offer == OFFER_ACCEPTED ? "ACCEPTED" :
+ "INVALID");
+
+ fprintf(f, " Application data: %p\n", context->app_data);
+ if (context->smstate == NULL) {
+ fprintf(f, " SM state: NULL\n");
+ } else {
+ otrl_sm_dump(f, context->smstate);
+ }
+ fprintf(f, "\n");
+}
+
+/* Dump the master context of this context, and all of its children. */
+void otrl_context_siblings_dump(FILE *f, const ConnContext *context)
+{
+ const ConnContext *citer;
+ for (citer = context->m_context;
+ citer && citer->m_context == context->m_context;
+ citer = citer->next) {
+ if (citer == context) {
+ fprintf(f, "*** ");
+ }
+ otrl_context_dump(f, citer);
+ }
+}
+
+/* Dump all contexts. */
+void otrl_context_all_dump(FILE *f, OtrlUserState us)
+{
+ const ConnContext *citer;
+ unsigned int ctxnum = 1;
+ for (citer = us->context_root; citer; citer = citer->next, ++ctxnum) {
+ fprintf(f, "%u. ", ctxnum);
+ otrl_context_dump(f, citer);
+ }
+}
+#endif
/* Create a new connection context. */
static ConnContext * new_context(const char * user, const char * accountname,
const char * protocol)
{
- ConnContext * context;
- OtrlSMState *smstate;
- context = malloc(sizeof(*context));
- assert(context != NULL);
- context->username = _strdup(user);
- context->accountname = _strdup(accountname);
- context->protocol = _strdup(protocol);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
- context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
- otrl_auth_new(&(context->auth));
-
- smstate = malloc(sizeof(OtrlSMState));
- assert(smstate != NULL);
- otrl_sm_state_new(smstate);
- context->smstate = smstate;
-
- context->fingerprint_root.fingerprint = NULL;
- context->fingerprint_root.context = context;
- context->fingerprint_root.next = NULL;
- context->fingerprint_root.tous = NULL;
- context->active_fingerprint = NULL;
- context->their_keyid = 0;
- context->their_y = NULL;
- context->their_old_y = NULL;
- context->our_keyid = 0;
- context->our_dh_key.groupid = 0;
- context->our_dh_key.priv = NULL;
- context->our_dh_key.pub = NULL;
- context->our_old_dh_key.groupid = 0;
- context->our_old_dh_key.priv = NULL;
- context->our_old_dh_key.pub = NULL;
- otrl_dh_session_blank(&(context->sesskeys[0][0]));
- otrl_dh_session_blank(&(context->sesskeys[0][1]));
- otrl_dh_session_blank(&(context->sesskeys[1][0]));
- otrl_dh_session_blank(&(context->sesskeys[1][1]));
- memset(context->sessionid, 0, 20);
- context->sessionid_len = 0;
- context->protocol_version = 0;
- context->numsavedkeys = 0;
- context->preshared_secret = NULL;
- context->preshared_secret_len = 0;
- context->saved_mac_keys = NULL;
- context->generation = 0;
- context->lastsent = 0;
- context->lastmessage = NULL;
- context->may_retransmit = 0;
- context->otr_offer = OFFER_NOT;
- context->app_data = NULL;
- context->app_data_free = NULL;
- context->next = NULL;
- return context;
+ ConnContext * context;
+ OtrlSMState *smstate;
+
+ 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);
+
+ 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;
+ context->fingerprint_root.tous = NULL;
+ context->active_fingerprint = NULL;
+ memset(context->sessionid, 0, 20);
+ context->sessionid_len = 0;
+ context->protocol_version = 0;
+ context->otr_offer = OFFER_NOT;
+ context->app_data = NULL;
+ context->app_data_free = NULL;
+ context->context_priv = otrl_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;
}
-ConnContext * otrl_context_new(const char * user, const char * accountname,
- const char * protocol)
+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)
{
- return new_context(user, accountname, protocol);
+ 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 ||
+ (cresult->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
+ curp->msgstate == OTRL_MSGSTATE_FINISHED)) {
+ 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 from the given
+/* 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_INSTAG_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 ||
+ 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 ||
(usercmp == 0 &&
- (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) ||
+ (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) ||
(usercmp == 0 && acctcmp == 0 &&
- (protocmp = strcmp((*curp)->protocol, protocol)) >= 0))
- /* We're at the right place in the list. We've either found
- * it, or gone too far. */
- break;
+ (protocmp = strcmp((*curp)->protocol, protocol)) > 0) ||
+ (usercmp == 0 && acctcmp == 0 && protocmp == 0
+ && (their_instance < OTRL_MIN_VALID_INSTAG ||
+ ((*curp)->their_instance >= 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 && *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;
}
- if (usercmp == 0 && acctcmp == 0 && protocmp == 0) {
- /* Found it! */
- 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) {
+ }
+
+ 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;
if (*curp) {
- (*curp)->tous = &(newctx->next);
+ (*curp)->tous = &(newctx->next);
}
*curp = newctx;
newctx->tous = curp;
if (add_app_data) {
- add_app_data(data, *curp);
+ add_app_data(data, *curp);
}
- return *curp;
+
+ /* Initialize specified instance tags */
+ if (our_instag) {
+ newctx->our_instance = our_instag->instag;
}
- return NULL;
+
+ 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;
+}
+
+/* Return true iff the given fingerprint is marked as trusted. */
+int otrl_context_is_fingerprint_trusted(Fingerprint *fprint) {
+ return fprint && fprint->trust && fprint->trust[0] != '\0';
+}
+
+/* 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) {
+ 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
@@ -144,107 +368,70 @@ ConnContext * otrl_context_find(OtrlUserState us, const char *user,
Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
unsigned char fingerprint[20], int add_if_missing, int *addedp)
{
- Fingerprint *f = context->fingerprint_root.next;
- if (addedp) *addedp = 0;
- while(f) {
+ 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) {
+ }
+
+ /* Didn't find it. */
+ if (add_if_missing) {
if (addedp) *addedp = 1;
f = malloc(sizeof(*f));
assert(f != NULL);
f->fingerprint = malloc(20);
assert(f->fingerprint != NULL);
- memcpy(f->fingerprint, fingerprint, 20);
+ memmove(f->fingerprint, fingerprint, 20);
f->context = context;
f->trust = NULL;
f->next = context->fingerprint_root.next;
if (f->next) {
- f->next->tous = &(f->next);
+ f->next->tous = &(f->next);
}
context->fingerprint_root.next = f;
f->tous = &(context->fingerprint_root.next);
return f;
- }
- return NULL;
+ }
+ return NULL;
}
/* Set the trust level for a given fingerprint */
void otrl_context_set_trust(Fingerprint *fprint, const char *trust)
{
- if (fprint == NULL) return;
-
- free(fprint->trust);
- fprint->trust = trust ? _strdup(trust) : NULL;
-}
+ if (fprint == NULL) return;
-/* Set the preshared secret for a given fingerprint. Note that this
- * currently only stores the secret in the ConnContext structure, but
- * doesn't yet do anything with it. */
-void otrl_context_set_preshared_secret(ConnContext *context,
- const unsigned char *secret, size_t secret_len)
-{
- free(context->preshared_secret);
- context->preshared_secret = NULL;
- context->preshared_secret_len = 0;
-
- if (secret_len) {
- context->preshared_secret = malloc(secret_len);
- if (context->preshared_secret) {
- memcpy(context->preshared_secret, secret, secret_len);
- context->preshared_secret_len = secret_len;
- }
- }
+ free(fprint->trust);
+ fprint->trust = trust ? strdup(trust) : NULL;
}
/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
void otrl_context_force_finished(ConnContext *context)
{
- context->msgstate = OTRL_MSGSTATE_FINISHED;
- otrl_auth_clear(&(context->auth));
- free(context->fragment);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
- context->active_fingerprint = NULL;
- context->their_keyid = 0;
- gcry_mpi_release(context->their_y);
- context->their_y = NULL;
- gcry_mpi_release(context->their_old_y);
- context->their_old_y = NULL;
- context->our_keyid = 0;
- otrl_dh_keypair_free(&(context->our_dh_key));
- otrl_dh_keypair_free(&(context->our_old_dh_key));
- otrl_dh_session_free(&(context->sesskeys[0][0]));
- otrl_dh_session_free(&(context->sesskeys[0][1]));
- otrl_dh_session_free(&(context->sesskeys[1][0]));
- otrl_dh_session_free(&(context->sesskeys[1][1]));
- memset(context->sessionid, 0, 20);
- context->sessionid_len = 0;
- free(context->preshared_secret);
- context->preshared_secret = NULL;
- context->preshared_secret_len = 0;
- context->protocol_version = 0;
- context->numsavedkeys = 0;
- free(context->saved_mac_keys);
- context->saved_mac_keys = NULL;
- gcry_free(context->lastmessage);
- context->lastmessage = NULL;
- context->may_retransmit = 0;
- otrl_sm_state_free(context->smstate);
+ context->msgstate = OTRL_MSGSTATE_FINISHED;
+ otrl_auth_clear(&(context->auth));
+ context->active_fingerprint = NULL;
+ memset(context->sessionid, 0, 20);
+ context->sessionid_len = 0;
+ context->protocol_version = 0;
+ otrl_sm_state_free(context->smstate);
+ otrl_context_priv_force_finished(context->context_priv);
}
/* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
void otrl_context_force_plaintext(ConnContext *context)
{
- /* First clean up everything we'd need to do for the FINISHED state */
- otrl_context_force_finished(context);
+ /* First clean up everything we'd need to do for the FINISHED state */
+ otrl_context_force_finished(context);
- /* And just set the state properly */
- context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
+ /* And just set the state properly */
+ context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
}
/* Forget a fingerprint (so long as it's not the active one. If it's a
@@ -255,76 +442,106 @@ void otrl_context_force_plaintext(ConnContext *context)
void otrl_context_forget_fingerprint(Fingerprint *fprint,
int and_maybe_context)
{
- ConnContext *context = fprint->context;
- if (fprint == &(context->fingerprint_root)) {
+ ConnContext *context = fprint->context;
+ if (fprint == &(context->fingerprint_root)) {
if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
and_maybe_context) {
- otrl_context_forget(context);
+ otrl_context_forget(context);
}
- } else {
+ } else {
if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
context->active_fingerprint != fprint) {
- free(fprint->fingerprint);
- free(fprint->trust);
- *(fprint->tous) = fprint->next;
- if (fprint->next) {
+
+ free(fprint->fingerprint);
+ free(fprint->trust);
+ *(fprint->tous) = fprint->next;
+ if (fprint->next) {
fprint->next->tous = fprint->tous;
- }
- free(fprint);
- if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
- context->fingerprint_root.next == NULL &&
- and_maybe_context) {
+ }
+ free(fprint);
+ if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
+ context->fingerprint_root.next == NULL &&
+ and_maybe_context) {
/* We just deleted the only fingerprint. Forget the
* whole thing. */
otrl_context_forget(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)
{
- if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return;
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return 1;
- /* Just to be safe, force to plaintext. This also frees any
- * extraneous data lying around. */
- otrl_context_force_plaintext(context);
+ if (context->their_instance == OTRL_INSTAG_MASTER) {
+ ConnContext *c_iter;
- /* First free all the Fingerprints */
- while(context->fingerprint_root.next) {
- otrl_context_forget_fingerprint(context->fingerprint_root.next, 0);
+ for (c_iter = context; c_iter &&
+ c_iter->m_context == context->m_context;
+ c_iter = c_iter->next) {
+ if (c_iter->msgstate != OTRL_MSGSTATE_PLAINTEXT) return 1;
}
- /* Now free all the dynamic info here */
- free(context->username);
- free(context->accountname);
- free(context->protocol);
- free(context->smstate);
- context->username = NULL;
- context->accountname = NULL;
- context->protocol = NULL;
- context->smstate = NULL;
-
- /* Free the application data, if it exists */
- if (context->app_data && context->app_data_free) {
+
+ 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. */
+ otrl_context_force_plaintext(context);
+
+ /* First free all the Fingerprints */
+ while(context->fingerprint_root.next) {
+ otrl_context_forget_fingerprint(context->fingerprint_root.next, 0);
+ }
+ /* Now free all the dynamic info here */
+ free(context->username);
+ free(context->accountname);
+ free(context->protocol);
+ free(context->smstate);
+ context->username = NULL;
+ context->accountname = NULL;
+ context->protocol = NULL;
+ context->smstate = NULL;
+
+ /* Free the application data, if it exists */
+ if (context->app_data && context->app_data_free) {
(context->app_data_free)(context->app_data);
context->app_data = NULL;
- }
+ }
- /* Fix the list linkages */
- *(context->tous) = context->next;
- if (context->next) {
+ /* Fix the list linkages */
+ *(context->tous) = context->next;
+ if (context->next) {
context->next->tous = context->tous;
- }
+ }
- free(context);
+ free(context);
+ return 0;
}
/* Forget all the contexts in a given OtrlUserState. */
void otrl_context_forget_all(OtrlUserState us)
{
- while (us->context_root) {
- otrl_context_force_plaintext(us->context_root);
+ 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_forget(us->context_root);
- }
+ }
}
diff --git a/plugins/MirOTR/libotr/src/context.h b/plugins/MirOTR/libotr/src/context.h
index 91a1b45c52..55cd082845 100644
--- a/plugins/MirOTR/libotr/src/context.h
+++ b/plugins/MirOTR/libotr/src/context.h
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,18 +16,24 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
+#include "context_priv.h"
+
#include <gcrypt.h>
#include "dh.h"
#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 */
@@ -47,23 +55,40 @@ 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 */
+ /* Context information that is meant for internal use */
+
+ ConnContextPriv *context_priv;
+
+ /* Context information that is meant for application use */
+
char * username; /* The user this context is for */
char * accountname; /* The username is relative to
this account... */
char * protocol; /* ... and this protocol */
- char *fragment; /* The part of the fragmented message
- we've seen so far */
- size_t fragment_len; /* The length of fragment */
- unsigned short fragment_n; /* The total number of fragments
- in this message */
- unsigned short fragment_k; /* The highest fragment number
- we've seen so far for this
- message */
+ 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 */
@@ -71,23 +96,13 @@ typedef struct context {
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 */
- unsigned int their_keyid; /* current keyid used by other side;
- this is set to 0 if we get a
- OTRL_TLV_DISCONNECTED message from
- them. */
- gcry_mpi_t their_y; /* Y[their_keyid] (their DH pubkey) */
- gcry_mpi_t their_old_y; /* Y[their_keyid-1] (their prev DH
- pubkey) */
- unsigned int our_keyid; /* current keyid used by us */
- DH_keypair our_dh_key; /* DH key[our_keyid] */
- DH_keypair our_old_dh_key; /* DH key[our_keyid-1] */
-
- DH_sesskeys sesskeys[2][2]; /* sesskeys[i][j] are the session keys
- derived from DH key[our_keyid-i]
- and mpi Y[their_keyid-j] */
+ A pointer into the above list */
unsigned char sessionid[20]; /* The sessionid and bold half */
size_t sessionid_len; /* determined when this private */
@@ -95,24 +110,6 @@ typedef struct context {
unsigned int protocol_version; /* The version of OTR in use */
- unsigned char *preshared_secret; /* A secret you share with this
- user, in order to do
- authentication. */
- size_t preshared_secret_len; /* The length of the above secret. */
-
- /* saved mac keys to be revealed later */
- unsigned int numsavedkeys;
- unsigned char *saved_mac_keys;
-
- /* generation number: increment every time we go private, and never
- * reset to 0 (unless we remove the context entirely) */
- unsigned int generation;
-
- time_t lastsent; /* The last time a Data Message was sent */
- char *lastmessage; /* The plaintext of the last Data Message sent */
- int may_retransmit; /* Is the last message eligible for
- retransmission? */
-
enum {
OFFER_NOT,
OFFER_SENT,
@@ -127,24 +124,32 @@ 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"
-ConnContext * otrl_context_new(const char * user, const char * accountname,
- const char * protocol);
-
-/* 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);
+/* Return true iff the given fingerprint is marked as trusted. */
+int otrl_context_is_fingerprint_trusted(Fingerprint *fprint);
+
+/* 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,
@@ -153,12 +158,6 @@ Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
/* Set the trust level for a given fingerprint */
void otrl_context_set_trust(Fingerprint *fprint, const char *trust);
-/* Set the preshared secret for a given fingerprint. Note that this
- * currently only stores the secret in the ConnContext structure, but
- * doesn't yet do anything with it. */
-void otrl_context_set_preshared_secret(ConnContext *context,
- const unsigned char *secret, size_t secret_len);
-
/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
void otrl_context_force_finished(ConnContext *context);
@@ -173,10 +172,22 @@ 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);
+/* Find requested recent instance */
+ConnContext * otrl_context_find_recent_instance(ConnContext * context,
+ otrl_instag_t recent_instag);
+
+/* 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);
+
#endif
diff --git a/plugins/MirOTR/libotr/src/context_priv.c b/plugins/MirOTR/libotr/src/context_priv.c
new file mode 100644
index 0000000000..47d05b9204
--- /dev/null
+++ b/plugins/MirOTR/libotr/src/context_priv.c
@@ -0,0 +1,95 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
+ * <otr@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+#include <assert.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "context_priv.h"
+
+/* Create a new private connection context */
+ConnContextPriv *otrl_context_priv_new()
+{
+ ConnContextPriv *context_priv;
+ context_priv = malloc(sizeof(*context_priv));
+ assert(context_priv != NULL);
+
+ context_priv->fragment = NULL;
+ context_priv->fragment_len = 0;
+ context_priv->fragment_n = 0;
+ context_priv->fragment_k = 0;
+ context_priv->numsavedkeys = 0;
+ context_priv->saved_mac_keys = NULL;
+ 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;
+ context_priv->their_old_y = NULL;
+ context_priv->our_keyid = 0;
+ context_priv->our_dh_key.groupid = 0;
+ context_priv->our_dh_key.priv = NULL;
+ context_priv->our_dh_key.pub = NULL;
+ context_priv->our_old_dh_key.groupid = 0;
+ context_priv->our_old_dh_key.priv = NULL;
+ context_priv->our_old_dh_key.pub = NULL;
+ otrl_dh_session_blank(&(context_priv->sesskeys[0][0]));
+ otrl_dh_session_blank(&(context_priv->sesskeys[0][1]));
+ otrl_dh_session_blank(&(context_priv->sesskeys[1][0]));
+ otrl_dh_session_blank(&(context_priv->sesskeys[1][1]));
+
+ return context_priv;
+}
+
+/* Resets the appropriate variables when a context
+ * is being force finished
+ */
+void otrl_context_priv_force_finished(ConnContextPriv *context_priv)
+{
+ free(context_priv->fragment);
+ context_priv->fragment = NULL;
+ context_priv->fragment_len = 0;
+ context_priv->fragment_n = 0;
+ context_priv->fragment_k = 0;
+ context_priv->numsavedkeys = 0;
+ free(context_priv->saved_mac_keys);
+ context_priv->saved_mac_keys = NULL;
+ gcry_free(context_priv->lastmessage);
+ context_priv->lastmessage = NULL;
+ context_priv->may_retransmit = 0;
+ context_priv->their_keyid = 0;
+ gcry_mpi_release(context_priv->their_y);
+ context_priv->their_y = NULL;
+ gcry_mpi_release(context_priv->their_old_y);
+ context_priv->their_old_y = NULL;
+ context_priv->our_keyid = 0;
+ otrl_dh_keypair_free(&(context_priv->our_dh_key));
+ otrl_dh_keypair_free(&(context_priv->our_old_dh_key));
+ otrl_dh_session_free(&(context_priv->sesskeys[0][0]));
+ otrl_dh_session_free(&(context_priv->sesskeys[0][1]));
+ otrl_dh_session_free(&(context_priv->sesskeys[1][0]));
+ otrl_dh_session_free(&(context_priv->sesskeys[1][1]));
+}
diff --git a/plugins/MirOTR/libotr/src/context_priv.h b/plugins/MirOTR/libotr/src/context_priv.h
new file mode 100644
index 0000000000..0748074e9b
--- /dev/null
+++ b/plugins/MirOTR/libotr/src/context_priv.h
@@ -0,0 +1,94 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Lisa Du, Nikita Borisov
+ * <otr@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __CONTEXT_PRIV_H__
+#define __CONTEXT_PRIV_H__
+
+#include <gcrypt.h>
+
+#include "dh.h"
+#include "auth.h"
+#include "sm.h"
+
+typedef struct context_priv {
+ /* The part of the fragmented message we've seen so far */
+ char *fragment;
+
+ /* The length of fragment */
+ size_t fragment_len;
+
+ /* The total number of fragments in this message */
+ unsigned short fragment_n;
+
+ /* The highest fragment number we've seen so far for this message */
+ unsigned short fragment_k;
+
+ /* current keyid used by other side; this is set to 0 if we get
+ * a OTRL_TLV_DISCONNECTED message from them. */
+ unsigned int their_keyid;
+
+ /* Y[their_keyid] (their DH pubkey) */
+ gcry_mpi_t their_y;
+
+ /* Y[their_keyid-1] (their prev DH pubkey) */
+ gcry_mpi_t their_old_y;
+
+ /* current keyid used by us */
+ unsigned int our_keyid;
+
+ /* DH key[our_keyid] */
+ DH_keypair our_dh_key;
+
+ /* DH key[our_keyid-1] */
+ DH_keypair our_old_dh_key;
+
+ /* sesskeys[i][j] are the session keys derived from DH
+ * key[our_keyid-i] and mpi Y[their_keyid-j] */
+ DH_sesskeys sesskeys[2][2];
+
+ /* saved mac keys to be revealed later */
+ unsigned int numsavedkeys;
+ unsigned char *saved_mac_keys;
+
+ /* generation number: increment every time we go private, and never
+ * reset to 0 (unless we remove the context entirely) */
+ unsigned int generation;
+
+ /* 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;
+
+ /* Is the last message eligible for retransmission? */
+ int may_retransmit;
+
+} ConnContextPriv;
+
+/* Create a new private connection context. */
+ConnContextPriv *otrl_context_priv_new();
+
+/* Frees up memory that was used in otrl_context_priv_new */
+void otrl_context_priv_force_finished(ConnContextPriv *context_priv);
+
+#endif
diff --git a/plugins/MirOTR/libotr/src/dh.c b/plugins/MirOTR/libotr/src/dh.c
index e23788dd08..d8bc45d10d 100644
--- a/plugins/MirOTR/libotr/src/dh.c
+++ b/plugins/MirOTR/libotr/src/dh.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -26,6 +28,7 @@
/* libotr headers */
#include "dh.h"
+
static const char* DH1536_MODULUS_S = "0x"
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
@@ -49,9 +52,10 @@ static gcry_mpi_t DH1536_GENERATOR = NULL;
*/
void otrl_dh_init(void)
{
- gcry_mpi_scan(&DH1536_MODULUS, GCRYMPI_FMT_HEX, DH1536_MODULUS_S, 0, NULL);
- gcry_mpi_scan(&DH1536_GENERATOR, GCRYMPI_FMT_HEX, DH1536_GENERATOR_S,
- 0, NULL);
+ gcry_mpi_scan(&DH1536_MODULUS, GCRYMPI_FMT_HEX,
+ (const unsigned char *)DH1536_MODULUS_S, 0, NULL);
+ gcry_mpi_scan(&DH1536_GENERATOR, GCRYMPI_FMT_HEX,
+ (const unsigned char *)DH1536_GENERATOR_S, 0, NULL);
DH1536_MODULUS_MINUS_2 = gcry_mpi_new(DH1536_MOD_LEN_BITS);
gcry_mpi_sub_ui(DH1536_MODULUS_MINUS_2, DH1536_MODULUS, 2);
}
@@ -90,7 +94,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;
@@ -135,7 +139,7 @@ gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp,
}
/* Calculate the shared secret MPI */
- gab = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gab = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
gcry_mpi_powm(gab, y, kp->priv, DH1536_MODULUS);
/* Output it in the right format */
@@ -200,6 +204,11 @@ gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp,
err = gcry_md_setkey(sess->rcvmac, sess->rcvmackey, 20);
if (err) goto err;
+ /* Calculate the extra key (used if applications wish to extract a
+ * symmetric key for transferring files, or something like that) */
+ gabdata[0] = 0xff;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, sess->extrakey, gabdata, gablen+5);
+
gcry_free(gabdata);
gcry_free(hashdata);
return gcry_error(GPG_ERR_NO_ERROR);
@@ -248,7 +257,7 @@ gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
}
/* Calculate the shared secret MPI */
- s = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ s = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
/* Output it in the right format */
@@ -273,7 +282,7 @@ gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
}
sdata[0] = 0x00;
gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
- memcpy(sessionid, hashdata, 8);
+ memmove(sessionid, hashdata, 8);
*sessionidlenp = 8;
/* Calculate the encryption keys */
@@ -373,7 +382,7 @@ gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh,
}
/* Calculate the shared secret MPI */
- s = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ s = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
/* Output it in the right format */
@@ -398,7 +407,7 @@ gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh,
}
sdata[0] = 0x00;
gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, sdata, slen+5);
- memcpy(sessionid, hashdata, 20);
+ memmove(sessionid, hashdata, 20);
*sessionidlenp = 20;
/* Which half should be bold? */
@@ -442,6 +451,7 @@ void otrl_dh_session_blank(DH_sesskeys *sess)
memset(sess->rcvmackey, 0, 20);
sess->sendmacused = 0;
sess->rcvmacused = 0;
+ memset(sess->extrakey, 0, OTRL_EXTRAKEY_BYTES);
}
/* Increment the top half of a counter block */
diff --git a/plugins/MirOTR/libotr/src/dh.h b/plugins/MirOTR/libotr/src/dh.h
index d68328f528..742c408042 100644
--- a/plugins/MirOTR/libotr/src/dh.h
+++ b/plugins/MirOTR/libotr/src/dh.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __DH_H__
@@ -33,6 +34,8 @@ typedef enum {
OTRL_SESSIONID_SECOND_HALF_BOLD
} OtrlSessionIdHalf;
+#define OTRL_EXTRAKEY_BYTES 32
+
typedef struct {
unsigned char sendctr[16];
unsigned char rcvctr[16];
@@ -44,6 +47,7 @@ typedef struct {
gcry_md_hd_t rcvmac;
unsigned char rcvmackey[20];
int rcvmacused;
+ unsigned char extrakey[OTRL_EXTRAKEY_BYTES];
} DH_sesskeys;
/*
@@ -70,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/plugins/MirOTR/libotr/src/instag.c b/plugins/MirOTR/libotr/src/instag.c
new file mode 100644
index 0000000000..6b829de449
--- /dev/null
+++ b/plugins/MirOTR/libotr/src/instag.c
@@ -0,0 +1,262 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
+ * <otr@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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)
+{
+
+ OtrlInsTag *p;
+ char storeline[1000];
+ size_t maxsize = sizeof(storeline);
+
+ if (!instf) return gcry_error(GPG_ERR_NO_ERROR);
+
+ while(fgets(storeline, maxsize, instf)) {
+ char *prevpos;
+ char *pos;
+ unsigned int instag = 0;
+
+ 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);
+ }
+
+ return result;
+}
+
+/* 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(GPG_ERR_NO_ERROR);
+
+ p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag));
+ p->accountname = strdup(accountname);
+ p->protocol = strdup(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;
+ /* This line should be ignored when read back in, since there are no
+ tabs. */
+ fprintf(instf, "# WARNING! You shouldn't copy this file to another"
+ " computer. It is unnecessary and can cause problems.\n");
+ 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/plugins/MirOTR/libotr/src/instag.h b/plugins/MirOTR/libotr/src/instag.h
new file mode 100644
index 0000000000..c8aabb324f
--- /dev/null
+++ b/plugins/MirOTR/libotr/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@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/plugins/MirOTR/libotr/src/mem.c b/plugins/MirOTR/libotr/src/mem.c
index fe32baadc4..29330ae757 100644
--- a/plugins/MirOTR/libotr/src/mem.c
+++ b/plugins/MirOTR/libotr/src/mem.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Memory allocation routines for libgcrypt. All of the session key
@@ -45,61 +47,61 @@
/* libotr headers */
#include "mem.h"
-static int header_size;
+static size_t header_size;
static void *otrl_mem_malloc(size_t n)
{
- void *p;
- size_t new_n = n;
- new_n += header_size;
+ void *p;
+ size_t new_n = n;
+ new_n += header_size;
- /* Check for overflow attack */
- if (new_n < n) return NULL;
- p = malloc(new_n);
- if (p == NULL) return NULL;
+ /* Check for overflow attack */
+ if (new_n < n) return NULL;
+ p = malloc(new_n);
+ if (p == NULL) return NULL;
- ((size_t *)p)[0] = new_n; /* Includes header size */
+ ((size_t *)p)[0] = new_n; /* Includes header size */
#ifdef OTRL_MEM_MAGIC
- ((size_t *)p)[1] = OTRL_MEM_MAGIC;
+ ((size_t *)p)[1] = OTRL_MEM_MAGIC;
#endif
- return (void *)((char *)p + header_size);
+ return (void *)((char *)p + header_size);
}
static int otrl_mem_is_secure(const void *p)
{
- return 1;
+ return 1;
}
static void otrl_mem_free(void *p)
{
- void *real_p = (void *)((char *)p - header_size);
- size_t n = ((size_t *)real_p)[0];
+ void *real_p = (void *)((char *)p - header_size);
+ size_t n = ((size_t *)real_p)[0];
#ifdef OTRL_MEM_MAGIC
- if (((size_t *)real_p)[1] != OTRL_MEM_MAGIC) {
+ if (((size_t *)real_p)[1] != OTRL_MEM_MAGIC) {
fprintf(stderr, "Illegal free!\n");
return;
- }
+ }
#endif
- /* Wipe the memory (in the same way the built-in deallocator in
- * libgcrypt would) */
- memset(real_p, 0xff, n);
- memset(real_p, 0xaa, n);
- memset(real_p, 0x55, n);
- memset(real_p, 0x00, n);
+ /* Wipe the memory (in the same way the built-in deallocator in
+ * libgcrypt would) */
+ memset(real_p, 0xff, n);
+ memset(real_p, 0xaa, n);
+ memset(real_p, 0x55, n);
+ memset(real_p, 0x00, n);
- free(real_p);
+ free(real_p);
}
static void *otrl_mem_realloc(void *p, size_t n)
{
- if (p == NULL) {
+ if (p == NULL) {
return otrl_mem_malloc(n);
- } else if (n == 0) {
+ } else if (n == 0) {
otrl_mem_free(p);
return NULL;
- } else {
+ } else {
void *real_p = (void *)((char *)p - header_size);
void *new_p;
size_t old_n = ((size_t *)real_p)[0];
@@ -114,50 +116,65 @@ static void *otrl_mem_realloc(void *p, size_t n)
#ifdef OTRL_MEM_MAGIC
if (magic != OTRL_MEM_MAGIC) {
- fprintf(stderr, "Illegal realloc!\n");
- return NULL;
+ fprintf(stderr, "Illegal realloc!\n");
+ return NULL;
}
#endif
if (new_n < old_n) {
- /* Overwrite the space we're about to stop using */
- void *p = (void *)((char *)real_p + new_n);
- size_t excess = old_n - new_n;
- memset(p, 0xff, excess);
- memset(p, 0xaa, excess);
- memset(p, 0x55, excess);
- memset(p, 0x00, excess);
-
- /* We don't actually need to realloc() */
- new_p = real_p;
+ /* Overwrite the space we're about to stop using */
+ void *p = (void *)((char *)real_p + new_n);
+ size_t excess = old_n - new_n;
+ memset(p, 0xff, excess);
+ memset(p, 0xaa, excess);
+ memset(p, 0x55, excess);
+ memset(p, 0x00, excess);
+
+ /* We don't actually need to realloc() */
+ new_p = real_p;
} else {
- new_p = realloc(real_p, new_n);
- if (new_p == NULL) return NULL;
+ new_p = realloc(real_p, new_n);
+ if (new_p == NULL) return NULL;
}
((size_t *)new_p)[0] = new_n; /* Includes header size */
return (void *)((char *)new_p + header_size);
- }
+ }
}
void otrl_mem_init(void)
{
- header_size = 8;
+ header_size = 8;
#ifdef OTRL_MEM_MAGIC
- if (header_size < 2*sizeof(size_t)) {
+ if (header_size < 2*sizeof(size_t)) {
header_size = 2*sizeof(size_t);
- }
+ }
#else
- if (header_size < sizeof(size_t)) {
+ if (header_size < sizeof(size_t)) {
header_size = sizeof(size_t);
- }
+ }
#endif
- gcry_set_allocation_handler(
- otrl_mem_malloc,
- otrl_mem_malloc,
- otrl_mem_is_secure,
- otrl_mem_realloc,
- otrl_mem_free
+ gcry_set_allocation_handler(
+ otrl_mem_malloc,
+ otrl_mem_malloc,
+ otrl_mem_is_secure,
+ otrl_mem_realloc,
+ otrl_mem_free
);
}
+
+/* Compare two memory blocks in time dependent on the length of the
+ * blocks, but not their contents. Returns 1 if they differ, 0 if they
+ * are the same. */
+int otrl_mem_differ(const unsigned char *buf1, const unsigned char *buf2,
+ size_t len)
+{
+ volatile unsigned char diff = 0;
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ diff |= (buf1[i] ^ buf2[i]);
+ }
+ return (diff != 0);
+}
diff --git a/plugins/MirOTR/libotr/src/mem.h b/plugins/MirOTR/libotr/src/mem.h
index 19e00cf5aa..2baf8f2e9a 100644
--- a/plugins/MirOTR/libotr/src/mem.h
+++ b/plugins/MirOTR/libotr/src/mem.h
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,12 +16,20 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __MEM_H__
#define __MEM_H__
+#include <stdlib.h>
+
void otrl_mem_init(void);
+/* Compare two memory blocks in time dependent on the length of the
+ * blocks, but not their contents. Returns 1 if they differ, 0 if they
+ * are the same. */
+int otrl_mem_differ(const unsigned char *buf1, const unsigned char *buf2,
+ size_t len);
+
#endif
diff --git a/plugins/MirOTR/libotr/src/message.c b/plugins/MirOTR/libotr/src/message.c
index b385c1f4f1..b13710cccd 100644
--- a/plugins/MirOTR/libotr/src/message.c
+++ b/plugins/MirOTR/libotr/src/message.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -27,10 +29,23 @@
/* libotr headers */
#include "privkey.h"
+#include "userstate.h"
#include "proto.h"
#include "auth.h"
#include "message.h"
#include "sm.h"
+#include "instag.h"
+
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+/* If OTRL_DEBUGGING is on, and the user types this string, the current
+ * context and its siblings will be dumped to stderr. */
+const char *OTRL_DEBUGGING_DEBUGSTR = "?OTR!";
+
+void otrl_context_all_dump(FILE *f, OtrlUserState us);
+void otrl_context_siblings_dump(FILE *f, const ConnContext *context);
+#endif
/* The API version */
extern unsigned int otrl_api_version;
@@ -42,10 +57,109 @@ extern unsigned int otrl_api_version;
* resending in response to a rekey? */
#define RESEND_INTERVAL 60
+/* How long should we wait for the last of the logged-in instances of
+ * our buddy to respond before marking our private key as a candidate
+ * for wiping (in seconds)? */
+#define MAX_AKE_WAIT_TIME 60
+
+/* How frequently should we check our ConnContexts for wipeable private
+ * keys (and wipe them) (in seconds)? */
+#define POLL_DEFAULT_INTERVAL 70
+
+/* Send a message to the network, fragmenting first if necessary.
+ * All messages to be sent to the network should go through this
+ * method immediately before they are sent, ie after encryption. */
+static gcry_error_t fragment_and_send(const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const char *message,
+ OtrlFragmentPolicy fragPolicy, char **returnFragment)
+{
+ int mms = 0;
+
+ if (message && ops->inject_message) {
+ int msglen;
+
+ if (ops->max_message_size) {
+ mms = ops->max_message_size(opdata, context);
+ }
+ msglen = strlen(message);
+
+ /* Don't incur overhead of fragmentation unless necessary */
+ if(mms != 0 && msglen > mms) {
+ char **fragments;
+ gcry_error_t err;
+ int i;
+ 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,
+ context, message);
+ if (err) {
+ return err;
+ }
+
+ /* Determine which fragments to send and which to return
+ * based on given Fragment Policy. If the first fragment
+ * should be returned instead of sent, store it. */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_FIRST) {
+ *returnFragment = strdup(fragments[0]);
+ } else {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, fragments[0]);
+ }
+ for (i=1; i<fragment_count-1; i++) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, fragments[i]);
+ }
+ /* If the last fragment should be stored instead of sent,
+ * store it */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_LAST) {
+ *returnFragment = strdup(fragments[fragment_count-1]);
+ } else {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username,
+ fragments[fragment_count-1]);
+ }
+ /* Now free all fragment memory */
+ otrl_proto_fragment_free(&fragments, fragment_count);
+
+ } else {
+ /* No fragmentation necessary */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, message);
+ } else {
+ /* Copy and return the entire given 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) {
+ context->our_instance = p_instag->instag;
+ } else {
+ context->our_instance = otrl_instag_get_new();
+ }
+}
+
/* Deallocate a message allocated by other otrl_message_* routines. */
void otrl_message_free(char *message)
{
- free(message);
+ free(message);
}
/* Handle a message about to be sent to the network. It is safe to pass
@@ -55,544 +169,654 @@ void otrl_message_free(char *message)
* pointer to the new ConnContext. You can use this to add
* application-specific information to the ConnContext using the
* "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_sending.
+ * can pass NULL for the last two arguments of otrl_message_sending.
*
* tlvs is a chain of OtrlTLVs to append to the private message. It is
* usually correct to just pass NULL here.
*
- * If this routine returns 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.
- *
- * If *messagep gets set by the call to something non-NULL, then you
- * should replace your message with the contents of *messagep, and
- * send that instead. Call otrl_message_free(*messagep) when you're
+ * 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
+ * tried to encrypt the message, but for some reason failed. DO NOT send the
+ * message in the clear in that case. If *messagep gets set by the call to
+ * something non-NULL, then you should replace your message with the contents
+ * 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.
+ *
+ * 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, const char *message, OtrlTLV *tlvs,
- char **messagep,
+ 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;
- char * msgtosend;
- gcry_error_t err;
- OtrlPolicy policy = OTRL_POLICY_DEFAULT;
- int context_added = 0;
-
+ 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;
+
+ if (messagep) {
*messagep = NULL;
+ }
+
+ err = gcry_error(GPG_ERR_NO_ERROR); /* Default to no error */
- if (!accountname || !protocol || !recipient || !message || !messagep)
- return gcry_error(GPG_ERR_NO_ERROR);
+ if (contextp) {
+ *contextp = NULL;
+ }
- /* See if we have a fingerprint for this user */
- context = otrl_context_find(us, recipient, accountname, protocol,
- 1, &context_added, add_appdata, data);
+ if (!accountname || !protocol || !recipient ||
+ !original_msg || !messagep) {
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto fragment;
+ }
- /* Update the context list if we added one */
- if (context_added && ops->update_context_list) {
+ /* See if we have a fingerprint for this user */
+ context = otrl_context_find(us, recipient, accountname, protocol,
+ 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);
- }
+ }
- /* Check the policy */
- if (ops->policy) {
- policy = ops->policy(opdata, context);
- }
+ /* 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;
+ }
- /* Should we go on at all? */
- if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
- return gcry_error(GPG_ERR_NO_ERROR);
+ /* Check the policy */
+ if (ops->policy) {
+ policy = ops->policy(opdata, context);
+ }
+
+ /* Should we go on at all? */
+ if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto fragment;
+ }
+
+#if OTRL_DEBUGGING
+ /* If the user typed the magic debug string, dump this context and
+ * its siblings. */
+ {
+ const char *debugtag = strstr(original_msg, OTRL_DEBUGGING_DEBUGSTR);
+
+ if (debugtag) {
+ const char *debugargs =
+ debugtag + strlen(OTRL_DEBUGGING_DEBUGSTR);
+ if (debugargs[0] == '!') { /* typed ?OTR!! */
+ otrl_context_all_dump(stderr, us);
+ } else { /* typed ?OTR! without extra command chars */
+ otrl_context_siblings_dump(stderr, context);
+ }
+
+ /* Don't actually send the message */
+ *messagep = strdup("");
+ if (!(*messagep)) {
+ err = gcry_error(GPG_ERR_ENOMEM);
+ }
+ goto fragment;
}
+ }
+#endif
- /* If this is an OTR Query message, don't encrypt it. */
- if (otrl_proto_message_type(message) == OTRL_MSGTYPE_QUERY) {
+ /* If this is an OTR Query message, don't encrypt it. */
+ 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;
- }
- return gcry_error(GPG_ERR_NO_ERROR);
+ *messagep = bettermsg;
}
+ context->otr_offer = OFFER_SENT;
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto fragment;
+ }
+
+ /* What is the current message disposition? */
+ switch(context->msgstate) {
- /* What is the current message disposition? */
- switch(context->msgstate) {
case OTRL_MSGSTATE_PLAINTEXT:
- if ((policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
+ if ((policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
/* We're trying to send an unencrypted message with a policy
* that disallows that. Don't do that, but try to start
* up OTR instead. */
- if ((!(ops->display_otr_message) ||
- ops->display_otr_message(opdata, accountname,
- protocol, recipient, "Attempting to start a "
- "private conversation...")) && ops->notify) {
- const char *format = "You attempted to send an "
- "unencrypted message to %s";
- char *primary = malloc(strlen(format) +
- strlen(recipient) - 1);
- if (primary) {
- sprintf(primary, format, recipient);
- ops->notify(opdata, OTRL_NOTIFY_WARNING, accountname,
- protocol, recipient, "OTR Policy Violation",
- primary,
- "Unencrypted messages to this recipient are "
- "not allowed. Attempting to start a private "
- "conversation.\n\nYour message will be "
- "retransmitted when the private conversation "
- "starts.");
- free(primary);
- }
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_ENCRYPTION_REQUIRED,
+ context, NULL, gcry_error(GPG_ERR_NO_ERROR));
}
- context->lastmessage = gcry_malloc_secure(strlen(message) + 1);
- if (context->lastmessage) {
- char *bettermsg = otrl_proto_default_query_msg(accountname,
- policy);
- strcpy(context->lastmessage, message);
- context->lastsent = time(NULL);
- context->may_retransmit = 2;
- if (bettermsg) {
+
+ context->context_priv->lastmessage =
+ 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_msg);
+ context->context_priv->lastsent = time(NULL);
+ otrl_context_update_recent_child(context, 1);
+ context->context_priv->may_retransmit = 2;
+ if (bettermsg) {
*messagep = bettermsg;
- } else {
- return gcry_error(GPG_ERR_ENOMEM);
- }
+ context->otr_offer = OFFER_SENT;
+ } else {
+ err = gcry_error(GPG_ERR_ENOMEM);
+ goto fragment;
+ }
}
- } else {
+ } else {
if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG) &&
context->otr_offer != OFFER_REJECTED) {
- /* 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(message);
- size_t basetaglen = strlen(OTRL_MESSAGE_TAG_BASE);
- size_t v1taglen = (policy & OTRL_POLICY_ALLOW_V1) ?
+ /* 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_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) ?
+ size_t v2taglen = (policy & OTRL_POLICY_ALLOW_V2) ?
strlen(OTRL_MESSAGE_TAG_V2) : 0;
- char *taggedmsg = malloc(msglen + basetaglen + v1taglen
- +v2taglen + 1);
- if (taggedmsg) {
- strcpy(taggedmsg, message);
+ size_t v3taglen = (policy & OTRL_POLICY_ALLOW_V3) ?
+ strlen(OTRL_MESSAGE_TAG_V3) : 0;
+ char *taggedmsg = malloc(msglen + basetaglen + v1taglen
+ + v2taglen + v3taglen + 1);
+ if (taggedmsg) {
+ strcpy(taggedmsg, original_msg);
strcpy(taggedmsg + msglen, OTRL_MESSAGE_TAG_BASE);
if (v1taglen) {
- strcpy(taggedmsg + msglen + basetaglen,
- OTRL_MESSAGE_TAG_V1);
+ strcpy(taggedmsg + msglen + basetaglen,
+ OTRL_MESSAGE_TAG_V1);
}
if (v2taglen) {
- strcpy(taggedmsg + msglen + basetaglen + v1taglen,
- OTRL_MESSAGE_TAG_V2);
- }
- *messagep = taggedmsg;
- if (context) {
- context->otr_offer = OFFER_SENT;
+ strcpy(taggedmsg + msglen + basetaglen + v1taglen,
+ OTRL_MESSAGE_TAG_V2);
}
+ if (v3taglen) {
+ strcpy(taggedmsg + msglen + basetaglen + v1taglen
+ + v2taglen, OTRL_MESSAGE_TAG_V3);
}
+ *messagep = taggedmsg;
+ context->otr_offer = OFFER_SENT;
+ }
}
- }
- break;
+ }
+ break;
case OTRL_MSGSTATE_ENCRYPTED:
- /* Create the new, encrypted message */
- err = otrl_proto_create_data(&msgtosend, context, message, tlvs,
- 0);
- if (!err) {
- context->lastsent = time(NULL);
+ /* convert the original message if necessary */
+ 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 */
+ 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 {
+ } else {
/* Uh, oh. Whatever we do, *don't* send the message in the
* clear. */
- *messagep = _strdup("?OTR Error: Error occurred encrypting "
- "message");
- if ((!(ops->display_otr_message) ||
- ops->display_otr_message(opdata, accountname,
- protocol, recipient, "An error occurred when "
- "encrypting your message. The message was not "
- "sent.")) && ops->notify) {
- ops->notify(opdata, OTRL_NOTIFY_ERROR,
- accountname, protocol, recipient,
- "Error encrypting message",
- "An error occurred when encrypting your message",
- "The message was not sent.");
- }
- if (!(*messagep)) {
- return gcry_error(GPG_ERR_ENOMEM);
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_ENCRYPTION_ERROR,
+ context, NULL, gcry_error(GPG_ERR_NO_ERROR));
}
+ if (ops->otr_error_message) {
+ err_msg = ops->otr_error_message(opdata, context,
+ OTRL_ERRCODE_ENCRYPTION_ERROR);
+ *messagep = malloc(strlen(OTR_ERROR_PREFIX) +
+ strlen(err_msg) + 1);
+ if (*messagep) {
+ strcpy(*messagep, OTR_ERROR_PREFIX);
+ strcat(*messagep, err_msg);
+ }
+ if (ops->otr_error_message_free) {
+ ops->otr_error_message_free(opdata, err_msg);
+ }
+ if (!(*messagep)) {
+ err = gcry_error(GPG_ERR_ENOMEM);
+ goto fragment;
+ }
}
- break;
+ }
+ break;
case OTRL_MSGSTATE_FINISHED:
- *messagep = _strdup("");
- if ((!(ops->display_otr_message) ||
- ops->display_otr_message(opdata, accountname,
- protocol, recipient, "Your message was not sent. "
- "Either end your private conversation, or restart "
- "it.")) && ops->notify) {
- const char *fmt = "%s has already closed his/her private "
- "connection to you";
- char *primary = malloc(strlen(fmt) + strlen(recipient) - 1);
- if (primary) {
- sprintf(primary, fmt, recipient);
- ops->notify(opdata, OTRL_NOTIFY_ERROR,
- accountname, protocol, recipient,
- "Private connection closed", primary,
- "Your message was not sent. Either close your "
- "private connection to him, or refresh it.");
- }
- }
- if (!(*messagep)) {
- return gcry_error(GPG_ERR_ENOMEM);
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata, OTRL_MSGEVENT_CONNECTION_ENDED,
+ context, NULL, gcry_error(GPG_ERR_NO_ERROR));
+ }
+ *messagep = strdup("");
+ if (!(*messagep)) {
+ err = gcry_error(GPG_ERR_ENOMEM);
+ goto fragment;
+ }
+ break;
+ }
+
+fragment:
+ if (fragPolicy == OTRL_FRAGMENT_SEND_SKIP ) {
+ /* Do not fragment/inject. Default behaviour of libotr3.2.0 */
+ return err;
+ } else {
+ /* Fragment and send according to policy */
+ if (!err && messagep && *messagep) {
+ if (context) {
+ char *rmessagep = NULL;
+ err = fragment_and_send(ops, opdata, context, *messagep,
+ fragPolicy, &rmessagep);
+ if (rmessagep) {
+ /* Free the current message pointer and return back the
+ * returned fragmented one. */
+ free(*messagep);
+ *messagep = rmessagep;
}
- break;
+ }
}
-
- return gcry_error(GPG_ERR_NO_ERROR);
+ return err;
+ }
}
/* If err == 0, send the last auth message for the given context to the
* appropriate user. Otherwise, display an appripriate error dialog.
* Return the value of err that was passed. */
static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
- void *opdata, gcry_error_t err, ConnContext *context)
+ void *opdata, gcry_error_t err, ConnContext *context,
+ OtrlUserState us)
{
- if (!err) {
+ if (!err) {
const char *msg = context->auth.lastauthmsg;
if (msg && *msg) {
- otrl_message_fragment_and_send(ops, opdata, context, msg, OTRL_FRAGMENT_SEND_ALL, NULL);
- /*if (ops->inject_message) {
- ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, msg);
- }*/
- }
- } else {
- const char *buf_format = "Error setting up private conversation: %s";
- const char *strerr;
- char *buf;
-
- switch(gcry_err_code(err)) {
- case GPG_ERR_INV_VALUE:
- strerr = "Malformed message received";
- break;
- default:
- strerr = gcry_strerror(err);
- break;
- }
- buf = malloc(strlen(buf_format) + strlen(strerr) - 1);
- if (buf) {
- sprintf(buf, buf_format, strerr);
- }
- if ((!(ops->display_otr_message) ||
- ops->display_otr_message(opdata, context->accountname,
- context->protocol, context->username, buf))
- && ops->notify) {
- ops->notify(opdata, OTRL_NOTIFY_ERROR, context->accountname,
- context->protocol, context->username, "OTR error",
- buf, NULL);
+ time_t now;
+ fragment_and_send(ops, opdata, context, msg,
+ OTRL_FRAGMENT_SEND_ALL, NULL);
+ now = time(NULL);
+ /* Update the "last sent" fields, unless this is a version 3
+ * message typing to update the master context (as happens
+ * when sending a v3 COMMIT message, for example). */
+ if (context != context->m_context ||
+ context->auth.protocol_version != 3) {
+ context->context_priv->lastsent = now;
+ otrl_context_update_recent_child(context, 1);
+ }
+
+ /* If this is a master context, and we're sending a v3 COMMIT
+ * message, update the commit_sent_time timestamp, so we can
+ * expire it. */
+ if (context == context->m_context &&
+ context->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY &&
+ context->auth.protocol_version == 3) {
+ context->auth.commit_sent_time = now;
+ /* If there's not already a timer running to clean up
+ * this private key, try to start one. */
+ if (us->timer_running == 0 && ops && ops->timer_control) {
+ ops->timer_control(opdata, POLL_DEFAULT_INTERVAL);
+ us->timer_running = 1;
+ }
+ }
}
- free(buf);
+ } else {
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata, OTRL_MSGEVENT_SETUP_ERROR,
+ context, NULL, err);
}
- return err;
+ }
+ return err;
}
typedef struct {
- int gone_encrypted;
- OtrlUserState us;
- const OtrlMessageAppOps *ops;
- void *opdata;
- ConnContext *context;
- int ignore_message;
- char **messagep;
+ int gone_encrypted;
+ OtrlUserState us;
+ const OtrlMessageAppOps *ops;
+ void *opdata;
+ ConnContext *context;
+ int ignore_message;
+ char **messagep;
} EncrData;
static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
{
- EncrData *edata = asdata;
- gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
- Fingerprint *found_print = NULL;
- int fprint_added = 0;
- OtrlMessageState oldstate = edata->context->msgstate;
- Fingerprint *oldprint = edata->context->active_fingerprint;
-
- /* See if we're talking to ourselves */
- if (!gcry_mpi_cmp(auth->their_pub, auth->our_dh.pub)) {
+ EncrData *edata = asdata;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ Fingerprint *found_print = NULL;
+ int fprint_added = 0;
+ OtrlMessageState oldstate = edata->context->msgstate;
+ Fingerprint *oldprint = edata->context->active_fingerprint;
+
+ /* See if we're talking to ourselves */
+ if (!gcry_mpi_cmp(auth->their_pub, auth->our_dh.pub)) {
/* Yes, we are. */
- if ((!(edata->ops->display_otr_message) ||
- edata->ops->display_otr_message(edata->opdata,
- edata->context->accountname, edata->context->protocol,
- edata->context->username,
- "We are receiving our own OTR messages. "
- "You are either trying to talk to yourself, "
- "or someone is reflecting your messages back "
- "at you.")) && edata->ops->notify) {
- edata->ops->notify(edata->opdata, OTRL_NOTIFY_ERROR,
- edata->context->accountname, edata->context->protocol,
- edata->context->username, "OTR Error",
- "We are receiving our own OTR messages.",
- "You are either trying to talk to yourself, "
- "or someone is reflecting your messages back "
- "at you.");
+ if (edata->ops->handle_msg_event) {
+ edata->ops->handle_msg_event(edata->opdata,
+ OTRL_MSGEVENT_MSG_REFLECTED, edata->context,
+ NULL, gcry_error(GPG_ERR_NO_ERROR));
}
edata->ignore_message = 1;
return gcry_error(GPG_ERR_NO_ERROR);
- }
+ }
- found_print = otrl_context_find_fingerprint(edata->context,
- edata->context->auth.their_fingerprint, 1, &fprint_added);
+ found_print = otrl_context_find_fingerprint(edata->context,
+ edata->context->auth.their_fingerprint, 1, &fprint_added);
- if (fprint_added) {
+ if (fprint_added) {
/* Inform the user of the new fingerprint */
if (edata->ops->new_fingerprint) {
- edata->ops->new_fingerprint(edata->opdata, edata->us,
- edata->context->accountname, edata->context->protocol,
- edata->context->username,
- edata->context->auth.their_fingerprint);
+ edata->ops->new_fingerprint(edata->opdata, edata->us,
+ edata->context->accountname, edata->context->protocol,
+ edata->context->username,
+ edata->context->auth.their_fingerprint);
}
/* Arrange that the new fingerprint be written to disk */
if (edata->ops->write_fingerprints) {
- edata->ops->write_fingerprints(edata->opdata);
- }
+ edata->ops->write_fingerprints(edata->opdata);
}
-
- /* 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->our_keyid - 1 == edata->context->auth.our_keyid &&
- !gcry_mpi_cmp(edata->context->our_old_dh_key.pub,
+ }
+
+ /* 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 &&
+ !gcry_mpi_cmp(edata->context->context_priv->our_old_dh_key.pub,
edata->context->auth.our_dh.pub) &&
- ((edata->context->their_keyid > 0 &&
- edata->context->their_keyid ==
- edata->context->auth.their_keyid &&
- !gcry_mpi_cmp(edata->context->their_y,
+ ((edata->context->context_priv->their_keyid > 0 &&
+ edata->context->context_priv->their_keyid ==
+ edata->context->auth.their_keyid &&
+ !gcry_mpi_cmp(edata->context->context_priv->their_y,
edata->context->auth.their_pub)) ||
- (edata->context->their_keyid > 1 &&
- edata->context->their_keyid - 1 ==
- edata->context->auth.their_keyid &&
- edata->context->their_old_y != NULL &&
- !gcry_mpi_cmp(edata->context->their_old_y,
+ (edata->context->context_priv->their_keyid > 1 &&
+ edata->context->context_priv->their_keyid - 1 ==
+ edata->context->auth.their_keyid &&
+ edata->context->context_priv->their_old_y != NULL &&
+ !gcry_mpi_cmp(edata->context->context_priv->their_old_y,
edata->context->auth.their_pub)))) {
/* This is just a refresh of the existing session. */
if (edata->ops->still_secure) {
- edata->ops->still_secure(edata->opdata, edata->context,
- edata->context->auth.initiated);
+ edata->ops->still_secure(edata->opdata, edata->context,
+ edata->context->auth.initiated);
}
edata->ignore_message = 1;
return gcry_error(GPG_ERR_NO_ERROR);
- }
-
- /* Copy the information from the auth into the context */
- memmove(edata->context->sessionid,
- edata->context->auth.secure_session_id, 20);
- edata->context->sessionid_len =
- edata->context->auth.secure_session_id_len;
- edata->context->sessionid_half =
- edata->context->auth.session_id_half;
- edata->context->protocol_version =
- edata->context->auth.protocol_version;
-
- edata->context->their_keyid = edata->context->auth.their_keyid;
- gcry_mpi_release(edata->context->their_y);
- gcry_mpi_release(edata->context->their_old_y);
- edata->context->their_y = gcry_mpi_copy(edata->context->auth.their_pub);
- edata->context->their_old_y = NULL;
-
- if (edata->context->our_keyid - 1 != edata->context->auth.our_keyid ||
- gcry_mpi_cmp(edata->context->our_old_dh_key.pub,
+ }
+
+ /* Copy the information from the auth into the context */
+ memmove(edata->context->sessionid,
+ edata->context->auth.secure_session_id, 20);
+ edata->context->sessionid_len =
+ edata->context->auth.secure_session_id_len;
+ edata->context->sessionid_half =
+ edata->context->auth.session_id_half;
+ edata->context->protocol_version =
+ edata->context->auth.protocol_version;
+
+ 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_old_y = NULL;
+
+ 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)) {
- otrl_dh_keypair_free(&(edata->context->our_dh_key));
- otrl_dh_keypair_free(&(edata->context->our_old_dh_key));
- otrl_dh_keypair_copy(&(edata->context->our_old_dh_key),
+ 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->our_old_dh_key.groupid,
- &(edata->context->our_dh_key));
- edata->context->our_keyid = edata->context->auth.our_keyid + 1;
- }
-
- /* Create the session keys from the DH keys */
- otrl_dh_session_free(&(edata->context->sesskeys[0][0]));
- err = otrl_dh_session(&(edata->context->sesskeys[0][0]),
- &(edata->context->our_dh_key), edata->context->their_y);
- if (err) return err;
- otrl_dh_session_free(&(edata->context->sesskeys[1][0]));
- err = otrl_dh_session(&(edata->context->sesskeys[1][0]),
- &(edata->context->our_old_dh_key), edata->context->their_y);
- if (err) return err;
-
- edata->context->generation++;
- edata->context->active_fingerprint = found_print;
- edata->context->msgstate = OTRL_MSGSTATE_ENCRYPTED;
-
- if (edata->ops->update_context_list) {
+ 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;
+ }
+
+ /* 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);
+ 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);
+ if (err) return err;
+
+ edata->context->context_priv->generation++;
+ edata->context->active_fingerprint = found_print;
+ edata->context->msgstate = OTRL_MSGSTATE_ENCRYPTED;
+
+ if (edata->ops->update_context_list) {
edata->ops->update_context_list(edata->opdata);
- }
- if (oldstate == OTRL_MSGSTATE_ENCRYPTED && oldprint == found_print) {
+ }
+ if (oldstate == OTRL_MSGSTATE_ENCRYPTED && oldprint == found_print) {
if (edata->ops->still_secure) {
- edata->ops->still_secure(edata->opdata, edata->context,
- edata->context->auth.initiated);
+ edata->ops->still_secure(edata->opdata, edata->context,
+ edata->context->auth.initiated);
}
- } else {
+ } else {
if (edata->ops->gone_secure) {
- edata->ops->gone_secure(edata->opdata, edata->context);
- }
+ edata->ops->gone_secure(edata->opdata, edata->context);
}
+ }
- edata->gone_encrypted = 1;
+ edata->gone_encrypted = 1;
- return gpg_error(GPG_ERR_NO_ERROR);
+ return gpg_error(GPG_ERR_NO_ERROR);
}
static void maybe_resend(EncrData *edata)
{
- gcry_error_t err;
- time_t now;
+ gcry_error_t err;
+ time_t now;
- if (!edata->gone_encrypted) return;
+ if (!edata->gone_encrypted) return;
- /* See if there's a message we sent recently that should be resent. */
- now = time(NULL);
- if (edata->context->lastmessage != NULL &&
- edata->context->may_retransmit &&
- edata->context->lastsent >= (now - RESEND_INTERVAL)) {
+ /* See if there's a message we sent recently that should be resent. */
+ now = time(NULL);
+ if (edata->context->context_priv->lastmessage != NULL &&
+ edata->context->context_priv->may_retransmit &&
+ edata->context->context_priv->lastsent >= (now - RESEND_INTERVAL)) {
char *resendmsg;
- int resending = (edata->context->may_retransmit == 1);
+ char *msg_to_send;
+ int resending = (edata->context->context_priv->may_retransmit == 1);
+
+ /* Initialize msg_to_send */
+ if (resending) {
+ const char *resent_prefix;
+ int used_ops_resentmp = 1;
+ resent_prefix = edata->ops->resent_msg_prefix ?
+ edata->ops->resent_msg_prefix(edata->opdata,
+ edata->context) : NULL;
+ if (!resent_prefix) {
+ resent_prefix = "[resent]"; /* Assign default prefix */
+ used_ops_resentmp = 0;
+ }
+ msg_to_send = malloc(
+ strlen(edata->context->context_priv->lastmessage) +
+ strlen(resent_prefix) + 2);
+ if (msg_to_send) {
+ strcpy(msg_to_send, resent_prefix);
+ strcat(msg_to_send, " ");
+ strcat(msg_to_send, edata->context->context_priv->lastmessage);
+ } else {
+ return; /* Out of memory; don't try to resend */
+ }
+ if (used_ops_resentmp) {
+ edata->ops->resent_msg_prefix_free(edata->opdata,
+ resent_prefix);
+ }
+ } else {
+ msg_to_send = edata->context->context_priv->lastmessage;
+ }
/* Re-encrypt the message with the new keys */
err = otrl_proto_create_data(&resendmsg,
- edata->context, edata->context->lastmessage, NULL, 0);
+ edata->context, msg_to_send, NULL, 0, NULL);
+ if (resending) {
+ free(msg_to_send);
+ }
if (!err) {
- const char *format = "<b>The last message "
- "to %s was resent.</b>";
- char *buf;
-
- /* Resend the message */
- otrl_message_fragment_and_send(edata->ops, edata->opdata, edata->context, resendmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
- free(resendmsg);
- edata->context->lastsent = now;
-
- if (!resending) {
- /* We're actually just sending it
- * for the first time. */
- edata->ignore_message = 1;
- } else {
- /* Let the user know we resent it */
- buf = malloc(strlen(format) +
- strlen(edata->context->username) - 1);
- if (buf) {
- sprintf(buf, format, edata->context->username);
- if (edata->ops->display_otr_message) {
- if (!edata->ops->display_otr_message(
- edata->opdata, edata->context->accountname,
- edata->context->protocol,
- edata->context->username, buf)) {
- edata->ignore_message = 1;
- }
- }
- if (edata->ignore_message != 1) {
- *(edata->messagep) = buf;
- edata->ignore_message = 0;
- } else {
- free(buf);
- }
- }
+ /* Resend the message */
+ fragment_and_send(edata->ops, edata->opdata, edata->context,
+ 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
+ * know we resent it */
+ if (edata->ops->handle_msg_event) {
+ edata->ops->handle_msg_event(edata->opdata,
+ OTRL_MSGEVENT_MSG_RESENT, edata->context,
+ NULL, gcry_error(GPG_ERR_NO_ERROR));
}
+ }
+ edata->ignore_message = 1;
}
- }
+ }
}
/* Set the trust level based on the result of the SMP */
static void set_smp_trust(const OtrlMessageAppOps *ops, void *opdata,
ConnContext *context, int trusted)
{
- otrl_context_set_trust(context->active_fingerprint, trusted ? "smp" : "");
+ otrl_context_set_trust(context->active_fingerprint, trusted ? "smp" : "");
- /* Write the new info to disk, redraw the ui, and redraw the
- * OTR buttons. */
- if (ops->write_fingerprints) {
+ /* Write the new info to disk, redraw the ui, and redraw the
+ * OTR buttons. */
+ if (ops->write_fingerprints) {
ops->write_fingerprints(opdata);
- }
+ }
}
static void init_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, ConnContext *context, const char *question,
const unsigned char *secret, size_t secretlen, int initiating)
{
- unsigned char *smpmsg = NULL;
- int smpmsglen;
- unsigned char combined_secret[SM_DIGEST_SIZE];
- gcry_error_t err;
- unsigned char our_fp[20];
- unsigned char *combined_buf;
- size_t combined_buf_len;
- OtrlTLV *sendtlv;
- char *sendsmp = NULL;
-
- if (!context || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) return;
-
- /*
- * Construct the combined secret as a SHA256 hash of:
- * Version byte (0x01), Initiator fingerprint (20 bytes),
- * responder fingerprint (20 bytes), secure session id, input secret
- */
- otrl_privkey_fingerprint_raw(us, our_fp, context->accountname,
- context->protocol);
-
- combined_buf_len = 41 + context->sessionid_len + secretlen;
- combined_buf = malloc(combined_buf_len);
- combined_buf[0] = 0x01;
- if (initiating) {
+ unsigned char *smpmsg = NULL;
+ int smpmsglen;
+ unsigned char combined_secret[SM_DIGEST_SIZE];
+ gcry_error_t err;
+ unsigned char our_fp[20];
+ unsigned char *combined_buf;
+ size_t combined_buf_len;
+ OtrlTLV *sendtlv;
+ char *sendsmp = NULL;
+
+ if (!context || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) return;
+
+ /*
+ * Construct the combined secret as a SHA256 hash of:
+ * Version byte (0x01), Initiator fingerprint (20 bytes),
+ * responder fingerprint (20 bytes), secure session id, input secret
+ */
+ otrl_privkey_fingerprint_raw(us, our_fp, context->accountname,
+ context->protocol);
+
+ combined_buf_len = 41 + context->sessionid_len + secretlen;
+ combined_buf = malloc(combined_buf_len);
+ combined_buf[0] = 0x01;
+ if (initiating) {
memmove(combined_buf + 1, our_fp, 20);
memmove(combined_buf + 21,
context->active_fingerprint->fingerprint, 20);
- } else {
+ } else {
memmove(combined_buf + 1,
context->active_fingerprint->fingerprint, 20);
memmove(combined_buf + 21, our_fp, 20);
- }
- memmove(combined_buf + 41, context->sessionid,
- context->sessionid_len);
- memmove(combined_buf + 41 + context->sessionid_len,
- secret, secretlen);
- gcry_md_hash_buffer(SM_HASH_ALGORITHM, combined_secret, combined_buf,
- combined_buf_len);
- free(combined_buf);
-
- if (initiating) {
+ }
+ memmove(combined_buf + 41, context->sessionid,
+ context->sessionid_len);
+ memmove(combined_buf + 41 + context->sessionid_len,
+ secret, secretlen);
+ gcry_md_hash_buffer(SM_HASH_ALGORITHM, combined_secret, combined_buf,
+ combined_buf_len);
+ free(combined_buf);
+
+ if (initiating) {
otrl_sm_step1(context->smstate, combined_secret, SM_DIGEST_SIZE,
&smpmsg, &smpmsglen);
- } else {
+ } else {
otrl_sm_step2b(context->smstate, combined_secret, SM_DIGEST_SIZE,
&smpmsg, &smpmsglen);
- }
+ }
- /* If we've got a question, attach it to the smpmsg */
- if (question != NULL) {
+ /* If we've got a question, attach it to the smpmsg */
+ if (question != NULL) {
size_t qlen = strlen(question);
unsigned char *qsmpmsg = malloc(qlen + 1 + smpmsglen);
if (!qsmpmsg) {
- free(smpmsg);
- return;
+ free(smpmsg);
+ return;
}
strcpy((char *)qsmpmsg, question);
memmove(qsmpmsg + qlen + 1, smpmsg, smpmsglen);
free(smpmsg);
smpmsg = qsmpmsg;
smpmsglen += qlen + 1;
- }
-
- /* Send msg with next smp msg content */
- sendtlv = otrl_tlv_new(initiating ?
- (question != NULL ? OTRL_TLV_SMP1Q : OTRL_TLV_SMP1)
- : OTRL_TLV_SMP2,
- smpmsglen, smpmsg);
- err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
- if (!err) {
- /* Send it, and set the next expected message to the
+ }
+
+ /* Send msg with next smp msg content */
+ sendtlv = otrl_tlv_new(initiating ?
+ (question != NULL ? OTRL_TLV_SMP1Q : OTRL_TLV_SMP1)
+ : OTRL_TLV_SMP2,
+ smpmsglen, smpmsg);
+ err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
+ if (!err) {
+ /* Send it, and set the next expected message to the
* logical response */
- err = otrl_message_fragment_and_send(ops, opdata, context,
+ err = fragment_and_send(ops, opdata, context,
sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
- context->smstate->nextExpected =
+ context->smstate->nextExpected =
initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
- }
- free(sendsmp);
- otrl_tlv_free(sendtlv);
- free(smpmsg);
+ }
+ free(sendsmp);
+ otrl_tlv_free(sendtlv);
+ free(smpmsg);
}
/* Initiate the Socialist Millionaires' Protocol */
@@ -600,7 +824,7 @@ void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, ConnContext *context, const unsigned char *secret,
size_t secretlen)
{
- init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 1);
+ init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 1);
}
/* Initiate the Socialist Millionaires' Protocol and send a prompt
@@ -609,7 +833,7 @@ void otrl_message_initiate_smp_q(OtrlUserState us,
const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
const char *question, const unsigned char *secret, size_t secretlen)
{
- init_respond_smp(us, ops, opdata, context, question, secret, secretlen, 1);
+ init_respond_smp(us, ops, opdata, context, question, secret, secretlen, 1);
}
/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
@@ -617,7 +841,7 @@ void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, ConnContext *context, const unsigned char *secret,
size_t secretlen)
{
- init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 0);
+ init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 0);
}
/* Abort the SMP. Called when an unexpected SMP message breaks the
@@ -625,25 +849,55 @@ void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, ConnContext *context)
{
- OtrlTLV *sendtlv = otrl_tlv_new(OTRL_TLV_SMP_ABORT, 0,
- (const unsigned char *)"");
- char *sendsmp = NULL;
- gcry_error_t err;
-
- context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ OtrlTLV *sendtlv = otrl_tlv_new(OTRL_TLV_SMP_ABORT, 0,
+ (const unsigned char *)"");
+ char *sendsmp = NULL;
+ gcry_error_t err;
- err = otrl_proto_create_data(&sendsmp,
- context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
- if (!err) {
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+
+ err = otrl_proto_create_data(&sendsmp,
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
+ if (!err) {
/* Send the abort signal so our buddy knows we've stopped */
- err = otrl_message_fragment_and_send(ops, opdata, context,
+ err = fragment_and_send(ops, opdata, context,
sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+ }
+ free(sendsmp);
+ 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(GPG_ERR_NO_ERROR));
+ }
+
+ 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);
+ }
}
- free(sendsmp);
- otrl_tlv_free(sendtlv);
+ }
}
+
/* 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.
@@ -651,7 +905,13 @@ void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
* a pointer to the new ConnContext. You can use this to add
* application-specific information to the ConnContext using the
* "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.
+ * can pass NULL for the last two arguments of otrl_message_receiving.
+ *
+ * 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
@@ -671,776 +931,1129 @@ 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,
+ OtrlTLV **tlvsp, ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data)
{
- ConnContext *context;
- OtrlMessageType msgtype;
- int context_added = 0;
- OtrlMessageState msgstate;
- OtrlPolicy policy = OTRL_POLICY_DEFAULT;
- int fragment_assembled = 0;
- char *unfragmessage = NULL;
- EncrData edata;
-
- if (!accountname || !protocol || !sender || !message || !newmessagep)
- 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);
-
- /* Update the context list if we added one */
- if (context_added && ops->update_context_list) {
+ ConnContext *context, *m_context, *best_context;
+ OtrlMessageType msgtype;
+ int context_added = 0;
+ OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+ char *unfragmessage = NULL, *otrtag = NULL;
+ EncrData edata;
+ otrl_instag_t our_instance = 0, their_instance = 0;
+ int version;
+ gcry_error_t err;
+
+ if (!accountname || !protocol || !sender || !message || !newmessagep)
+ return 0;
+
+ *newmessagep = NULL;
+ if (tlvsp) *tlvsp = NULL;
+
+ 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);
- }
+ }
- /* Check the policy */
- if (ops->policy) {
- policy = ops->policy(opdata, context);
- }
+ 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);
+ }
- /* Should we go on at all? */
- if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
- return 0;
- }
- /* See if we have a fragment */
- switch(otrl_proto_fragment_accumulate(&unfragmessage, context, message)) {
- case OTRL_FRAGMENT_UNFRAGMENTED:
+ /* Check the policy */
+ if (ops->policy) {
+ policy = ops->policy(opdata, context);
+ }
+
+ /* Should we go on at all? */
+ if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
+ return 0;
+ }
+
+ otrtag = strstr(message, "?OTR");
+ if (otrtag) {
+ /* See if we have a V3 fragment. The '4' in the next line is
+ * strlen("?OTR"). otrtag[4] is the character immediately after
+ * the "?OTR", and is guaranteed to exist, because in the worst
+ * case, it is the NUL terminating 'message'. */
+ if (otrtag[4] == '|') {
+ /* 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(GPG_ERR_NO_ERROR));
+ }
+ 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:
+ case OTRL_FRAGMENT_INCOMPLETE:
/* We've accumulated this fragment, but we don't have a
* complete message yet */
return 1;
- case OTRL_FRAGMENT_COMPLETE:
+ 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;
+ /* 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);
+ version = otrl_proto_message_version(message);
- /* See if they responded to our OTR offer */
- if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG)) {
+ /* See if they responded to our OTR offer */
+ if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG)) {
if (msgtype != OTRL_MSGTYPE_NOTOTR) {
- context->otr_offer = OFFER_ACCEPTED;
+ context->otr_offer = OFFER_ACCEPTED;
} else if (context->otr_offer == OFFER_SENT) {
- context->otr_offer = OFFER_REJECTED;
+ context->otr_offer = OFFER_REJECTED;
}
+ }
+
+ /* 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))) {
+ edata.ignore_message = 1;
+ goto end;
+ }
+ /* Check the to and from instance tags */
+ if (version == 3) {
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ if (otrtag) {
+ err = otrl_proto_instance(otrtag, &their_instance, &our_instance);
+ }
+ if (!err) {
+ if ((msgtype == OTRL_MSGTYPE_DH_COMMIT && our_instance &&
+ context->our_instance != our_instance) ||
+ (msgtype != OTRL_MSGTYPE_DH_COMMIT &&
+ 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(GPG_ERR_NO_ERROR));
+ }
+ /* ignore message intended for a different instance */
+ edata.ignore_message = 1;
+ goto end;
+ }
+
+ if (their_instance >= OTRL_MIN_VALID_INSTAG) {
+ context = otrl_context_find(us, sender, accountname,
+ protocol, their_instance, 1, &context_added,
+ add_appdata, data);
+ }
}
- edata.gone_encrypted = 0;
- edata.us = us;
- edata.context = context;
- edata.ops = ops;
- edata.opdata = opdata;
- edata.ignore_message = -1;
- edata.messagep = newmessagep;
+ if (err || their_instance < OTRL_MIN_VALID_INSTAG) {
+ message_malformed(ops, opdata, context);
+ edata.ignore_message = 1;
+ goto end;
+ }
+
+ 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;
+ context->msgstate = m_context->msgstate;
+
+ if (m_context->context_priv->may_retransmit) {
+ gcry_free(context->context_priv->lastmessage);
+ context->context_priv->lastmessage = m_context->context_priv->lastmessage;
+ m_context->context_priv->lastmessage = NULL;
+ context->context_priv->may_retransmit = m_context->context_priv->may_retransmit;
+ m_context->context_priv->may_retransmit = 0;
+ }
+
+ if (msgtype == OTRL_MSGTYPE_DH_KEY) {
+ otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
+ } else if (msgtype != OTRL_MSGTYPE_DH_COMMIT) {
+ edata.ignore_message = 1;
+ goto end;
+ }
+
+ /* Update the context list */
+ if (ops->update_context_list) {
+ ops->update_context_list(opdata);
+ }
+ } else if (m_context != context) {
+ /* 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));
+ }
+ }
+ }
- switch(msgtype) {
+ 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;
+ edata.ops = ops;
+ edata.opdata = opdata;
+ edata.ignore_message = -1;
+ edata.messagep = newmessagep;
+
+ switch(msgtype) {
unsigned int bestversion;
const char *startwhite, *endwhite;
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. */
- if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
- our_dh = &(context->our_old_dh_key);
- our_keyid = context->our_keyid - 1;
- } else {
+ /* 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;
- }
-
- /* Find the best version of OTR that we both speak */
- switch(otrl_proto_query_bestversion(message, policy)) {
+ }
+
+ /* 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, us);
+ break;
case 2:
- err = otrl_auth_start_v2(&(context->auth));
- send_or_error_auth(ops, opdata, err, context);
- break;
+ err = otrl_auth_start_v23(&(context->auth), 2);
+ send_or_error_auth(ops, opdata, err, context, us);
+ break;
case 1:
- /* Get our private key */
- privkey = otrl_privkey_find(us, context->accountname,
- context->protocol);
- if (privkey == NULL) {
+ /* 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);
- }
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
}
- if (privkey) {
+ }
+ if (privkey) {
err = otrl_auth_start_v1(&(context->auth), our_dh,
our_keyid, privkey);
- send_or_error_auth(ops, opdata, err, context);
- }
- break;
+ send_or_error_auth(ops, opdata, err, context, us);
+ }
+ break;
default:
- /* Just ignore this message */
- break;
- }
- /* Don't display the Query message to the user. */
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ /* Just ignore this message */
+ break;
+ }
+ /* Don't display the Query message to the user. */
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ 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, us);
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ 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);
- }
- }
- if (privkey) {
- err = otrl_auth_handle_key(&(context->auth), message,
- &haveauthmsg, privkey);
- if (err || haveauthmsg) {
- send_or_error_auth(ops, opdata, err, context);
- }
+ /* 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), otrtag,
+ &haveauthmsg, privkey);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context, us);
}
+ }
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ 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);
- }
- }
- 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);
- }
+ /* 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),
+ otrtag, &haveauthmsg, privkey, go_encrypted,
+ &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context, us);
+ maybe_resend(&edata);
}
+ }
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ 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);
- }
- }
-
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ err = otrl_auth_handle_signature(&(context->auth),
+ otrtag, &haveauthmsg, go_encrypted, &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context, us);
+ maybe_resend(&edata);
+ }
- 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->our_old_dh_key);
- our_keyid = context->our_keyid - 1;
- } else {
- our_dh = NULL;
- our_keyid = 0;
- }
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
- /* 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);
- }
+ case OTRL_MSGTYPE_V1_KEYEXCH:
+ /* 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);
}
+ }
+ 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, us);
+ maybe_resend(&edata);
}
-
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
case OTRL_MSGTYPE_DATA:
- switch(context->msgstate) {
+ switch(context->msgstate) {
gcry_error_t err;
OtrlTLV *tlvs, *tlv;
char *plaintext;
char *buf;
- const char *format;
- const char *displayaccountname;
+ const char *err_msg;
+ unsigned char *extrakey;
unsigned char flags;
NextExpectedSMP nextMsg;
case OTRL_MSGSTATE_PLAINTEXT:
case OTRL_MSGSTATE_FINISHED:
- /* See if we're supposed to ignore this message in
- * the event it's unreadable. */
- err = otrl_proto_data_read_flags(message, &flags);
- if ((flags & OTRL_MSGFLAGS_IGNORE_UNREADABLE)) {
+ /* See if we're supposed to ignore this message in
+ * the event it's unreadable. */
+ err = otrl_proto_data_read_flags(message, &flags);
+ if ((flags & OTRL_MSGFLAGS_IGNORE_UNREADABLE)) {
edata.ignore_message = 1;
break;
- }
+ }
- /* Don't use g_strdup_printf here, because someone
- * (not us) is going to free() the *newmessagep pointer,
- * not g_free() it. */
- format = "<b>The encrypted message received from %s is "
- "unreadable, as you are not currently communicating "
- "privately.</b>";
- buf = malloc(strlen(format) + strlen(context->username)
- - 1); /* Remove "%s", add username + '\0' */
- if (buf) {
- sprintf(buf, format, context->username);
- if (ops->display_otr_message) {
- if (!ops->display_otr_message(opdata, accountname,
- protocol, sender, buf)) {
- edata.ignore_message = 1;
- }
- }
- if (edata.ignore_message != 1) {
- *newmessagep = buf;
- edata.ignore_message = 0;
- } else {
- free(buf);
- }
- }
- format = "?OTR Error: You sent encrypted "
- "data to %s, who wasn't expecting it.";
- if (otrl_api_version >= 0x00030100 &&
- ops->account_name) {
- displayaccountname = ops->account_name(opdata,
- context->accountname, protocol);
- } else {
- displayaccountname = NULL;
- }
- buf = malloc(strlen(format) + strlen(displayaccountname ?
- displayaccountname : context->accountname)
- - 1);
- if (buf) {
- sprintf(buf, format, displayaccountname ?
- displayaccountname : context->accountname);
- if (ops->inject_message) {
- ops->inject_message(opdata, accountname, protocol,
- sender, buf);
- }
- free(buf);
- }
- if (displayaccountname && otrl_api_version >= 0x00030100 &&
- ops->account_name_free) {
- ops->account_name_free(opdata, displayaccountname);
- }
+ if(best_context && best_context != context &&
+ best_context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
- break;
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
+ m_context, NULL,
+ gcry_error(GPG_ERR_NO_ERROR));
+ }
+ } else if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE,
+ context, NULL,
+ gcry_error(GPG_ERR_NO_ERROR));
+ }
+ edata.ignore_message = 1;
+
+ /* 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:
- err = otrl_proto_accept_data(&plaintext, &tlvs, context,
- message, &flags);
- if (err) {
+ extrakey = gcry_malloc_secure(OTRL_EXTRAKEY_BYTES);
+ err = otrl_proto_accept_data(&plaintext, &tlvs, context,
+ message, &flags, extrakey);
+ if (err) {
int is_conflict =
(gpg_err_code(err) == GPG_ERR_CONFLICT);
if ((flags & OTRL_MSGFLAGS_IGNORE_UNREADABLE)) {
- edata.ignore_message = 1;
- break;
+ edata.ignore_message = 1;
+ break;
}
- format = is_conflict ? "We received an unreadable "
- "encrypted message from %s." :
- "We received a malformed data message from %s.";
- buf = malloc(strlen(format) + strlen(sender) - 1);
- if (buf) {
- sprintf(buf, format, sender);
- if ((!(ops->display_otr_message) ||
- ops->display_otr_message(opdata,
- accountname, protocol, sender,
- buf)) && ops->notify) {
- ops->notify(opdata, OTRL_NOTIFY_ERROR,
- accountname, protocol, sender,
- "OTR Error", buf, NULL);
- }
- free(buf);
+ if (is_conflict) {
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_UNREADABLE,
+ context, NULL,
+ gcry_error(GPG_ERR_NO_ERROR));
+ }
+ } else {
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_MALFORMED,
+ context, NULL,
+ gcry_error(GPG_ERR_NO_ERROR));
+ }
}
- if (ops->inject_message) {
- ops->inject_message(opdata, accountname, protocol,
- sender, is_conflict ? "?OTR Error: "
- "You transmitted an unreadable "
- "encrypted message." :
- "?OTR Error: You transmitted "
- "a malformed data message");
+ if (ops->inject_message && ops->otr_error_message) {
+ err_msg = ops->otr_error_message(opdata,
+ context,
+ is_conflict ?
+ OTRL_ERRCODE_MSG_UNREADABLE :
+ OTRL_ERRCODE_MSG_MALFORMED);
+ if (err_msg) {
+ 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);
+ }
}
edata.ignore_message = 1;
break;
- }
+ }
- /* If the other side told us he's disconnected his
- * private connection, make a note of that so we
- * don't try sending anything else to him. */
- if (otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED)) {
+ /* If the other side told us he's disconnected his
+ * private connection, make a note of that so we
+ * don't try sending anything else to him. */
+ if (otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED)) {
otrl_context_force_finished(context);
+ }
+
+ /* If the other side told us to use the current
+ * extra symmetric key, let the application know. */
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SYMKEY);
+ if (tlv && otrl_api_version >= 0x040000) {
+ if (ops->received_symkey && tlv->len >= 4) {
+ unsigned char *bufp = tlv->data;
+ unsigned int use =
+ (bufp[0] << 24) | (bufp[1] << 16) |
+ (bufp[2] << 8) | bufp[3];
+ ops->received_symkey(opdata, context, use,
+ bufp+4, tlv->len - 4, extrakey);
}
-
- /* If TLVs contain SMP data, process it */
- nextMsg = context->smstate->nextExpected;
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
- if (tlv && nextMsg == OTRL_SMP_EXPECT1) {
- /* We can only do the verification half now.
- * We must wait for the secret to be entered
- * to continue. */
- char *question = (char *)tlv->data;
- char *qend = memchr(question, '\0', tlv->len - 1);
- size_t qlen = qend ? (qend - question + 1) : tlv->len;
- otrl_sm_step2a(context->smstate, tlv->data + qlen,
- tlv->len - qlen, 1);
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
- if (tlv && nextMsg == OTRL_SMP_EXPECT1) {
- /* We can only do the verification half now.
- * We must wait for the secret to be entered
- * to continue. */
- otrl_sm_step2a(context->smstate, tlv->data, tlv->len,
- 0);
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
- if (tlv && nextMsg == OTRL_SMP_EXPECT2) {
- unsigned char* nextmsg;
- int nextmsglen;
- OtrlTLV *sendtlv;
- char *sendsmp;
- otrl_sm_step3(context->smstate, tlv->data, tlv->len,
- &nextmsg, &nextmsglen);
-
- if (context->smstate->sm_prog_state !=
- OTRL_SMP_PROG_CHEATED) {
+ }
+ gcry_free(extrakey);
+ extrakey = NULL;
+
+ /* If TLVs contain SMP data, process it */
+ nextMsg = context->smstate->nextExpected;
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
+ if (tlv) {
+ if (nextMsg == OTRL_SMP_EXPECT1 && tlv->len > 0) {
+ /* We can only do the verification half now.
+ * We must wait for the secret to be entered
+ * to continue. */
+ char *question = (char *)tlv->data;
+ char *qend = memchr(question, '\0', tlv->len - 1);
+ size_t qlen = qend ? (qend - question + 1) :
+ tlv->len;
+ otrl_sm_step2a(context->smstate, tlv->data + qlen,
+ tlv->len - qlen, 1);
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ASK_FOR_ANSWER,
+ context, 25, question);
+ }
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_CHEATED, context,
+ 0, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ context->smstate->sm_prog_state =
+ OTRL_SMP_PROG_OK;
+ }
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ERROR, context,
+ 0, NULL);
+ }
+ }
+ }
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+ if (tlv) {
+ if (nextMsg == OTRL_SMP_EXPECT1) {
+ /* We can only do the verification half now.
+ * We must wait for the secret to be entered
+ * to continue. */
+ otrl_sm_step2a(context->smstate, tlv->data,
+ tlv->len, 0);
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ASK_FOR_SECRET,
+ context, 25, NULL);
+ }
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_CHEATED,
+ context, 0, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ context->smstate->sm_prog_state =
+ OTRL_SMP_PROG_OK;
+ }
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ERROR, context,
+ 0, NULL);
+ }
+ }
+ }
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+ if (tlv) {
+ if (nextMsg == OTRL_SMP_EXPECT2) {
+ unsigned char* nextmsg;
+ int nextmsglen;
+ OtrlTLV *sendtlv;
+ char *sendsmp;
+ otrl_sm_step3(context->smstate, tlv->data,
+ tlv->len, &nextmsg, &nextmsglen);
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
/* Send msg with next smp msg content */
- sendtlv = otrl_tlv_new(OTRL_TLV_SMP3, nextmsglen,
- nextmsg);
+ sendtlv = otrl_tlv_new(OTRL_TLV_SMP3,
+ nextmsglen, nextmsg);
err = otrl_proto_create_data(&sendsmp,
context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE,
+ NULL);
if (!err) {
- err = otrl_message_fragment_and_send(ops,
+ err = fragment_and_send(ops,
opdata, context, sendsmp,
OTRL_FRAGMENT_SEND_ALL, NULL);
}
free(sendsmp);
otrl_tlv_free(sendtlv);
+
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_IN_PROGRESS,
+ context, 60, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT4;
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_CHEATED,
+ context, 0, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ context->smstate->sm_prog_state =
+ OTRL_SMP_PROG_OK;
+ }
+ free(nextmsg);
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ERROR, context,
+ 0, NULL);
+ }
}
- free(nextmsg);
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
- if (tlv && nextMsg == OTRL_SMP_EXPECT3) {
- unsigned char* nextmsg;
- int nextmsglen;
- OtrlTLV *sendtlv;
- char *sendsmp;
- err = otrl_sm_step4(context->smstate, tlv->data,
- tlv->len, &nextmsg, &nextmsglen);
- /* Set trust level based on result */
- if (context->smstate->received_question == 0) {
+ }
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+ if (tlv) {
+ if (nextMsg == OTRL_SMP_EXPECT3) {
+ unsigned char* nextmsg;
+ int nextmsglen;
+ OtrlTLV *sendtlv;
+ char *sendsmp;
+ err = otrl_sm_step4(context->smstate, tlv->data,
+ tlv->len, &nextmsg, &nextmsglen);
+ /* 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)));
- }
-
- if (context->smstate->sm_prog_state !=
- OTRL_SMP_PROG_CHEATED) {
+ }
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
/* Send msg with next smp msg content */
- sendtlv = otrl_tlv_new(OTRL_TLV_SMP4, nextmsglen,
- nextmsg);
+ sendtlv = otrl_tlv_new(OTRL_TLV_SMP4,
+ nextmsglen, nextmsg);
err = otrl_proto_create_data(&sendsmp,
context, "", sendtlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE,
+ NULL);
if (!err) {
- err = otrl_message_fragment_and_send(ops,
+ err = fragment_and_send(ops,
opdata, context, sendsmp,
OTRL_FRAGMENT_SEND_ALL, NULL);
}
free(sendsmp);
otrl_tlv_free(sendtlv);
+
+ if (ops->handle_smp_event) {
+ OtrlSMPEvent succorfail =
+ context->smstate->sm_prog_state ==
+ OTRL_SMP_PROG_SUCCEEDED ?
+ OTRL_SMPEVENT_SUCCESS :
+ OTRL_SMPEVENT_FAILURE;
+ ops->handle_smp_event(opdata, succorfail,
+ context, 100, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_CHEATED,
+ context, 0, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ context->smstate->sm_prog_state =
+ OTRL_SMP_PROG_OK;
+ }
+ free(nextmsg);
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ERROR, context,
+ 0, NULL);
+ }
+ }
+ }
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+ if (tlv) {
+ if (nextMsg == OTRL_SMP_EXPECT4) {
+ err = otrl_sm_step5(context->smstate, tlv->data,
+ tlv->len);
+ /* Set trust level based on result */
+ set_smp_trust(ops, opdata, context,
+ (err == gcry_error(GPG_ERR_NO_ERROR)));
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
+ if (ops->handle_smp_event) {
+ OtrlSMPEvent succorfail =
+ context->smstate->sm_prog_state ==
+ OTRL_SMP_PROG_SUCCEEDED ?
+ OTRL_SMPEVENT_SUCCESS :
+ OTRL_SMPEVENT_FAILURE;
+ ops->handle_smp_event(opdata, succorfail,
+ context, 100, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_CHEATED,
+ context, 0, NULL);
+ }
+ context->smstate->nextExpected =
+ OTRL_SMP_EXPECT1;
+ context->smstate->sm_prog_state =
+ OTRL_SMP_PROG_OK;
+ }
+ } else {
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata,
+ OTRL_SMPEVENT_ERROR, context,
+ 0, NULL);
+ }
}
- free(nextmsg);
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
- if (tlv && nextMsg == OTRL_SMP_EXPECT4) {
- err = otrl_sm_step5(context->smstate, tlv->data,
- tlv->len);
- /* Set trust level based on result */
- set_smp_trust(ops, opdata, context,
- (err == gcry_error(GPG_ERR_NO_ERROR)));
- }
- tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
- if (tlv) {
+ }
+
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+ if (tlv) {
context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ if (ops->handle_smp_event) {
+ ops->handle_smp_event(opdata, OTRL_SMPEVENT_ABORT,
+ context, 0, NULL);
}
- if (plaintext[0] == '\0') {
+ }
+
+ if (plaintext[0] == '\0') {
/* If it's a heartbeat (an empty message), don't
- * display it to the user, but log a debug message. */
- format = "Heartbeat received from %s.\n";
- buf = malloc(strlen(format) + strlen(sender) - 1);
- if (buf) {
- sprintf(buf, format, sender);
- if (ops->log_message) {
- ops->log_message(opdata, buf);
- }
- free(buf);
+ * display it to the user, but signal an event. */
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD,
+ context, NULL,
+ gcry_error(GPG_ERR_NO_ERROR));
}
edata.ignore_message = 1;
- } else if (edata.ignore_message == 0 &&
- context->their_keyid > 0) {
+ } else if (edata.ignore_message != 1 &&
+ context->context_priv->their_keyid > 0) {
/* If it's *not* a heartbeat, and we haven't
* sent anything in a while, also send a
* heartbeat. */
time_t now = time(NULL);
- if (context->lastsent < (now - HEARTBEAT_INTERVAL)) {
- char *heartbeat;
-
- /* Create the heartbeat message */
- err = otrl_proto_create_data(&heartbeat,
- context, "", NULL,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
- if (!err) {
- /* Send it, and log a debug message */
+ if (context->context_priv->lastsent <
+ (now - HEARTBEAT_INTERVAL)) {
+ char *heartbeat;
+
+ /* Create the heartbeat message */
+ err = otrl_proto_create_data(&heartbeat,
+ context, "", NULL,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE,
+ NULL);
+ if (!err) {
+ /* Send it, and inject a debug message */
if (ops->inject_message) {
- ops->inject_message(opdata, accountname,
- protocol, sender, heartbeat);
+ ops->inject_message(opdata, accountname,
+ protocol, sender, heartbeat);
}
free(heartbeat);
- context->lastsent = now;
+ context->context_priv->lastsent = now;
+ otrl_context_update_recent_child(context, 1);
- /* Log a debug message */
- format = "Heartbeat sent to %s.\n";
- buf = malloc(strlen(format) + strlen(sender)
- - 1);
- if (buf) {
- sprintf(buf, format, sender);
- if (ops->log_message) {
- ops->log_message(opdata, buf);
- }
- free(buf);
+ /* 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(GPG_ERR_NO_ERROR));
}
- }
- }
+ }
}
+ }
- /* Return the TLVs even if ignore_message == 1 so
- * that we can attach TLVs to heartbeats. */
- if (tlvsp) {
+ /* Return the TLVs even if ignore_message == 1 so
+ * that we can attach TLVs to heartbeats. */
+ if (tlvsp) {
*tlvsp = tlvs;
- } else {
+ } else {
otrl_tlv_free(tlvs);
- }
+ }
+
+ if (edata.ignore_message != 1) {
+ char *converted_msg = NULL;
- if (edata.ignore_message != 1) {
*newmessagep = plaintext;
edata.ignore_message = 0;
- } else {
- free(plaintext);
+
+ /* 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);
+ }
+ }
}
- break;
- }
- break;
+ } else {
+ free(plaintext);
+ }
+ break;
+ }
+ break;
case OTRL_MSGTYPE_ERROR:
- if ((policy & OTRL_POLICY_ERROR_START_AKE)) {
+ if ((policy & OTRL_POLICY_ERROR_START_AKE)) {
char *msgtosend = otrl_proto_default_query_msg(
context->accountname, policy);
if (msgtosend && ops->inject_message) {
- ops->inject_message(opdata, context->accountname,
- context->protocol, context->username,
- msgtosend);
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username,
+ msgtosend);
}
free(msgtosend);
- }
+ }
- if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
/* Mark the last message we sent as eligible for
* retransmission */
- context->may_retransmit = 1;
- }
-
- /* In any event, display the error message, with the
- * display_otr_message callback, if possible */
- if (ops->display_otr_message) {
- const char *otrerror = strstr(message, "?OTR Error:");
- if (otrerror) {
- /* Skip the leading '?' */
- ++otrerror;
+ 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) {
+ just_err_msg = message;
} else {
- otrerror = message;
- }
- if (!ops->display_otr_message(opdata, accountname, protocol,
- sender, otrerror)) {
- edata.ignore_message = 1;
- }
+ just_err_msg += (strlen(OTR_ERROR_PREFIX));
+ if (*just_err_msg == ' ') {
+ /* Advance pointer to skip the space character */
+ just_err_msg++;
+ }
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR,
+ context, just_err_msg,
+ gcry_error(GPG_ERR_NO_ERROR));
+ edata.ignore_message = 1;
}
- break;
+ }
+ break;
case OTRL_MSGTYPE_TAGGEDPLAINTEXT:
- /* Strip the tag from the message */
- bestversion = otrl_proto_whitespace_bestversion(message,
- &startwhite, &endwhite, policy);
- if (startwhite && endwhite) {
+ /* Strip the tag from the message */
+ bestversion = otrl_proto_whitespace_bestversion(message,
+ &startwhite, &endwhite, policy);
+ if (startwhite && endwhite) {
size_t restlen = strlen(endwhite);
- char *strippedmsg = _strdup(message);
+ char *strippedmsg = strdup(message);
if (strippedmsg) {
- memmove(strippedmsg + (startwhite - message),
- strippedmsg + (endwhite - message), restlen+1);
- *newmessagep = strippedmsg;
- edata.ignore_message = 0;
- }
+ memmove(strippedmsg + (startwhite - message),
+ strippedmsg + (endwhite - message), restlen+1);
+ *newmessagep = strippedmsg;
+ edata.ignore_message = 0;
}
- if (bestversion && context->msgstate != OTRL_MSGSTATE_ENCRYPTED
- && (policy & OTRL_POLICY_WHITESPACE_START_AKE)) {
+ }
+ if (bestversion && context->msgstate != OTRL_MSGSTATE_ENCRYPTED
+ && (policy & OTRL_POLICY_WHITESPACE_START_AKE)) {
switch(bestversion) {
- case 2:
- err = otrl_auth_start_v2(&(context->auth));
- send_or_error_auth(ops, opdata, err, context);
+ case 3:
+ err = otrl_auth_start_v23(&(context->auth), 3);
+ send_or_error_auth(ops, opdata, err, context, us);
+ break;
+ case 2:
+ err = otrl_auth_start_v23(&(context->auth), 2);
+ send_or_error_auth(ops, opdata, err, context, us);
break;
- case 1:
+ case 1:
/* 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) {
+ /* 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_start_v1(&(context->auth), NULL, 0,
- privkey);
- send_or_error_auth(ops, opdata, err, context);
+ err = otrl_auth_start_v1(&(context->auth), NULL, 0,
+ privkey);
+ send_or_error_auth(ops, opdata, err, context, us);
}
break;
- default:
+ default:
/* Don't start the AKE */
break;
}
- }
+ }
- /* FALLTHROUGH */
+ /* FALLTHROUGH */
case OTRL_MSGTYPE_NOTOTR:
- if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
- (policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
+ if (best_context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
+ (policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
/* Not fine. Let the user know. */
-
- /* Don't use g_strdup_printf here, because someone
- * (not us) is going to free() the *message pointer,
- * not g_free() it. */
const char *plainmsg = (*newmessagep) ? *newmessagep : message;
- const char *format = "The following message received "
- "from %s was not encrypted: [</b>%s<b>]";
- char *buf = malloc(strlen(format) + strlen(context->username)
- + strlen(plainmsg) - 3);
- /* Remove "%s%s", add username + message + '\0' */
- if (buf) {
- sprintf(buf, format, context->username, plainmsg);
- if (ops->display_otr_message) {
- if (!ops->display_otr_message(opdata, accountname,
- protocol, sender, buf)) {
- free(*newmessagep);
- *newmessagep = NULL;
- edata.ignore_message = 1;
- }
- }
- if (edata.ignore_message != 1) {
- free(*newmessagep);
- *newmessagep = buf;
- edata.ignore_message = 0;
- } else {
- free(buf);
- }
- }
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED,
+ context, plainmsg, gcry_error(GPG_ERR_NO_ERROR));
+ free(*newmessagep);
+ *newmessagep = NULL;
+ edata.ignore_message = 1;
}
- break;
+ }
+ break;
case OTRL_MSGTYPE_UNKNOWN:
- /* We received an OTR message we didn't recognize. Ignore
- * it, but make a log entry. */
- if (ops->log_message) {
- const char *format = "Unrecognized OTR message received "
- "from %s.\n";
- char *buf = malloc(strlen(format) + strlen(sender) - 1);
- if (buf) {
- sprintf(buf, format, sender);
- ops->log_message(opdata, buf);
- free(buf);
- }
- }
- if (edata.ignore_message == -1) edata.ignore_message = 1;
- break;
- }
-
- /* If we reassembled a fragmented message, we need to free the
- * allocated memory now. */
- if (fragment_assembled) {
- free(unfragmessage);
- }
-
- if (edata.ignore_message == -1) edata.ignore_message = 0;
- return edata.ignore_message;
+ /* We received an OTR message we didn't recognize. Ignore
+ * it, and signal an event. */
+ if (ops->handle_msg_event) {
+ ops->handle_msg_event(opdata,
+ OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED,
+ context, NULL, gcry_error(GPG_ERR_NO_ERROR));
+ }
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+ }
+
+end:
+ /* If we reassembled a fragmented message, we need to free the
+ * allocated memory now. */
+ free(unfragmessage);
+
+ if (edata.ignore_message == -1) edata.ignore_message = 0;
+ return edata.ignore_message;
}
-/* Send a message to the network, fragmenting first if necessary.
- * All messages to be sent to the network should go through this
- * method immediately before they are sent, ie after encryption. */
-gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
- void *opdata, ConnContext *context, const char *message,
- OtrlFragmentPolicy fragPolicy, char **returnFragment)
+/* 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 context. */
+static void disconnect_context(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context)
{
- int mms = 0;
- if (message && ops->inject_message) {
- int msglen;
+ if (!context) return;
- if (otrl_api_version >= 0x030100 && ops->max_message_size) {
- mms = ops->max_message_size(opdata, context);
- }
- msglen = strlen(message);
-
- /* Don't incur overhead of fragmentation unless necessary */
- 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)) */
-
- err = otrl_proto_fragment_create(mms, fragment_count, &fragments,
- message);
- if (err) {
- return err;
- }
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+ context->context_priv->their_keyid > 0 &&
+ ops->is_logged_in &&
+ ops->is_logged_in(opdata, context->accountname, context->protocol,
+ context->username) == 1) {
+ if (ops->inject_message) {
+ char *encmsg = NULL;
+ gcry_error_t err;
+ OtrlTLV *tlv = otrl_tlv_new(OTRL_TLV_DISCONNECTED, 0, NULL);
- /* Determine which fragments to send and which to return
- * based on given Fragment Policy. If the first fragment
- * should be returned instead of sent, store it. */
- if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_FIRST) {
- *returnFragment = _strdup(fragments[0]);
- } else {
+ err = otrl_proto_create_data(&encmsg, context, "", tlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
+ if (!err) {
ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, fragments[0]);
- }
- for (i=1; i<fragment_count-1; i++) {
- ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, fragments[i]);
- }
- /* If the last fragment should be stored instead of sent,
- * store it */
- if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_LAST) {
- *returnFragment = _strdup(fragments[fragment_count-1]);
- } else {
- ops->inject_message(opdata, context->accountname,
- context->protocol, context->username, fragments[fragment_count-1]);
- }
- /* Now free all fragment memory */
- otrl_proto_fragment_free(&fragments, fragment_count);
-
- } else {
- /* No fragmentation necessary */
- if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
- 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);
- }
+ context->protocol, context->username, encmsg);
+ }
+ free(encmsg);
+ otrl_tlv_free(tlv);
}
- }
- return gcry_error(GPG_ERR_NO_ERROR);
+ }
+
+ otrl_context_force_plaintext(context);
+ if (ops->update_context_list) {
+ ops->update_context_list(opdata);
+ }
}
+
/* 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)
{
- ConnContext *context = otrl_context_find(us, username, accountname,
- protocol, 0, NULL, NULL, NULL);
+ ConnContext *context = otrl_context_find(us, username, accountname,
+ protocol, instance, 0, NULL, NULL, NULL);
- if (!context) return;
+ if (!context) return;
- if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
- context->their_keyid > 0 &&
- ops->is_logged_in &&
- ops->is_logged_in(opdata, accountname, protocol, username) == 1) {
- if (ops->inject_message) {
- char *encmsg = NULL;
- gcry_error_t err;
- OtrlTLV *tlv = otrl_tlv_new(OTRL_TLV_DISCONNECTED, 0, NULL);
+ disconnect_context(us, ops, opdata, context);
+}
- err = otrl_proto_create_data(&encmsg, context, "", tlv,
- OTRL_MSGFLAGS_IGNORE_UNREADABLE);
- if (!err) {
- ops->inject_message(opdata, accountname, protocol,
- username, encmsg);
- }
- free(encmsg);
+/* 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
+ * and OTRL_EXTRAKEY_BYTES bytes long. */
+gcry_error_t otrl_message_symkey(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
+ unsigned int use, const unsigned char *usedata, size_t usedatalen,
+ unsigned char *symkey)
+{
+ if (!context || (usedatalen > 0 && !usedata)) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+ context->context_priv->their_keyid > 0) {
+ unsigned char *tlvdata = malloc(usedatalen+4);
+ char *encmsg = NULL;
+ gcry_error_t err;
+ OtrlTLV *tlv;
+
+ tlvdata[0] = (use >> 24) & 0xff;
+ tlvdata[1] = (use >> 16) & 0xff;
+ tlvdata[2] = (use >> 8) & 0xff;
+ tlvdata[3] = (use) & 0xff;
+ if (usedatalen > 0) {
+ memmove(tlvdata+4, usedata, usedatalen);
}
+
+ tlv = otrl_tlv_new(OTRL_TLV_SYMKEY, usedatalen+4, tlvdata);
+ free(tlvdata);
+
+ err = otrl_proto_create_data(&encmsg, context, "", tlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE, symkey);
+ if (!err && ops->inject_message) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, encmsg);
}
+ free(encmsg);
+ otrl_tlv_free(tlv);
- otrl_context_force_plaintext(context);
- if (ops->update_context_list) {
- ops->update_context_list(opdata);
+ return err;
+ }
+
+ /* We weren't in an encrypted session. */
+ return gcry_error(GPG_ERR_INV_VALUE);
+}
+
+/* If you do _not_ define a timer_control callback function, set a timer
+ * to go off every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds, and call
+ * otrl_message_poll every time the timer goes off. */
+unsigned int otrl_message_poll_get_default_interval(OtrlUserState us)
+{
+ return POLL_DEFAULT_INTERVAL;
+}
+
+/* Call this function every so often, either as directed by the
+ * timer_control callback, or every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds if you have
+ * no timer_control callback. This function must be called from the
+ * main libotr thread.*/
+void otrl_message_poll(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata)
+{
+ /* Wipe private keys last sent before this time */
+ time_t expire_before = time(NULL) - MAX_AKE_WAIT_TIME;
+
+ ConnContext *contextp;
+
+ /* Is there a context still waiting for a DHKEY message, even after
+ * we wipe the stale ones? */
+ int still_waiting = 0;
+
+ if (us == NULL) return;
+
+ for (contextp = us->context_root; contextp; contextp = contextp->next) {
+ /* If this is a master context, and it's still waiting for a
+ * v3 DHKEY message, see if it's waited long enough. */
+ if (contextp->m_context == contextp &&
+ contextp->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY &&
+ contextp->auth.protocol_version == 3 &&
+ contextp->auth.commit_sent_time > 0) {
+ if (contextp->auth.commit_sent_time < expire_before) {
+ otrl_auth_clear(&contextp->auth);
+ } else {
+ /* Not yet expired */
+ still_waiting = 1;
+ }
}
+ }
+
+ /* If there's nothing more to wait for, stop the timer, if possible. */
+ if (still_waiting == 0 && ops && ops->timer_control) {
+ ops->timer_control(opdata, 0);
+ us->timer_running = 0;
+ }
}
diff --git a/plugins/MirOTR/libotr/src/message.h b/plugins/MirOTR/libotr/src/message.h
index e658e9d4b1..bb82ffc7e6 100644
--- a/plugins/MirOTR/libotr/src/message.h
+++ b/plugins/MirOTR/libotr/src/message.h
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,18 +16,67 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __MESSAGE_H__
#define __MESSAGE_H__
+#define OTR_ERROR_PREFIX "?OTR Error: "
+
+typedef enum {
+ OTRL_ERRCODE_NONE,
+ OTRL_ERRCODE_ENCRYPTION_ERROR,
+ OTRL_ERRCODE_MSG_NOT_IN_PRIVATE,
+ OTRL_ERRCODE_MSG_UNREADABLE,
+ OTRL_ERRCODE_MSG_MALFORMED,
+} OtrlErrorCode;
+
+/* These define the events used to indicate status of SMP to the UI */
+typedef enum {
+ OTRL_SMPEVENT_NONE,
+ OTRL_SMPEVENT_ERROR,
+ OTRL_SMPEVENT_ABORT,
+ OTRL_SMPEVENT_CHEATED,
+ OTRL_SMPEVENT_ASK_FOR_ANSWER,
+ OTRL_SMPEVENT_ASK_FOR_SECRET,
+ OTRL_SMPEVENT_IN_PROGRESS,
+ OTRL_SMPEVENT_SUCCESS,
+ OTRL_SMPEVENT_FAILURE
+} OtrlSMPEvent;
+
+/* These define the events used to indicate the messages that need
+ * to be sent */
+typedef enum {
+ OTRL_MSGEVENT_NONE,
+ OTRL_MSGEVENT_ENCRYPTION_REQUIRED,
+ OTRL_MSGEVENT_ENCRYPTION_ERROR,
+ OTRL_MSGEVENT_CONNECTION_ENDED,
+ OTRL_MSGEVENT_SETUP_ERROR,
+ OTRL_MSGEVENT_MSG_REFLECTED,
+ OTRL_MSGEVENT_MSG_RESENT,
+ OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE,
+ OTRL_MSGEVENT_RCVDMSG_UNREADABLE,
+ OTRL_MSGEVENT_RCVDMSG_MALFORMED,
+ OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD,
+ OTRL_MSGEVENT_LOG_HEARTBEAT_SENT,
+ OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR,
+ OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED,
+ OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED,
+ OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE
+} OtrlMessageEvent;
+
typedef enum {
OTRL_NOTIFY_ERROR,
OTRL_NOTIFY_WARNING,
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);
@@ -49,33 +100,10 @@ typedef struct s_OtrlMessageAppOps {
void (*inject_message)(void *opdata, const char *accountname,
const char *protocol, const char *recipient, const char *message);
- /* Display a notification message for a particular accountname /
- * protocol / username conversation. */
- void (*notify)(void *opdata, OtrlNotifyLevel level,
- const char *accountname, const char *protocol,
- const char *username, const char *title,
- const char *primary, const char *secondary);
-
- /* Display an OTR control message for a particular accountname /
- * protocol / username conversation. Return 0 if you are able to
- * successfully display it. If you return non-0 (or if this
- * function is NULL), the control message will be displayed inline,
- * as a received message, or else by using the above notify()
- * callback. */
- int (*display_otr_message)(void *opdata, const char *accountname,
- const char *protocol, const char *username, const char *msg);
-
/* When the list of ConnContexts changes (including a change in
* state), this is called so the UI can be updated. */
void (*update_context_list)(void *opdata);
- /* Return a newly allocated string containing a human-friendly name
- * for the given protocol id */
- const char *(*protocol_name)(void *opdata, const char *protocol);
-
- /* Deallocate a string allocated by protocol_name */
- void (*protocol_name_free)(void *opdata, const char *protocol_name);
-
/* A new fingerprint for the given user has been received. */
void (*new_fingerprint)(void *opdata, OtrlUserState us,
const char *accountname, const char *protocol,
@@ -94,9 +122,6 @@ typedef struct s_OtrlMessageAppOps {
* already knew. is_reply indicates whether we initiated the AKE. */
void (*still_secure)(void *opdata, ConnContext *context, int is_reply);
- /* Log a message. The passed message will end in "\n". */
- void (*log_message)(void *opdata, const char *message);
-
/* Find the maximum message size supported by this protocol. */
int (*max_message_size)(void *opdata, ConnContext *context);
@@ -108,6 +133,166 @@ typedef struct s_OtrlMessageAppOps {
/* Deallocate a string returned by account_name */
void (*account_name_free)(void *opdata, const char *account_name);
+ /* We received a request from the buddy to use the current "extra"
+ * symmetric key. The key will be passed in symkey, of length
+ * OTRL_EXTRAKEY_BYTES. The requested use, as well as use-specific
+ * data will be passed so that the applications can communicate other
+ * information (some id for the data transfer, for example). */
+ void (*received_symkey)(void *opdata, ConnContext *context,
+ unsigned int use, const unsigned char *usedata,
+ size_t usedatalen, const unsigned char *symkey);
+
+ /* Return a string according to the error event. This string will then
+ * be concatenated to an OTR header to produce an OTR protocol error
+ * message. The following are the possible error events:
+ * - OTRL_ERRCODE_ENCRYPTION_ERROR
+ * occured while encrypting a message
+ * - OTRL_ERRCODE_MSG_NOT_IN_PRIVATE
+ * sent encrypted message to somebody who is not in
+ * a mutual OTR session
+ * - OTRL_ERRCODE_MSG_UNREADABLE
+ * sent an unreadable encrypted message
+ * - OTRL_ERRCODE_MSG_MALFORMED
+ * message sent is malformed */
+ const char *(*otr_error_message)(void *opdata, ConnContext *context,
+ OtrlErrorCode err_code);
+
+ /* Deallocate a string returned by otr_error_message */
+ void (*otr_error_message_free)(void *opdata, const char *err_msg);
+
+ /* Return a string that will be prefixed to any resent message. If this
+ * function is not provided by the application then the default prefix,
+ * "[resent]", will be used.
+ * */
+ const char *(*resent_msg_prefix)(void *opdata, ConnContext *context);
+
+ /* Deallocate a string returned by resent_msg_prefix */
+ void (*resent_msg_prefix_free)(void *opdata, const char *prefix);
+
+ /* Update the authentication UI with respect to SMP events
+ * These are the possible events:
+ * - OTRL_SMPEVENT_ASK_FOR_SECRET
+ * prompt the user to enter a shared secret. The sender application
+ * should call otrl_message_initiate_smp, passing NULL as the question.
+ * When the receiver application resumes the SM protocol by calling
+ * otrl_message_respond_smp with the secret answer.
+ * - OTRL_SMPEVENT_ASK_FOR_ANSWER
+ * (same as OTRL_SMPEVENT_ASK_FOR_SECRET but sender calls
+ * otrl_message_initiate_smp_q instead)
+ * - OTRL_SMPEVENT_CHEATED
+ * abort the current auth and update the auth progress dialog
+ * with progress_percent. otrl_message_abort_smp should be called to
+ * stop the SM protocol.
+ * - OTRL_SMPEVENT_INPROGRESS and
+ * OTRL_SMPEVENT_SUCCESS and
+ * OTRL_SMPEVENT_FAILURE and
+ * OTRL_SMPEVENT_ABORT
+ * update the auth progress dialog with progress_percent
+ * - OTRL_SMPEVENT_ERROR
+ * (same as OTRL_SMPEVENT_CHEATED)
+ * */
+ void (*handle_smp_event)(void *opdata, OtrlSMPEvent smp_event,
+ ConnContext *context, unsigned short progress_percent,
+ char *question);
+
+ /* Handle and send the appropriate message(s) to the sender/recipient
+ * depending on the message events. All the events only require an opdata,
+ * the event, and the context. The message and err will be NULL except for
+ * some events (see below). The possible events are:
+ * - OTRL_MSGEVENT_ENCRYPTION_REQUIRED
+ * Our policy requires encryption but we are trying to send
+ * an unencrypted message out.
+ * - OTRL_MSGEVENT_ENCRYPTION_ERROR
+ * An error occured while encrypting a message and the message
+ * was not sent.
+ * - OTRL_MSGEVENT_CONNECTION_ENDED
+ * Message has not been sent because our buddy has ended the
+ * private conversation. We should either close the connection,
+ * or refresh it.
+ * - OTRL_MSGEVENT_SETUP_ERROR
+ * A private conversation could not be set up. A gcry_error_t
+ * will be passed.
+ * - OTRL_MSGEVENT_MSG_REFLECTED
+ * Received our own OTR messages.
+ * - OTRL_MSGEVENT_MSG_RESENT
+ * The previous message was resent.
+ * - OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE
+ * Received an encrypted message but cannot read
+ * it because no private connection is established yet.
+ * - OTRL_MSGEVENT_RCVDMSG_UNREADABLE
+ * Cannot read the received message.
+ * - OTRL_MSGEVENT_RCVDMSG_MALFORMED
+ * The message received contains malformed data.
+ * - OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD
+ * Received a heartbeat.
+ * - OTRL_MSGEVENT_LOG_HEARTBEAT_SENT
+ * Sent a heartbeat.
+ * - OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR
+ * Received a general OTR error. The argument 'message' will
+ * also be passed and it will contain the OTR error message.
+ * - OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED
+ * Received an unencrypted message. The argument 'message' will
+ * 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);
+
+ /* 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);
+
+ /* When timer_control is called, turn off any existing periodic
+ * timer.
+ *
+ * Additionally, if interval > 0, set a new periodic timer
+ * to go off every interval seconds. When that timer fires, you
+ * must call otrl_message_poll(userstate, uiops, uiopdata); from the
+ * main libotr thread.
+ *
+ * The timing does not have to be exact; this timer is used to
+ * provide forward secrecy by cleaning up stale private state that
+ * may otherwise stick around in memory. Note that the
+ * timer_control callback may be invoked from otrl_message_poll
+ * itself, possibly to indicate that interval == 0 (that is, that
+ * there's no more periodic work to be done at this time).
+ *
+ * If you set this callback to NULL, then you must ensure that your
+ * application calls otrl_message_poll(userstate, uiops, uiopdata);
+ * from the main libotr thread every definterval seconds (where
+ * definterval can be obtained by calling
+ * definterval = otrl_message_poll_get_default_interval(userstate);
+ * right after creating the userstate). The advantage of
+ * implementing the timer_control callback is that the timer can be
+ * turned on by libotr only when it's needed.
+ *
+ * It is not a problem (except for a minor performance hit) to call
+ * otrl_message_poll more often than requested, whether
+ * timer_control is implemented or not.
+ *
+ * If you fail to implement the timer_control callback, and also
+ * fail to periodically call otrl_message_poll, then you open your
+ * users to a possible forward secrecy violation: an attacker that
+ * compromises the user's computer may be able to decrypt a handful
+ * of long-past messages (the first messages of an OTR
+ * conversation).
+ */
+ void (*timer_control)(void *opdata, unsigned int interval);
+
} OtrlMessageAppOps;
/* Deallocate a message allocated by other otrl_message_* routines. */
@@ -120,24 +305,40 @@ void otrl_message_free(char *message);
* pointer to the new ConnContext. You can use this to add
* application-specific information to the ConnContext using the
* "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_sending.
+ * can pass NULL for the last two arguments of otrl_message_sending.
*
* tlvs is a chain of OtrlTLVs to append to the private message. It is
* usually correct to just pass NULL here.
*
- * If this routine returns 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.
- *
- * If *messagep gets set by the call to something non-NULL, then you
- * should replace your message with the contents of *messagep, and
- * send that instead. Call otrl_message_free(*messagep) when you're
+ * 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
+ * tried to encrypt the message, but for some reason failed. DO NOT send the
+ * message in the clear in that case. If *messagep gets set by the call to
+ * something non-NULL, then you should replace your message with the contents
+ * 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.
+ *
+ * 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, const char *message, OtrlTLV *tlvs,
- char **messagep,
+ 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);
@@ -148,7 +349,13 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
* a pointer to the new ConnContext. You can use this to add
* application-specific information to the ConnContext using the
* "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.
+ * can pass NULL for the last two arguments of otrl_message_receiving.
+ *
+ * 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
@@ -168,23 +375,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,
+ OtrlTLV **tlvsp, ConnContext **contextp,
void (*add_appdata)(void *data, ConnContext *context),
void *data);
-/* Send a message to the network, fragmenting first if necessary.
- * All messages to be sent to the network should go through this
- * method immediately before they are sent, ie after encryption. */
-gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
- void *opdata, ConnContext *context, const char *message,
- OtrlFragmentPolicy fragPolicy, char **returnFragment);
-
/* 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,
@@ -207,4 +414,27 @@ void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
void *opdata, ConnContext *context);
+/* 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
+ * and OTRL_EXTRAKEY_BYTES bytes long. */
+gcry_error_t otrl_message_symkey(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
+ unsigned int use, const unsigned char *usedata, size_t usedatalen,
+ unsigned char *symkey);
+
+/* If you do _not_ define a timer_control callback function, set a timer
+ * to go off every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds, and call
+ * otrl_message_poll every time the timer goes off. */
+unsigned int otrl_message_poll_get_default_interval(OtrlUserState us);
+
+/* Call this function every so often, either as directed by the
+ * timer_control callback, or every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds if you have
+ * no timer_control callback. This function must be called from the
+ * main libotr thread.*/
+void otrl_message_poll(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata);
+
#endif
diff --git a/plugins/MirOTR/libotr/src/privkey-t.h b/plugins/MirOTR/libotr/src/privkey-t.h
index 3421b8baac..7dd120e789 100644
--- a/plugins/MirOTR/libotr/src/privkey-t.h
+++ b/plugins/MirOTR/libotr/src/privkey-t.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __PRIVKEY_T_H__
@@ -36,4 +37,14 @@ typedef struct s_OtrlPrivKey {
#define OTRL_PUBKEY_TYPE_DSA 0x0000
+/* The list of privkeys currently being constructed, possibly in a
+ * background thread */
+typedef struct s_OtrlPendingPrivKey {
+ struct s_OtrlPendingPrivKey *next;
+ struct s_OtrlPendingPrivKey **tous;
+
+ char *accountname;
+ char *protocol;
+} OtrlPendingPrivKey;
+
#endif
diff --git a/plugins/MirOTR/libotr/src/privkey.c b/plugins/MirOTR/libotr/src/privkey.c
index 498bfae39a..6e4bbe40fc 100644
--- a/plugins/MirOTR/libotr/src/privkey.c
+++ b/plugins/MirOTR/libotr/src/privkey.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -31,44 +32,47 @@
#include "serial.h"
/* Convert a 20-byte hash value to a 45-byte human-readable value */
-void otrl_privkey_hash_to_human(char human[45], const unsigned char hash[20])
+void otrl_privkey_hash_to_human(
+ char human[OTRL_PRIVKEY_FPRINT_HUMAN_LEN],
+ const unsigned char hash[20])
{
- int word, byte;
- char *p = human;
+ int word, byte;
+ char *p = human;
- for(word=0; word<5; ++word) {
+ for(word=0; word<5; ++word) {
for(byte=0; byte<4; ++byte) {
- sprintf(p, "%02X", hash[word*4+byte]);
- p += 2;
+ sprintf(p, "%02X", hash[word*4+byte]);
+ p += 2;
}
*(p++) = ' ';
- }
- /* Change that last ' ' to a '\0' */
- --p;
- *p = '\0';
+ }
+ /* Change that last ' ' to a '\0' */
+ --p;
+ *p = '\0';
}
/* Calculate a human-readable hash of our DSA public key. Return it in
* the passed fingerprint buffer. Return NULL on error, or a pointer to
* the given buffer on success. */
-char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
+char *otrl_privkey_fingerprint(OtrlUserState us,
+ char fingerprint[OTRL_PRIVKEY_FPRINT_HUMAN_LEN],
const char *accountname, const char *protocol)
{
- unsigned char hash[20];
- OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
+ unsigned char hash[20];
+ OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
- if (p) {
+ if (p) {
/* Calculate the hash */
gcry_md_hash_buffer(GCRY_MD_SHA1, hash, p->pubkey_data,
p->pubkey_datalen);
/* Now convert it to a human-readable format */
otrl_privkey_hash_to_human(fingerprint, hash);
- } else {
+ } else {
return NULL;
- }
+ }
- return fingerprint;
+ return fingerprint;
}
/* Calculate a raw hash of our DSA public key. Return it in the passed
@@ -77,184 +81,184 @@ char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us,
unsigned char hash[20], const char *accountname, const char *protocol)
{
- OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
+ OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
- if (p) {
+ if (p) {
/* Calculate the hash */
gcry_md_hash_buffer(GCRY_MD_SHA1, hash, p->pubkey_data,
p->pubkey_datalen);
- } else {
+ } else {
return NULL;
- }
+ }
- return hash;
+ return hash;
}
/* Create a public key block from a private key */
static gcry_error_t make_pubkey(unsigned char **pubbufp, size_t *publenp,
gcry_sexp_t privkey)
{
- gcry_mpi_t p,q,g,y;
- gcry_sexp_t dsas,ps,qs,gs,ys;
- size_t np,nq,ng,ny;
- enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- unsigned char *bufp;
- size_t lenp;
-
- *pubbufp = NULL;
- *publenp = 0;
-
- /* Extract the public parameters */
- dsas = gcry_sexp_find_token(privkey, "dsa", 0);
- if (dsas == NULL) {
+ gcry_mpi_t p,q,g,y;
+ gcry_sexp_t dsas,ps,qs,gs,ys;
+ size_t np,nq,ng,ny;
+ enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ unsigned char *bufp;
+ size_t lenp;
+
+ *pubbufp = NULL;
+ *publenp = 0;
+
+ /* Extract the public parameters */
+ dsas = gcry_sexp_find_token(privkey, "dsa", 0);
+ if (dsas == NULL) {
return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
- }
- ps = gcry_sexp_find_token(dsas, "p", 0);
- qs = gcry_sexp_find_token(dsas, "q", 0);
- gs = gcry_sexp_find_token(dsas, "g", 0);
- ys = gcry_sexp_find_token(dsas, "y", 0);
- gcry_sexp_release(dsas);
- if (!ps || !qs || !gs || !ys) {
+ }
+ ps = gcry_sexp_find_token(dsas, "p", 0);
+ qs = gcry_sexp_find_token(dsas, "q", 0);
+ gs = gcry_sexp_find_token(dsas, "g", 0);
+ ys = gcry_sexp_find_token(dsas, "y", 0);
+ gcry_sexp_release(dsas);
+ if (!ps || !qs || !gs || !ys) {
gcry_sexp_release(ps);
gcry_sexp_release(qs);
gcry_sexp_release(gs);
gcry_sexp_release(ys);
return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
- }
- p = gcry_sexp_nth_mpi(ps, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(ps);
- q = gcry_sexp_nth_mpi(qs, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(qs);
- g = gcry_sexp_nth_mpi(gs, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(gs);
- y = gcry_sexp_nth_mpi(ys, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(ys);
- if (!p || !q || !g || !y) {
+ }
+ p = gcry_sexp_nth_mpi(ps, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(ps);
+ q = gcry_sexp_nth_mpi(qs, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(qs);
+ g = gcry_sexp_nth_mpi(gs, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(gs);
+ y = gcry_sexp_nth_mpi(ys, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(ys);
+ if (!p || !q || !g || !y) {
gcry_mpi_release(p);
gcry_mpi_release(q);
gcry_mpi_release(g);
gcry_mpi_release(y);
return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
- }
-
- *publenp = 0;
- gcry_mpi_print(format, NULL, 0, &np, p);
- *publenp += np + 4;
- gcry_mpi_print(format, NULL, 0, &nq, q);
- *publenp += nq + 4;
- gcry_mpi_print(format, NULL, 0, &ng, g);
- *publenp += ng + 4;
- gcry_mpi_print(format, NULL, 0, &ny, y);
- *publenp += ny + 4;
-
- *pubbufp = malloc(*publenp);
- if (*pubbufp == NULL) {
+ }
+
+ *publenp = 0;
+ gcry_mpi_print(format, NULL, 0, &np, p);
+ *publenp += np + 4;
+ gcry_mpi_print(format, NULL, 0, &nq, q);
+ *publenp += nq + 4;
+ gcry_mpi_print(format, NULL, 0, &ng, g);
+ *publenp += ng + 4;
+ gcry_mpi_print(format, NULL, 0, &ny, y);
+ *publenp += ny + 4;
+
+ *pubbufp = malloc(*publenp);
+ if (*pubbufp == NULL) {
gcry_mpi_release(p);
gcry_mpi_release(q);
gcry_mpi_release(g);
gcry_mpi_release(y);
return gcry_error(GPG_ERR_ENOMEM);
- }
- bufp = *pubbufp;
- lenp = *publenp;
+ }
+ bufp = *pubbufp;
+ lenp = *publenp;
- write_mpi(p,np,"P");
- write_mpi(q,nq,"Q");
- write_mpi(g,ng,"G");
- write_mpi(y,ny,"Y");
+ write_mpi(p,np,"P");
+ write_mpi(q,nq,"Q");
+ write_mpi(g,ng,"G");
+ write_mpi(y,ny,"Y");
- gcry_mpi_release(p);
- gcry_mpi_release(q);
- gcry_mpi_release(g);
- gcry_mpi_release(y);
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(g);
+ gcry_mpi_release(y);
- return gcry_error(GPG_ERR_NO_ERROR);
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Read a sets of private DSA keys from a file on disk into the given
* OtrlUserState. */
gcry_error_t otrl_privkey_read(OtrlUserState us, const char *filename)
{
- FILE *privf;
- gcry_error_t err;
-
- /* Open the privkey file. We use rb mode so that on WIN32, fread()
- * reads the same number of bytes that fstat() indicates are in the
- * file. */
- privf = fopen(filename, "rb");
- if (!privf) {
+ FILE *privf;
+ gcry_error_t err;
+
+ /* Open the privkey file. We use rb mode so that on WIN32, fread()
+ * reads the same number of bytes that fstat() indicates are in the
+ * file. */
+ privf = fopen(filename, "rb");
+ if (!privf) {
err = gcry_error_from_errno(errno);
return err;
- }
+ }
- err = otrl_privkey_read_FILEp(us, privf);
+ err = otrl_privkey_read_FILEp(us, privf);
- fclose(privf);
- return err;
+ fclose(privf);
+ return err;
}
/* Read a sets of private DSA keys from a FILE* into the given
* OtrlUserState. The FILE* must be open for reading. */
gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
{
- int privfd;
- struct stat st;
- char *buf;
- const char *token;
- size_t tokenlen;
- gcry_error_t err;
- gcry_sexp_t allkeys;
- size_t i;
-
- if (!privf) return gcry_error(GPG_ERR_NO_ERROR);
-
- /* Release any old ideas we had about our keys */
- otrl_privkey_forget_all(us);
-
- /* Load the data into a buffer */
- privfd = _fileno(privf);
- if (fstat(privfd, &st)) {
+ int privfd;
+ struct stat st;
+ char *buf;
+ const char *token;
+ size_t tokenlen;
+ gcry_error_t err;
+ gcry_sexp_t allkeys;
+ int i;
+
+ if (!privf) return gcry_error(GPG_ERR_NO_ERROR);
+
+ /* Release any old ideas we had about our keys */
+ otrl_privkey_forget_all(us);
+
+ /* Load the data into a buffer */
+ privfd = fileno(privf);
+ if (fstat(privfd, &st)) {
err = gcry_error_from_errno(errno);
return err;
- }
- buf = malloc(st.st_size);
- if (!buf && st.st_size > 0) {
+ }
+ buf = malloc(st.st_size);
+ if (!buf && st.st_size > 0) {
return gcry_error(GPG_ERR_ENOMEM);
- }
- if (fread(buf, st.st_size, 1, privf) != 1) {
+ }
+ if (fread(buf, st.st_size, 1, privf) != 1) {
err = gcry_error_from_errno(errno);
free(buf);
return err;
- }
+ }
- err = gcry_sexp_new(&allkeys, buf, st.st_size, 0);
- free(buf);
- if (err) {
+ err = gcry_sexp_new(&allkeys, buf, st.st_size, 0);
+ free(buf);
+ if (err) {
return err;
- }
+ }
- token = gcry_sexp_nth_data(allkeys, 0, &tokenlen);
- if (tokenlen != 8 || strncmp(token, "privkeys", 8)) {
+ token = gcry_sexp_nth_data(allkeys, 0, &tokenlen);
+ if (tokenlen != 8 || strncmp(token, "privkeys", 8)) {
gcry_sexp_release(allkeys);
return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
- }
+ }
- /* Get each account */
- for(i=1; i<gcry_sexp_length(allkeys); ++i) {
+ /* Get each account */
+ for(i=1; i<gcry_sexp_length(allkeys); ++i) {
gcry_sexp_t names, protos, privs;
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)) {
- gcry_sexp_release(accounts);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ gcry_sexp_release(accounts);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
}
/* Extract the name, protocol, and privkey S-exps */
names = gcry_sexp_find_token(accounts, "name", 0);
@@ -262,28 +266,28 @@ gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
privs = gcry_sexp_find_token(accounts, "private-key", 0);
gcry_sexp_release(accounts);
if (!names || !protos || !privs) {
- gcry_sexp_release(names);
- gcry_sexp_release(protos);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ gcry_sexp_release(names);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
}
/* Extract the actual name and protocol */
token = gcry_sexp_nth_data(names, 1, &tokenlen);
if (!token) {
- gcry_sexp_release(names);
- gcry_sexp_release(protos);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ gcry_sexp_release(names);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
}
name = malloc(tokenlen + 1);
if (!name) {
- gcry_sexp_release(names);
- gcry_sexp_release(protos);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_ENOMEM);
+ gcry_sexp_release(names);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_ENOMEM);
}
memmove(name, token, tokenlen);
name[tokenlen] = '\0';
@@ -291,19 +295,19 @@ gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
token = gcry_sexp_nth_data(protos, 1, &tokenlen);
if (!token) {
- free(name);
- gcry_sexp_release(protos);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ free(name);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
}
proto = malloc(tokenlen + 1);
if (!proto) {
- free(name);
- gcry_sexp_release(protos);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_ENOMEM);
+ free(name);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_ENOMEM);
}
memmove(proto, token, tokenlen);
proto[tokenlen] = '\0';
@@ -312,11 +316,11 @@ gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
/* Make a new OtrlPrivKey entry */
p = malloc(sizeof(*p));
if (!p) {
- free(name);
- free(proto);
- gcry_sexp_release(privs);
- gcry_sexp_release(allkeys);
- return gcry_error(GPG_ERR_ENOMEM);
+ free(name);
+ free(proto);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_ENOMEM);
}
/* Fill it in and link it up */
@@ -326,155 +330,340 @@ gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf)
p->privkey = privs;
p->next = us->privkey_root;
if (p->next) {
- p->next->tous = &(p->next);
+ p->next->tous = &(p->next);
}
p->tous = &(us->privkey_root);
us->privkey_root = p;
err = make_pubkey(&(p->pubkey_data), &(p->pubkey_datalen), p->privkey);
if (err) {
- gcry_sexp_release(allkeys);
- otrl_privkey_forget(p);
- return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ gcry_sexp_release(allkeys);
+ otrl_privkey_forget(p);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
}
+ }
+ gcry_sexp_release(allkeys);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+static OtrlPendingPrivKey *pending_find(OtrlUserState us,
+ const char *accountname, const char *protocol)
+{
+ OtrlPendingPrivKey *search = us->pending_root;
+
+ while (search) {
+ if (!strcmp(search->accountname, accountname) &&
+ !strcmp(search->protocol, protocol)) {
+ /* Found it */
+ return search;
}
- gcry_sexp_release(allkeys);
+ search = search->next;
+ }
+ return NULL;
+}
- return gcry_error(GPG_ERR_NO_ERROR);
+/* Insert an account/protocol pair into the pending privkey list of the
+ * given OtrlUserState and return a pointer to the new
+ * OtrlPendingPrivKey, or return NULL if it's already there. */
+static OtrlPendingPrivKey *pending_insert(OtrlUserState us,
+ const char *accountname, const char *protocol)
+{
+ /* See if it's already there */
+ OtrlPendingPrivKey *search = pending_find(us, accountname, protocol);
+
+ if (search) {
+ /* It is */
+ return NULL;
+ }
+
+ /* We'll insert it at the beginning of the list */
+ search = malloc(sizeof(*search));
+ if (!search) return NULL;
+
+ search->accountname = strdup(accountname);
+ search->protocol = strdup(protocol);
+
+ search->next = us->pending_root;
+ us->pending_root = search;
+ if (search->next) {
+ search->next->tous = &(search->next);
+ }
+ search->tous = &(us->pending_root);
+ return search;
+}
+
+static void pending_forget(OtrlPendingPrivKey *ppk)
+{
+ if (ppk) {
+ free(ppk->accountname);
+ free(ppk->protocol);
+
+ /* Re-link the list */
+ *(ppk->tous) = ppk->next;
+ if (ppk->next) {
+ ppk->next->tous = ppk->tous;
+ }
+
+ free(ppk);
+ }
+}
+
+/* Free the memory associated with the pending privkey list */
+void otrl_privkey_pending_forget_all(OtrlUserState us)
+{
+ while(us->pending_root) {
+ pending_forget(us->pending_root);
+ }
}
static gcry_error_t sexp_write(FILE *privf, gcry_sexp_t sexp)
{
- size_t buflen;
- char *buf;
+ size_t buflen;
+ char *buf;
- buflen = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
- buf = malloc(buflen);
- if (buf == NULL && buflen > 0) {
+ buflen = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+ buf = malloc(buflen);
+ if (buf == NULL && buflen > 0) {
return gcry_error(GPG_ERR_ENOMEM);
- }
- gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, buf, buflen);
-
- fprintf(privf, "%s", buf);
- free(buf);
+ }
+ gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, buf, buflen);
- return gcry_error(GPG_ERR_NO_ERROR);
+ fprintf(privf, "%s", buf);
+ free(buf);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
}
static gcry_error_t account_write(FILE *privf, const char *accountname,
const char *protocol, gcry_sexp_t privkey)
{
- gcry_error_t err;
- gcry_sexp_t names, protos;
+ gcry_error_t err;
+ gcry_sexp_t names, protos;
- fprintf(privf, " (account\n");
+ fprintf(privf, " (account\n");
- err = gcry_sexp_build(&names, NULL, "(name %s)", accountname);
- if (!err) {
+ err = gcry_sexp_build(&names, NULL, "(name %s)", accountname);
+ if (!err) {
err = sexp_write(privf, names);
gcry_sexp_release(names);
- }
- if (!err) err = gcry_sexp_build(&protos, NULL, "(protocol %s)", protocol);
- if (!err) {
+ }
+ if (!err) err = gcry_sexp_build(&protos, NULL, "(protocol %s)", protocol);
+ if (!err) {
err = sexp_write(privf, protos);
gcry_sexp_release(protos);
- }
- if (!err) err = sexp_write(privf, privkey);
+ }
+ if (!err) err = sexp_write(privf, privkey);
- fprintf(privf, " )\n");
+ fprintf(privf, " )\n");
+ return err;
+}
+
+struct s_pending_privkey_calc {
+ char *accountname;
+ char *protocol;
+ gcry_sexp_t privkey;
+};
+
+/* Begin a private key generation that will potentially take place in
+ * a background thread. This routine must be called from the main
+ * thread. It will set *newkeyp, which you can pass to
+ * otrl_privkey_generate_calculate in a background thread. If it
+ * returns gcry_error(GPG_ERR_EEXIST), then a privkey creation for
+ * this accountname/protocol is already in progress, and *newkeyp will
+ * be set to NULL. */
+gcry_error_t otrl_privkey_generate_start(OtrlUserState us,
+ const char *accountname, const char *protocol, void **newkeyp)
+{
+ OtrlPendingPrivKey *found = pending_find(us, accountname, protocol);
+ struct s_pending_privkey_calc *ppc;
+
+ if (found) {
+ if (newkeyp) *newkeyp = NULL;
+ return gcry_error(GPG_ERR_EEXIST);
+ }
+
+ /* We're not already creating this key. Mark it as in progress. */
+ pending_insert(us, accountname, protocol);
+
+ /* Allocate the working structure */
+ ppc = malloc(sizeof(*ppc));
+ ppc->accountname = strdup(accountname);
+ ppc->protocol = strdup(protocol);
+ ppc->privkey = NULL;
+
+ *newkeyp = ppc;
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Do the private key generation calculation. You may call this from a
+ * background thread. When it completes, call
+ * otrl_privkey_generate_finish from the _main_ thread. */
+gcry_error_t otrl_privkey_generate_calculate(void *newkey)
+{
+ struct s_pending_privkey_calc *ppc =
+ (struct s_pending_privkey_calc *)newkey;
+ gcry_error_t err;
+ gcry_sexp_t key, parms;
+ static const char *parmstr = "(genkey (dsa (nbits 4:1024)))";
+
+ /* Create a DSA key */
+ err = gcry_sexp_new(&parms, parmstr, strlen(parmstr), 0);
+ if (err) {
+ return err;
+ }
+ err = gcry_pk_genkey(&key, parms);
+ gcry_sexp_release(parms);
+ if (err) {
return err;
+ }
+
+ /* Extract the privkey */
+ ppc->privkey = gcry_sexp_find_token(key, "private-key", 0);
+ gcry_sexp_release(key);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
}
-/* Generate a private DSA key for a given account, storing it into a
- * file on disk, and loading it into the given OtrlUserState. Overwrite any
- * previously generated keys for that account in that OtrlUserState. */
-gcry_error_t otrl_privkey_generate(OtrlUserState us, const char *filename,
- const char *accountname, const char *protocol)
+static FILE* privkey_fopen(const char *filename, gcry_error_t *errp)
{
- gcry_error_t err;
- FILE *privf;
+ FILE *privf;
#ifndef WIN32
- mode_t oldmask;
+ mode_t oldmask;
#endif
#ifndef WIN32
- oldmask = umask(077);
+ oldmask = umask(077);
#endif
- privf = fopen(filename, "w+b");
- if (!privf) {
+ privf = fopen(filename, "w+b");
+ if (!privf && errp) {
+ *errp = gcry_error_from_errno(errno);
+ }
#ifndef WIN32
- umask(oldmask);
+ umask(oldmask);
#endif
- err = gcry_error_from_errno(errno);
- return err;
- }
-
- err = otrl_privkey_generate_FILEp(us, privf, accountname, protocol);
+ return privf;
+}
- fclose(privf);
-#ifndef WIN32
- umask(oldmask);
-#endif
- return err;
+/* 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_cancelled(OtrlUserState us, void *newkey)
+{
+ struct s_pending_privkey_calc *ppc =
+ (struct s_pending_privkey_calc *)newkey;
+
+ if (us) {
+ pending_forget(pending_find(us, ppc->accountname, ppc->protocol));
+ }
+
+ /* Deallocate ppc */
+ free(ppc->accountname);
+ free(ppc->protocol);
+ gcry_sexp_release(ppc->privkey);
+ free(ppc);
}
-/* Generate a private DSA key for a given account, storing it into a
- * FILE*, and loading it into the given OtrlUserState. Overwrite any
- * previously generated keys for that account in that OtrlUserState.
- * The FILE* must be open for reading and writing. */
-gcry_error_t otrl_privkey_generate_FILEp(OtrlUserState us, FILE *privf,
- const char *accountname, const char *protocol)
+/* Call this from the main thread only. It will write the newly created
+ * private key into the given file and store it in the OtrlUserState. */
+gcry_error_t otrl_privkey_generate_finish(OtrlUserState us,
+ void *newkey, const char *filename)
{
- gcry_error_t err;
- gcry_sexp_t key, parms, privkey;
- static const char *parmstr = "(genkey (dsa (nbits 4:1024)))";
- OtrlPrivKey *p;
+ gcry_error_t err;
+ FILE *privf = privkey_fopen(filename, &err);
+ if (!privf) {
+ return err;
+ }
- if (!privf) return gcry_error(GPG_ERR_NO_ERROR);
+ err = otrl_privkey_generate_finish_FILEp(us, newkey, privf);
- /* Create a DSA key */
- err = gcry_sexp_new(&parms, parmstr, strlen(parmstr), 0);
- if (err) {
- return err;
- }
- err = gcry_pk_genkey(&key, parms);
- gcry_sexp_release(parms);
- if (err) {
- return err;
- }
+ fclose(privf);
+ return err;
+}
- /* Extract the privkey */
- privkey = gcry_sexp_find_token(key, "private-key", 0);
- gcry_sexp_release(key);
+/* Call this from the main thread only. It will write the newly created
+ * private key into the given FILE* (which must be open for reading and
+ * writing) and store it in the OtrlUserState. */
+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;
+ gcry_error_t ret = gcry_error(GPG_ERR_INV_VALUE);
+
+ if (ppc && us && privf) {
+ OtrlPrivKey *p;
/* Output the other keys we know */
fprintf(privf, "(privkeys\n");
for (p=us->privkey_root; p; p=p->next) {
- /* Skip this one if our new key replaces it */
- if (!strcmp(p->accountname, accountname) &&
- !strcmp(p->protocol, protocol)) {
+ /* Skip this one if our new key replaces it */
+ if (!strcmp(p->accountname, ppc->accountname) &&
+ !strcmp(p->protocol, ppc->protocol)) {
continue;
- }
+ }
- account_write(privf, p->accountname, p->protocol, p->privkey);
+ account_write(privf, p->accountname, p->protocol, p->privkey);
}
- account_write(privf, accountname, protocol, privkey);
- gcry_sexp_release(privkey);
+ account_write(privf, ppc->accountname, ppc->protocol, ppc->privkey);
fprintf(privf, ")\n");
fseek(privf, 0, SEEK_SET);
- return otrl_privkey_read_FILEp(us, privf);
+ ret = otrl_privkey_read_FILEp(us, privf);
+ }
+
+ otrl_privkey_generate_cancelled(us, newkey);
+
+ return ret;
+}
+
+/* Generate a private DSA key for a given account, storing it into a
+ * file on disk, and loading it into the given OtrlUserState. Overwrite any
+ * previously generated keys for that account in that OtrlUserState. */
+gcry_error_t otrl_privkey_generate(OtrlUserState us, const char *filename,
+ const char *accountname, const char *protocol)
+{
+ gcry_error_t err;
+ FILE *privf = privkey_fopen(filename, &err);
+ if (!privf) {
+ return err;
+ }
+
+ err = otrl_privkey_generate_FILEp(us, privf, accountname, protocol);
+
+ fclose(privf);
+ return err;
+}
+
+/* Generate a private DSA key for a given account, storing it into a
+ * FILE*, and loading it into the given OtrlUserState. Overwrite any
+ * previously generated keys for that account in that OtrlUserState.
+ * The FILE* must be open for reading and writing. */
+gcry_error_t otrl_privkey_generate_FILEp(OtrlUserState us, FILE *privf,
+ const char *accountname, const char *protocol)
+{
+ void *newkey = NULL;
+ gcry_error_t err;
+
+ err = otrl_privkey_generate_start(us, accountname, protocol, &newkey);
+ if (newkey) {
+ otrl_privkey_generate_calculate(newkey);
+ err = otrl_privkey_generate_finish_FILEp(us, newkey, privf);
+ }
+
+ return err;
}
/* Convert a hex character to a value */
static unsigned int ctoh(char c)
{
- if (c >= '0' && c <= '9') return c-'0';
- if (c >= 'a' && c <= 'f') return c-'a'+10;
- if (c >= 'A' && c <= 'F') return c-'A'+10;
- return 0; /* Unknown hex char */
+ if (c >= '0' && c <= '9') return c-'0';
+ if (c >= 'a' && c <= 'f') return c-'a'+10;
+ if (c >= 'A' && c <= 'F') return c-'A'+10;
+ return 0; /* Unknown hex char */
}
/* Read the fingerprint store from a file on disk into the given
@@ -485,19 +674,19 @@ gcry_error_t otrl_privkey_read_fingerprints(OtrlUserState us,
void (*add_app_data)(void *data, ConnContext *context),
void *data)
{
- gcry_error_t err;
- FILE *storef;
+ gcry_error_t err;
+ FILE *storef;
- storef = fopen(filename, "rb");
- if (!storef) {
+ storef = fopen(filename, "rb");
+ if (!storef) {
err = gcry_error_from_errno(errno);
return err;
- }
+ }
- err = otrl_privkey_read_fingerprints_FILEp(us, storef, add_app_data, data);
+ err = otrl_privkey_read_fingerprints_FILEp(us, storef, add_app_data, data);
- fclose(storef);
- return err;
+ fclose(storef);
+ return err;
}
/* Read the fingerprint store from a FILE* into the given
@@ -508,14 +697,14 @@ gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us,
void (*add_app_data)(void *data, ConnContext *context),
void *data)
{
- ConnContext *context;
- char storeline[1000];
- unsigned char fingerprint[20];
- size_t maxsize = sizeof(storeline);
+ ConnContext *context;
+ char storeline[1000];
+ unsigned char fingerprint[20];
+ size_t maxsize = sizeof(storeline);
- if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
+ if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
- while(fgets(storeline, maxsize, storef)) {
+ while(fgets(storeline, maxsize, storef)) {
char *username;
char *accountname;
char *protocol;
@@ -545,52 +734,52 @@ gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us,
hex = tab + 1;
tab = strchr(hex, '\t');
if (!tab) {
- eol = strchr(hex, '\r');
- if (!eol) eol = strchr(hex, '\n');
- if (!eol) continue;
- *eol = '\0';
- trust = NULL;
+ eol = strchr(hex, '\r');
+ if (!eol) eol = strchr(hex, '\n');
+ if (!eol) continue;
+ *eol = '\0';
+ trust = NULL;
} else {
- *tab = '\0';
- trust = tab + 1;
- eol = strchr(trust, '\r');
- if (!eol) eol = strchr(trust, '\n');
- if (!eol) continue;
- *eol = '\0';
+ *tab = '\0';
+ trust = tab + 1;
+ eol = strchr(trust, '\r');
+ if (!eol) eol = strchr(trust, '\n');
+ if (!eol) continue;
+ *eol = '\0';
}
if (strlen(hex) != 40) continue;
for(j=0, i=0; i<40; i+=2) {
- fingerprint[j++] = (ctoh(hex[i]) << 4) + (ctoh(hex[i+1]));
+ fingerprint[j++] = (ctoh(hex[i]) << 4) + (ctoh(hex[i+1]));
}
/* 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);
- }
+ }
- return gcry_error(GPG_ERR_NO_ERROR);
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Write the fingerprint store from a given OtrlUserState to a file on disk. */
gcry_error_t otrl_privkey_write_fingerprints(OtrlUserState us,
const char *filename)
{
- gcry_error_t err;
- FILE *storef;
+ gcry_error_t err;
+ FILE *storef;
- storef = fopen(filename, "wb");
- if (!storef) {
+ storef = fopen(filename, "wb");
+ if (!storef) {
err = gcry_error_from_errno(errno);
return err;
- }
+ }
- err = otrl_privkey_write_fingerprints_FILEp(us, storef);
+ err = otrl_privkey_write_fingerprints_FILEp(us, storef);
- fclose(storef);
- return err;
+ fclose(storef);
+ return err;
}
/* Write the fingerprint store from a given OtrlUserState to a FILE*.
@@ -598,26 +787,29 @@ gcry_error_t otrl_privkey_write_fingerprints(OtrlUserState us,
gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us,
FILE *storef)
{
- ConnContext *context;
- Fingerprint *fprint;
+ ConnContext *context;
+ Fingerprint *fprint;
+
+ if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
- if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
+ for(context = us->context_root; context; context = context->next) {
+ /* Fingerprints are only stored in the master contexts */
+ if (context->their_instance != OTRL_INSTAG_MASTER) continue;
- for(context = us->context_root; context; context = context->next) {
- /* Don't both with the first (fingerprintless) entry. */
+ /* Don't bother with the first (fingerprintless) entry. */
for (fprint = context->fingerprint_root.next; fprint;
fprint = fprint->next) {
- int i;
- fprintf(storef, "%s\t%s\t%s\t", context->username,
- context->accountname, context->protocol);
- for(i=0;i<20;++i) {
+ int i;
+ fprintf(storef, "%s\t%s\t%s\t", context->username,
+ context->accountname, context->protocol);
+ for(i=0;i<20;++i) {
fprintf(storef, "%02x", fprint->fingerprint[i]);
- }
- fprintf(storef, "\t%s\n", fprint->trust ? fprint->trust : "");
- }
+ }
+ fprintf(storef, "\t%s\n", fprint->trust ? fprint->trust : "");
}
+ }
- return gcry_error(GPG_ERR_NO_ERROR);
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Fetch the private key from the given OtrlUserState associated with
@@ -625,42 +817,42 @@ gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us,
OtrlPrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname,
const char *protocol)
{
- OtrlPrivKey *p;
- if (!accountname || !protocol) return NULL;
+ OtrlPrivKey *p;
+ if (!accountname || !protocol) return NULL;
- for(p=us->privkey_root; p; p=p->next) {
+ for(p=us->privkey_root; p; p=p->next) {
if (!strcmp(p->accountname, accountname) &&
!strcmp(p->protocol, protocol)) {
- return p;
+ return p;
}
- }
- return NULL;
+ }
+ return NULL;
}
/* Forget a private key */
void otrl_privkey_forget(OtrlPrivKey *privkey)
{
- free(privkey->accountname);
- free(privkey->protocol);
- gcry_sexp_release(privkey->privkey);
- free(privkey->pubkey_data);
-
- /* Re-link the list */
- *(privkey->tous) = privkey->next;
- if (privkey->next) {
+ free(privkey->accountname);
+ free(privkey->protocol);
+ gcry_sexp_release(privkey->privkey);
+ free(privkey->pubkey_data);
+
+ /* Re-link the list */
+ *(privkey->tous) = privkey->next;
+ if (privkey->next) {
privkey->next->tous = privkey->tous;
- }
+ }
- /* Free the privkey struct */
- free(privkey);
+ /* Free the privkey struct */
+ free(privkey);
}
/* Forget all private keys in a given OtrlUserState. */
void otrl_privkey_forget_all(OtrlUserState us)
{
- while (us->privkey_root) {
+ while (us->privkey_root) {
otrl_privkey_forget(us->privkey_root);
- }
+ }
}
/* Sign data using a private key. The data must be small enough to be
@@ -670,45 +862,45 @@ void otrl_privkey_forget_all(OtrlUserState us)
gcry_error_t otrl_privkey_sign(unsigned char **sigp, size_t *siglenp,
OtrlPrivKey *privkey, const unsigned char *data, size_t len)
{
- gcry_mpi_t r,s, datampi;
- gcry_sexp_t dsas, rs, ss, sigs, datas;
- size_t nr, ns;
- const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ gcry_mpi_t r,s, datampi;
+ gcry_sexp_t dsas, rs, ss, sigs, datas;
+ size_t nr, ns;
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
- if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA)
+ if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA)
return gcry_error(GPG_ERR_INV_VALUE);
- *sigp = malloc(40);
- if (sigp == NULL) return gcry_error(GPG_ERR_ENOMEM);
- *siglenp = 40;
+ *sigp = malloc(40);
+ if (*sigp == NULL) return gcry_error(GPG_ERR_ENOMEM);
+ *siglenp = 40;
- if (len) {
+ if (len) {
gcry_mpi_scan(&datampi, GCRYMPI_FMT_USG, data, len, NULL);
- } else {
+ } else {
datampi = gcry_mpi_set_ui(NULL, 0);
- }
- gcry_sexp_build(&datas, NULL, "(%m)", datampi);
- gcry_mpi_release(datampi);
- gcry_pk_sign(&sigs, datas, privkey->privkey);
- gcry_sexp_release(datas);
- dsas = gcry_sexp_find_token(sigs, "dsa", 0);
- gcry_sexp_release(sigs);
- rs = gcry_sexp_find_token(dsas, "r", 0);
- ss = gcry_sexp_find_token(dsas, "s", 0);
- gcry_sexp_release(dsas);
- r = gcry_sexp_nth_mpi(rs, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(rs);
- s = gcry_sexp_nth_mpi(ss, 1, GCRYMPI_FMT_USG);
- gcry_sexp_release(ss);
- gcry_mpi_print(format, NULL, 0, &nr, r);
- gcry_mpi_print(format, NULL, 0, &ns, s);
- memset(*sigp, 0, 40);
- gcry_mpi_print(format, (*sigp)+(20-nr), nr, NULL, r);
- gcry_mpi_print(format, (*sigp)+20+(20-ns), ns, NULL, s);
- gcry_mpi_release(r);
- gcry_mpi_release(s);
-
- return gcry_error(GPG_ERR_NO_ERROR);
+ }
+ gcry_sexp_build(&datas, NULL, "(%m)", datampi);
+ gcry_mpi_release(datampi);
+ gcry_pk_sign(&sigs, datas, privkey->privkey);
+ gcry_sexp_release(datas);
+ dsas = gcry_sexp_find_token(sigs, "dsa", 0);
+ gcry_sexp_release(sigs);
+ rs = gcry_sexp_find_token(dsas, "r", 0);
+ ss = gcry_sexp_find_token(dsas, "s", 0);
+ gcry_sexp_release(dsas);
+ r = gcry_sexp_nth_mpi(rs, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(rs);
+ s = gcry_sexp_nth_mpi(ss, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(ss);
+ gcry_mpi_print(format, NULL, 0, &nr, r);
+ gcry_mpi_print(format, NULL, 0, &ns, s);
+ memset(*sigp, 0, 40);
+ gcry_mpi_print(format, (*sigp)+(20-nr), nr, NULL, r);
+ gcry_mpi_print(format, (*sigp)+20+(20-ns), ns, NULL, s);
+ gcry_mpi_release(r);
+ gcry_mpi_release(s);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Verify a signature on data using a public key. The data must be
@@ -717,33 +909,30 @@ gcry_error_t otrl_privkey_verify(const unsigned char *sigbuf, size_t siglen,
unsigned short pubkey_type, gcry_sexp_t pubs,
const unsigned char *data, size_t len)
{
- gcry_error_t err;
- gcry_mpi_t datampi,r,s;
- gcry_sexp_t datas, sigs;
+ gcry_error_t err;
+ gcry_mpi_t datampi,r,s;
+ gcry_sexp_t datas, sigs;
- if (pubkey_type != OTRL_PUBKEY_TYPE_DSA || siglen != 40)
+ if (pubkey_type != OTRL_PUBKEY_TYPE_DSA || siglen != 40)
return gcry_error(GPG_ERR_INV_VALUE);
- if (len) {
+ if (len) {
gcry_mpi_scan(&datampi, GCRYMPI_FMT_USG, data, len, NULL);
- } else {
+ } else {
datampi = gcry_mpi_set_ui(NULL, 0);
- }
- gcry_sexp_build(&datas, NULL, "(%m)", datampi);
- gcry_mpi_release(datampi);
- gcry_mpi_scan(&r, GCRYMPI_FMT_USG, sigbuf, 20, NULL);
- gcry_mpi_scan(&s, GCRYMPI_FMT_USG, sigbuf+20, 20, NULL);
- gcry_sexp_build(&sigs, NULL, "(sig-val (dsa (r %m)(s %m)))", r, s);
- gcry_mpi_release(r);
- gcry_mpi_release(s);
-
- err = gcry_pk_verify(sigs, datas, pubs);
- gcry_sexp_release(datas);
- gcry_sexp_release(sigs);
-
- return err;
+ }
+ gcry_sexp_build(&datas, NULL, "(%m)", datampi);
+ gcry_mpi_release(datampi);
+ gcry_mpi_scan(&r, GCRYMPI_FMT_USG, sigbuf, 20, NULL);
+ gcry_mpi_scan(&s, GCRYMPI_FMT_USG, sigbuf+20, 20, NULL);
+ gcry_sexp_build(&sigs, NULL, "(sig-val (dsa (r %m)(s %m)))", r, s);
+ gcry_mpi_release(r);
+ gcry_mpi_release(s);
+
+ err = gcry_pk_verify(sigs, datas, pubs);
+ gcry_sexp_release(datas);
+ gcry_sexp_release(sigs);
+
+ return err;
}
-gcry_error_t otrl_account_write(FILE *privf, const char *accountname,
- const char *protocol, gcry_sexp_t privkey) {
- return account_write(privf, accountname, protocol, privkey);
-} \ No newline at end of file
+
diff --git a/plugins/MirOTR/libotr/src/privkey.h b/plugins/MirOTR/libotr/src/privkey.h
index 1208f54993..3b2c1735e3 100644
--- a/plugins/MirOTR/libotr/src/privkey.h
+++ b/plugins/MirOTR/libotr/src/privkey.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __PRIVKEY_H__
@@ -24,16 +25,20 @@
#include "privkey-t.h"
#include "userstate.h"
-gcry_error_t otrl_account_write(FILE *privf, const char *accountname,
- const char *protocol, gcry_sexp_t privkey);
+/* The length of a string representing a human-readable version of a
+ * fingerprint (including the trailing NUL) */
+#define OTRL_PRIVKEY_FPRINT_HUMAN_LEN 45
/* Convert a 20-byte hash value to a 45-byte human-readable value */
-void otrl_privkey_hash_to_human(char human[45], const unsigned char hash[20]);
+void otrl_privkey_hash_to_human(
+ char human[OTRL_PRIVKEY_FPRINT_HUMAN_LEN],
+ const unsigned char hash[20]);
/* Calculate a human-readable hash of our DSA public key. Return it in
* the passed fingerprint buffer. Return NULL on error, or a pointer to
* the given buffer on success. */
-char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
+char *otrl_privkey_fingerprint(OtrlUserState us,
+ char fingerprint[OTRL_PRIVKEY_FPRINT_HUMAN_LEN],
const char *accountname, const char *protocol);
/* Calculate a raw hash of our DSA public key. Return it in the passed
@@ -50,6 +55,40 @@ gcry_error_t otrl_privkey_read(OtrlUserState us, const char *filename);
* OtrlUserState. The FILE* must be open for reading. */
gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf);
+/* Free the memory associated with the pending privkey list */
+void otrl_privkey_pending_forget_all(OtrlUserState us);
+
+/* Begin a private key generation that will potentially take place in
+ * a background thread. This routine must be called from the main
+ * thread. It will set *newkeyp, which you can pass to
+ * otrl_privkey_generate_calculate in a background thread. If it
+ * returns gcry_error(GPG_ERR_EEXIST), then a privkey creation for
+ * this accountname/protocol is already in progress, and *newkeyp will
+ * be set to NULL. */
+gcry_error_t otrl_privkey_generate_start(OtrlUserState us,
+ const char *accountname, const char *protocol, void **newkeyp);
+
+/* Do the private key generation calculation. You may call this from a
+ * background thread. When it completes, call
+ * otrl_privkey_generate_finish from the _main_ thread. */
+gcry_error_t otrl_privkey_generate_calculate(void *newkey);
+
+/* Call this from the main thread only. It will write the newly created
+ * private key into the given file and store it in the OtrlUserState. */
+gcry_error_t otrl_privkey_generate_finish(OtrlUserState us,
+ void *newkey, const char *filename);
+
+/* Call this from the main thread only. It will write the newly created
+ * private key into the given FILE* (which must be open for reading and
+ * writing) and store it in the OtrlUserState. */
+gcry_error_t otrl_privkey_generate_finish_FILEp(OtrlUserState us,
+ void *newkey, FILE *privf);
+
+/* 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_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
* previously generated keys for that account in that OtrlUserState. */
diff --git a/plugins/MirOTR/libotr/src/proto.c b/plugins/MirOTR/libotr/src/proto.c
index 307ab047ff..22e50ebb20 100644
--- a/plugins/MirOTR/libotr/src/proto.c
+++ b/plugins/MirOTR/libotr/src/proto.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +16,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* OTR Protocol implementation. This file should be independent of
@@ -37,13 +39,19 @@
#include "tlv.h"
#include "serial.h"
+#define snprintf _snprintf /* Miranda NG modification */
+
+#if OTRL_DEBUGGING
+extern const char *OTRL_DEBUGGING_DEBUGSTR;
+#endif
+
/* For now, we need to know the API version the client is using so that
* we don't use any UI callbacks it hasn't set. */
unsigned int otrl_api_version = 0;
/* Initialize the OTR library. Pass the version of the API you are
* using. */
-void otrl_init(unsigned int ver_major, unsigned int ver_minor,
+gcry_error_t otrl_init(unsigned int ver_major, unsigned int ver_minor,
unsigned int ver_sub)
{
unsigned int api_version;
@@ -55,7 +63,7 @@ void otrl_init(unsigned int ver_major, unsigned int ver_minor,
"with actual version %u.%u.%u. Aborting.\n",
ver_major, ver_minor, ver_sub,
OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, OTRL_VERSION_SUB);
- exit(1);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Set the API version. If we get called multiple times for some
@@ -73,6 +81,14 @@ void otrl_init(unsigned int ver_major, unsigned int ver_minor,
/* Initialize the SM module */
otrl_sm_init();
+
+#if OTRL_DEBUGGING
+ /* Inform the user that debugging is available */
+ fprintf(stderr, "\nlibotr debugging is available. Type %s in a message\n"
+ " to see debug info.\n\n", OTRL_DEBUGGING_DEBUGSTR);
+#endif
+
+ return gcry_error(GPG_ERR_NO_ERROR);
}
/* Return a pointer to a static string containing the version number of
@@ -90,33 +106,37 @@ static gcry_error_t reveal_macs(ConnContext *context,
sess2->rcvmacused + sess2->sendmacused;
unsigned int newnumsaved;
unsigned char *newmacs;
-
+
/* Is there anything to do? */
if (numnew == 0) return gcry_error(GPG_ERR_NO_ERROR);
- newnumsaved = context->numsavedkeys + numnew;
- newmacs = realloc(context->saved_mac_keys,
+ newnumsaved = context->context_priv->numsavedkeys + numnew;
+ newmacs = realloc(context->context_priv->saved_mac_keys,
newnumsaved * 20);
if (!newmacs) {
return gcry_error(GPG_ERR_ENOMEM);
}
if (sess1->rcvmacused) {
- memmove(newmacs + context->numsavedkeys * 20, sess1->rcvmackey, 20);
- context->numsavedkeys++;
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess1->rcvmackey, 20);
+ context->context_priv->numsavedkeys++;
}
if (sess1->sendmacused) {
- memmove(newmacs + context->numsavedkeys * 20, sess1->sendmackey, 20);
- context->numsavedkeys++;
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess1->sendmackey, 20);
+ context->context_priv->numsavedkeys++;
}
if (sess2->rcvmacused) {
- memmove(newmacs + context->numsavedkeys * 20, sess2->rcvmackey, 20);
- context->numsavedkeys++;
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess2->rcvmackey, 20);
+ context->context_priv->numsavedkeys++;
}
if (sess2->sendmacused) {
- memmove(newmacs + context->numsavedkeys * 20, sess2->sendmackey, 20);
- context->numsavedkeys++;
+ memmove(newmacs + context->context_priv->numsavedkeys * 20,
+ sess2->sendmackey, 20);
+ context->context_priv->numsavedkeys++;
}
- context->saved_mac_keys = newmacs;
+ context->context_priv->saved_mac_keys = newmacs;
return gcry_error(GPG_ERR_NO_ERROR);
}
@@ -128,39 +148,44 @@ static gcry_error_t rotate_dh_keys(ConnContext *context)
gcry_error_t err;
/* Rotate the keypair */
- otrl_dh_keypair_free(&(context->our_old_dh_key));
- memmove(&(context->our_old_dh_key), &(context->our_dh_key),
+ 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));
/* Rotate the session keys */
- err = reveal_macs(context, &(context->sesskeys[1][0]),
- &(context->sesskeys[1][1]));
+ err = reveal_macs(context, &(context->context_priv->sesskeys[1][0]),
+ &(context->context_priv->sesskeys[1][1]));
if (err) return err;
- otrl_dh_session_free(&(context->sesskeys[1][0]));
- otrl_dh_session_free(&(context->sesskeys[1][1]));
- memmove(&(context->sesskeys[1][0]), &(context->sesskeys[0][0]),
+ 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));
- memmove(&(context->sesskeys[1][1]), &(context->sesskeys[0][1]),
+ memmove(&(context->context_priv->sesskeys[1][1]),
+ &(context->context_priv->sesskeys[0][1]),
sizeof(DH_sesskeys));
/* Create a new DH key */
- otrl_dh_gen_keypair(DH1536_GROUP_ID, &(context->our_dh_key));
- context->our_keyid++;
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(context->context_priv->our_dh_key));
+ context->context_priv->our_keyid++;
/* Make the session keys */
- if (context->their_y) {
- err = otrl_dh_session(&(context->sesskeys[0][0]),
- &(context->our_dh_key), context->their_y);
+ 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);
if (err) return err;
} else {
- otrl_dh_session_blank(&(context->sesskeys[0][0]));
+ otrl_dh_session_blank(&(context->context_priv->sesskeys[0][0]));
}
- if (context->their_old_y) {
- err = otrl_dh_session(&(context->sesskeys[0][1]),
- &(context->our_dh_key), context->their_old_y);
+ if (context->context_priv->their_old_y) {
+ err = otrl_dh_session(&(context->context_priv->sesskeys[0][1]),
+ &(context->context_priv->our_dh_key),
+ context->context_priv->their_old_y);
if (err) return err;
} else {
- otrl_dh_session_blank(&(context->sesskeys[0][1]));
+ otrl_dh_session_blank(&(context->context_priv->sesskeys[0][1]));
}
return gcry_error(GPG_ERR_NO_ERROR);
}
@@ -172,30 +197,34 @@ static gcry_error_t rotate_y_keys(ConnContext *context, gcry_mpi_t new_y)
gcry_error_t err;
/* Rotate the public key */
- gcry_mpi_release(context->their_old_y);
- context->their_old_y = context->their_y;
+ gcry_mpi_release(context->context_priv->their_old_y);
+ context->context_priv->their_old_y = context->context_priv->their_y;
/* Rotate the session keys */
- err = reveal_macs(context, &(context->sesskeys[0][1]),
- &(context->sesskeys[1][1]));
+ err = reveal_macs(context, &(context->context_priv->sesskeys[0][1]),
+ &(context->context_priv->sesskeys[1][1]));
if (err) return err;
- otrl_dh_session_free(&(context->sesskeys[0][1]));
- otrl_dh_session_free(&(context->sesskeys[1][1]));
- memmove(&(context->sesskeys[0][1]), &(context->sesskeys[0][0]),
+ 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));
- memmove(&(context->sesskeys[1][1]), &(context->sesskeys[1][0]),
+ memmove(&(context->context_priv->sesskeys[1][1]),
+ &(context->context_priv->sesskeys[1][0]),
sizeof(DH_sesskeys));
/* Copy in the new public key */
- context->their_y = gcry_mpi_copy(new_y);
- context->their_keyid++;
+ context->context_priv->their_y = gcry_mpi_copy(new_y);
+ context->context_priv->their_keyid++;
/* Make the session keys */
- err = otrl_dh_session(&(context->sesskeys[0][0]),
- &(context->our_dh_key), context->their_y);
+ err = otrl_dh_session(&(context->context_priv->sesskeys[0][0]),
+ &(context->context_priv->our_dh_key),
+ context->context_priv->their_y);
if (err) return err;
- err = otrl_dh_session(&(context->sesskeys[1][0]),
- &(context->our_old_dh_key), context->their_y);
+ err = otrl_dh_session(&(context->context_priv->sesskeys[1][0]),
+ &(context->context_priv->our_old_dh_key),
+ context->context_priv->their_y);
if (err) return err;
return gcry_error(GPG_ERR_NO_ERROR);
@@ -207,53 +236,72 @@ 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
* get passed to the main IM application for processing (and
* free()ing). */
const char *format = "?OTR%s\n<b>%s</b> has requested an "
- "<a href=\"http://otr.cypherpunks.ca/\">Off-the-Record "
+ "<a href=\"https://otr.cypherpunks.ca/\">Off-the-Record "
"private conversation</a>. However, you do not have a plugin "
- "to support that.\nSee <a href=\"http://otr.cypherpunks.ca/\">"
- "http://otr.cypherpunks.ca/</a> for more information.";
+ "to support that.\nSee <a href=\"https://otr.cypherpunks.ca/\">"
+ "https://otr.cypherpunks.ca/</a> for more information.";
/* 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;
- otrtag = strstr(querymsg, "?OTR");
+
+ otrtag = strstr(otrquerymsg, "?OTR");
+ if (!otrtag) {
+ return 0;
+ }
otrtag += 4;
+
if (*otrtag == '?') {
query_versions = (1<<0);
++otrtag;
@@ -264,10 +312,16 @@ unsigned int otrl_proto_query_bestversion(const char *querymsg,
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;
}
@@ -311,6 +365,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;
@@ -320,6 +377,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;
}
@@ -329,7 +389,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;
@@ -344,25 +404,83 @@ 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 = gcry_error(GPG_ERR_NO_ERROR);
+
+ const char *otrtag = otrmsg;
+ unsigned char *bufp = NULL;
+ unsigned char *bufp_head = NULL;
+ size_t lenp;
+
+ if (!otrtag || strncmp(otrtag, "?OTR:AAM", 8)) {
+ goto invval;
+ }
+
+ if (strlen(otrtag) < 21 ) goto invval;
+
+ /* Decode and extract instance tag */
+ bufp = malloc(OTRL_B64_MAX_DECODED_SIZE(12));
+ 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:
+ free(bufp_head);
+ 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. */
+ * *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)
+ const char *msg, const OtrlTLV *tlvs, unsigned char flags,
+ unsigned char *extrakey)
{
size_t justmsglen = strlen(msg);
size_t msglen = justmsglen + 1 + otrl_tlv_seriallen(tlvs);
@@ -371,10 +489,9 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
unsigned char *buf = NULL;
unsigned char *bufp;
size_t lenp;
- DH_sesskeys *sess = &(context->sesskeys[1][0]);
+ DH_sesskeys *sess = &(context->context_priv->sesskeys[1][0]);
gcry_error_t err;
- size_t reveallen = 20 * context->numsavedkeys;
- size_t base64len;
+ size_t reveallen = 20 * context->context_priv->numsavedkeys;
char *base64buf = NULL;
unsigned char *msgbuf = NULL;
enum gcry_mpi_format format = GCRYMPI_FMT_USG;
@@ -383,7 +500,7 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
/* Make sure we're actually supposed to be able to encrypt */
if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED ||
- context->their_keyid == 0) {
+ context->context_priv->their_keyid == 0) {
return gcry_error(GPG_ERR_CONFLICT);
}
@@ -397,11 +514,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;
- gcry_mpi_print(format, NULL, 0, &pubkeylen, context->our_dh_key.pub);
+ 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);
buflen += pubkeylen + 4;
buf = malloc(buflen);
msgbuf = gcry_malloc_secure(msglen);
@@ -417,25 +536,38 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
bufp = buf;
lenp = buflen;
if (version == 1) {
- memcpy(bufp, "\x00\x01\x03", 3); /* header */
+ memmove(bufp, "\x00\x01\x03", 3); /* header */
+ } else if (version == 2) {
+ memmove(bufp, "\x00\x02\x03", 3); /* header */
} else {
- memcpy(bufp, "\x00\x02\x03", 3); /* header */
+ 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->our_keyid-1); /* sender keyid */
+
+ write_int(context->context_priv->our_keyid-1); /* sender keyid */
debug_int("Sender keyid", bufp-4);
- write_int(context->their_keyid); /* recipient keyid */
+ write_int(context->context_priv->their_keyid); /* recipient keyid */
debug_int("Recipient keyid", bufp-4);
- write_mpi(context->our_dh_key.pub, pubkeylen, "Y"); /* Y */
+ write_mpi(context->context_priv->our_dh_key.pub, pubkeylen, "Y"); /* Y */
otrl_dh_incctr(sess->sendctr);
- memcpy(bufp, sess->sendctr, 8); /* Counter (top 8 bytes only) */
+ memmove(bufp, sess->sendctr, 8); /* Counter (top 8 bytes only) */
debug_data("Counter", bufp, 8);
bufp += 8; lenp -= 8;
@@ -454,7 +586,7 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
gcry_md_reset(sess->sendmac);
gcry_md_write(sess->sendmac, buf, bufp-buf);
- memcpy(bufp, gcry_md_read(sess->sendmac, GCRY_MD_SHA1), 20);
+ memmove(bufp, gcry_md_read(sess->sendmac, GCRY_MD_SHA1), 20);
debug_data("MAC", bufp, 20);
bufp += 20; /* MAC */
lenp -= 20;
@@ -463,49 +595,42 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
debug_int("Revealed MAC length", bufp-4);
if (reveallen > 0) {
- memcpy(bufp, context->saved_mac_keys, reveallen);
+ memmove(bufp, context->context_priv->saved_mac_keys, reveallen);
debug_data("Revealed MAC data", bufp, reveallen);
bufp += reveallen; lenp -= reveallen;
- free(context->saved_mac_keys);
- context->saved_mac_keys = NULL;
- context->numsavedkeys = 0;
+ free(context->context_priv->saved_mac_keys);
+ context->context_priv->saved_mac_keys = NULL;
+ context->context_priv->numsavedkeys = 0;
}
assert(lenp == 0);
/* Make the base64-encoding. */
- base64len = ((buflen + 2) / 3) * 4;
- base64buf = malloc(5 + base64len + 1 + 1);
+ base64buf = otrl_base64_otr_encode(buf, buflen);
if (base64buf == NULL) {
err = gcry_error(GPG_ERR_ENOMEM);
goto err;
}
- memcpy(base64buf, "?OTR:", 5);
- otrl_base64_encode(base64buf+5, buf, buflen);
- base64buf[5 + base64len] = '.';
- base64buf[5 + base64len + 1] = '\0';
free(buf);
gcry_free(msgbuf);
*encmessagep = base64buf;
- gcry_free(context->lastmessage);
- context->lastmessage = NULL;
- context->may_retransmit = 0;
+ gcry_free(context->context_priv->lastmessage);
+ context->context_priv->lastmessage = NULL;
+ context->context_priv->may_retransmit = 0;
if (msglen > 0) {
- const char *prefix = "[resent] ";
- size_t prefixlen = strlen(prefix);
- if (!strncmp(prefix, msgdup, prefixlen)) {
- /* The prefix is already there. Don't add it again. */
- prefix = "";
- prefixlen = 0;
- }
- context->lastmessage = gcry_malloc_secure(prefixlen + justmsglen + 1);
- if (context->lastmessage) {
- strcpy(context->lastmessage, prefix);
- strcat(context->lastmessage, msgdup);
+ context->context_priv->lastmessage = gcry_malloc_secure(justmsglen + 1);
+ if (context->context_priv->lastmessage) {
+ strcpy(context->context_priv->lastmessage, msgdup);
}
}
gcry_free(msgdup);
+
+ /* Save a copy of the current extra key */
+ if (extrakey) {
+ memmove(extrakey, sess->extrakey, OTRL_EXTRAKEY_BYTES);
+ }
+
return gcry_error(GPG_ERR_NO_ERROR);
err:
free(buf);
@@ -537,26 +662,31 @@ gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
msglen = strlen(otrtag);
}
+ /* Skip over the "?OTR:" */
+ otrtag += 5;
+ msglen -= 5;
+
/* Base64-decode the message */
- rawlen = ((msglen-5) / 4) * 3; /* maximum possible */
+ rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen); /* maximum possible */
rawmsg = malloc(rawlen);
if (!rawmsg && rawlen > 0) {
return gcry_error(GPG_ERR_ENOMEM);
}
- rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
+ rawlen = otrl_base64_decode(rawmsg, otrtag, msglen); /* actual size */
bufp = rawmsg;
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;
@@ -572,9 +702,11 @@ invval:
/* Accept an OTR Data Message in datamsg. Decrypt it and put the
* plaintext into *plaintextp, and any TLVs into tlvsp. Put any
- * received flags into *flagsp (if non-NULL). */
+ * received flags into *flagsp (if non-NULL). Put the current extra
+ * symmetric key into extrakey (if non-NULL). */
gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
- ConnContext *context, const char *datamsg, unsigned char *flagsp)
+ ConnContext *context, const char *datamsg, unsigned char *flagsp,
+ unsigned char *extrakey)
{
char *otrtag, *endtag;
gcry_error_t err;
@@ -606,37 +738,44 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
msglen = strlen(otrtag);
}
+ /* Skip over the "?OTR:" */
+ otrtag += 5;
+ msglen -= 5;
+
/* Base64-decode the message */
- rawlen = ((msglen-5) / 4) * 3; /* maximum possible */
+ rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen); /* maximum possible */
rawmsg = malloc(rawlen);
if (!rawmsg && rawlen > 0) {
err = gcry_error(GPG_ERR_ENOMEM);
goto err;
}
- rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
+ rawlen = otrl_base64_decode(rawmsg, otrtag, msglen); /* actual size */
bufp = rawmsg;
lenp = rawlen;
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);
require_len(8);
- memcpy(ctr, bufp, 8);
+ memmove(ctr, bufp, 8);
bufp += 8; lenp -= 8;
read_int(datalen);
require_len(datalen);
@@ -645,12 +784,12 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
err = gcry_error(GPG_ERR_ENOMEM);
goto err;
}
- memcpy(data, bufp, datalen);
+ memmove(data, bufp, datalen);
data[datalen] = '\0';
bufp += datalen; lenp -= datalen;
macend = bufp;
require_len(20);
- memcpy(givenmac, bufp, 20);
+ memmove(givenmac, bufp, 20);
bufp += 20; lenp -= 20;
read_int(reveallen);
require_len(reveallen);
@@ -664,28 +803,29 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
/* We don't take any action on this message (especially rotating
* keys) until we've verified the MAC on this message. To that end,
* we need to know which keys this message is claiming to use. */
- if (context->their_keyid == 0 ||
- (sender_keyid != context->their_keyid &&
- sender_keyid != context->their_keyid - 1) ||
- (recipient_keyid != context->our_keyid &&
- recipient_keyid != context->our_keyid - 1) ||
+ if (context->context_priv->their_keyid == 0 ||
+ (sender_keyid != context->context_priv->their_keyid &&
+ sender_keyid != context->context_priv->their_keyid - 1) ||
+ (recipient_keyid != context->context_priv->our_keyid &&
+ recipient_keyid != context->context_priv->our_keyid - 1) ||
sender_keyid == 0 || recipient_keyid == 0) {
goto conflict;
}
- if (sender_keyid == context->their_keyid - 1 &&
- context->their_old_y == NULL) {
+ if (sender_keyid == context->context_priv->their_keyid - 1 &&
+ context->context_priv->their_old_y == NULL) {
goto conflict;
}
/* These are the session keys this message is claiming to use. */
- sess = &(context->sesskeys
- [context->our_keyid - recipient_keyid]
- [context->their_keyid - sender_keyid]);
+ sess = &(context->context_priv->sesskeys
+ [context->context_priv->our_keyid - recipient_keyid]
+ [context->context_priv->their_keyid - sender_keyid]);
gcry_md_reset(sess->rcvmac);
gcry_md_write(sess->rcvmac, macstart, macend-macstart);
- if (memcmp(givenmac, gcry_md_read(sess->rcvmac, GCRY_MD_SHA1), 20)) {
+ if (otrl_mem_differ(givenmac, gcry_md_read(sess->rcvmac, GCRY_MD_SHA1),
+ 20)) {
/* The MACs didn't match! */
goto conflict;
}
@@ -698,7 +838,7 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
}
/* Decrypt the message */
- memcpy(sess->rcvctr, ctr, 8);
+ memmove(sess->rcvctr, ctr, 8);
err = gcry_cipher_reset(sess->rcvenc);
if (err) goto err;
err = gcry_cipher_setctr(sess->rcvenc, sess->rcvctr, 16);
@@ -706,15 +846,20 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
err = gcry_cipher_decrypt(sess->rcvenc, data, datalen, NULL, 0);
if (err) goto err;
+ /* Save a copy of the current extra key */
+ if (extrakey) {
+ memmove(extrakey, sess->extrakey, OTRL_EXTRAKEY_BYTES);
+ }
+
/* See if either set of keys needs rotating */
- if (recipient_keyid == context->our_keyid) {
+ if (recipient_keyid == context->context_priv->our_keyid) {
/* They're using our most recent key, so generate a new one */
err = rotate_dh_keys(context);
if (err) goto err;
}
- if (sender_keyid == context->their_keyid) {
+ if (sender_keyid == context->context_priv->their_keyid) {
/* They've sent us a new public key */
err = rotate_y_keys(context, sender_next_y);
if (err) goto err;
@@ -752,77 +897,90 @@ 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,")) != NULL) {
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;
- free(context->fragment);
- context->fragment = malloc(fraglen + 1);
- if (fraglen + 1 > fraglen && context->fragment) {
- memmove(context->fragment, tag + start, fraglen);
- context->fragment_len = fraglen;
- context->fragment[context->fragment_len] = '\0';
- context->fragment_n = n;
- context->fragment_k = k;
- } else {
- free(context->fragment);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
- }
- } else if (n == context->fragment_n &&
- k == context->fragment_k + 1) {
- int fraglen = end - start - 1;
- char *newfrag = realloc(context->fragment,
- context->fragment_len + fraglen + 1);
- if (context->fragment_len + fraglen + 1 > fraglen && newfrag) {
- context->fragment = newfrag;
- memmove(context->fragment + context->fragment_len,
- tag + start, fraglen);
- context->fragment_len += fraglen;
- context->fragment[context->fragment_len] = '\0';
- context->fragment_k = k;
- } else {
- free(context->fragment);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->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 >= 1) { /* 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 {
- free(context->fragment);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
+ 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;
+ /* Check for overflow */
+ if (newsize > context->context_priv->fragment_len) {
+ 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 {
+ 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;
}
+ }
- if (context->fragment_n > 0 &&
- context->fragment_n == context->fragment_k) {
- /* We've got a complete message */
- *unfragmessagep = context->fragment;
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
- res = OTRL_FRAGMENT_COMPLETE;
- }
- } else {
- /* Unfragmented message, so discard any fragment we may have */
- free(context->fragment);
- context->fragment = NULL;
- context->fragment_len = 0;
- context->fragment_n = 0;
- context->fragment_k = 0;
- res = OTRL_FRAGMENT_UNFRAGMENTED;
+ 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_COMPLETE;
}
return res;
@@ -830,60 +988,79 @@ 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 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;
- char **fragmentarray = malloc(fragment_count * sizeof(char*));
+ if (fragment_count < 1 || fragment_count > 65535) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ fragmentarray = malloc(fragment_count * sizeof(char*));
if(!fragmentarray) return gcry_error(GPG_ERR_ENOMEM);
-
+
/*
* Find the next message fragment and store it in the array.
*/
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);
- }
-
- /*
- * 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;
-
- fragmentarray[curfrag-1] = fragmentmsg;
-
- free(fragdata);
- index += fragdatalen;
+ free(fragmentarray);
+ free(fragdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /*
+ * Create the actual fragment and store it in the array
+ */
+ if (context->auth.protocol_version != 3) {
+ snprintf(fragmentmsg, fragdatalen + headerlen,
+ "?OTR,%05hu,%05hu,%s,", (unsigned short)curfrag,
+ (unsigned short)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,
+ (unsigned short)curfrag, (unsigned short)fragment_count,
+ fragdata);
+ }
+ fragmentmsg[fragdatalen + headerlen] = 0;
+
+ fragmentarray[curfrag-1] = fragmentmsg;
+
+ free(fragdata);
+ index += fragdatalen;
message += fragdatalen;
}
-
+
*fragments = fragmentarray;
return gcry_error(GPG_ERR_NO_ERROR);
}
@@ -897,7 +1074,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/plugins/MirOTR/libotr/src/proto.h b/plugins/MirOTR/libotr/src/proto.h
index d7b0ae6f90..28be83f439 100644
--- a/plugins/MirOTR/libotr/src/proto.h
+++ b/plugins/MirOTR/libotr/src/proto.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __PROTO_H__
@@ -30,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
@@ -38,27 +40,33 @@ 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)
-/* For v1 compatibility */
+/* Length of OTR message headers */
+#define OTRL_HEADER_LEN 3
+#define OTRL_B64_HEADER_LEN 4
+
+/* Analogous to v1 policies */
#define OTRL_POLICY_NEVER 0x00
#define OTRL_POLICY_OPPORTUNISTIC \
- ( OTRL_POLICY_ALLOW_V1 | \
- OTRL_POLICY_ALLOW_V2 | \
+ ( 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_V2 | \
+ OTRL_POLICY_ALLOW_V3 | \
OTRL_POLICY_REQUIRE_ENCRYPTION | \
OTRL_POLICY_WHITESPACE_START_AKE | \
OTRL_POLICY_ERROR_START_AKE )
@@ -85,6 +93,8 @@ 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
@@ -92,12 +102,15 @@ typedef enum {
/* Initialize the OTR library. Pass the version of the API you are
* using. */
-void otrl_init(unsigned int ver_major, unsigned int ver_minor,
+gcry_error_t otrl_init(unsigned int ver_major, unsigned int ver_minor,
unsigned int ver_sub);
/* Shortcut */
#define OTRL_INIT do { \
- otrl_init(OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, OTRL_VERSION_SUB); \
+ if (otrl_init(OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, \
+ OTRL_VERSION_SUB)) { \
+ exit(1); \
+ } \
} while(0)
/* Return a pointer to a static string containing the version number of
@@ -120,14 +133,23 @@ 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);
+ const char *msg, const OtrlTLV *tlvs, unsigned char flags,
+ unsigned char *extrakey);
/* Extract the flags from an otherwise unreadable Data Message. */
gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
@@ -135,16 +157,18 @@ gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
/* Accept an OTR Data Message in datamsg. Decrypt it and put the
* plaintext into *plaintextp, and any TLVs into tlvsp. Put any
- * received flags into *flagsp (if non-NULL). */
+ * received flags into *flagsp (if non-NULL). Put the current extra
+ * symmetric key into extrakey (if non-NULL). */
gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
- ConnContext *context, const char *datamsg, unsigned char *flagsp);
+ ConnContext *context, const char *datamsg, unsigned char *flagsp,
+ unsigned char *extrakey);
/* Accumulate a potential fragment into the current context. */
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/plugins/MirOTR/libotr/src/serial.h b/plugins/MirOTR/libotr/src/serial.h
index edc31847e1..cd2442b332 100644
--- a/plugins/MirOTR/libotr/src/serial.h
+++ b/plugins/MirOTR/libotr/src/serial.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __SERIAL_H__
@@ -36,7 +37,7 @@
#define debug_int(t,b) do { const unsigned char *data = (b); \
unsigned int v = \
- (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \
+ (((unsigned int)data[0]) << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \
fprintf(stderr, "%s: %u (0x%x)\n", (t), v, v); \
} while(0)
@@ -66,7 +67,7 @@
#define read_int(x) do { \
require_len(4); \
- (x) = (bufp[0] << 24) | (bufp[1] << 16) | (bufp[2] << 8) | bufp[3]; \
+ (x) = (((unsigned int)bufp[0]) << 24) | (bufp[1] << 16) | (bufp[2] << 8) | bufp[3]; \
bufp += 4; lenp -= 4; \
} while(0)
@@ -82,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/plugins/MirOTR/libotr/src/sm.c b/plugins/MirOTR/libotr/src/sm.c
index 535ae2243c..4fb679ea81 100644
--- a/plugins/MirOTR/libotr/src/sm.c
+++ b/plugins/MirOTR/libotr/src/sm.c
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,13 +16,13 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
#include <stdlib.h>
#include <stdio.h>
-#include <sys/types.h>
+#include <sys/types.h>
/* libgcrypt headers */
#include <gcrypt.h>
@@ -29,6 +31,30 @@
#include "sm.h"
#include "serial.h"
+#if OTRL_DEBUGGING
+
+/* Dump the contents of an SMState to the FILE *f. */
+void otrl_sm_dump(FILE *f, const OtrlSMState *sm)
+{
+ fprintf(f, " SM state:\n");
+ fprintf(f, " Next expected: %d (%s)\n", sm->nextExpected,
+ sm->nextExpected == OTRL_SMP_EXPECT1 ? "EXPECT1" :
+ sm->nextExpected == OTRL_SMP_EXPECT2 ? "EXPECT2" :
+ sm->nextExpected == OTRL_SMP_EXPECT3 ? "EXPECT3" :
+ sm->nextExpected == OTRL_SMP_EXPECT4 ? "EXPECT4" :
+ sm->nextExpected == OTRL_SMP_EXPECT5 ? "EXPECT5" :
+ "INVALID");
+ fprintf(f, " Received_Q: %d\n", sm->received_question);
+ fprintf(f, " Progress state: %d (%s)\n", sm->sm_prog_state,
+ sm->sm_prog_state == OTRL_SMP_PROG_OK ? "OK" :
+ sm->sm_prog_state == OTRL_SMP_PROG_CHEATED ? "CHEATED" :
+ sm->sm_prog_state == OTRL_SMP_PROG_FAILED ? "FAILED" :
+ sm->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED ? "SUCCEEDED" :
+ "INVALID");
+}
+
+#endif
+
static const int SM_MSG1_LEN = 6;
static const int SM_MSG2_LEN = 11;
static const int SM_MSG3_LEN = 8;
@@ -70,10 +96,12 @@ static gcry_mpi_t SM_MODULUS_MINUS_2 = NULL;
void otrl_sm_init(void)
{
gcry_check_version(NULL);
- gcry_mpi_scan(&SM_MODULUS, GCRYMPI_FMT_HEX, SM_MODULUS_S, 0, NULL);
- gcry_mpi_scan(&SM_ORDER, GCRYMPI_FMT_HEX, SM_ORDER_S, 0, NULL);
- gcry_mpi_scan(&SM_GENERATOR, GCRYMPI_FMT_HEX, SM_GENERATOR_S,
- 0, NULL);
+ gcry_mpi_scan(&SM_MODULUS, GCRYMPI_FMT_HEX,
+ (const unsigned char *)SM_MODULUS_S, 0, NULL);
+ gcry_mpi_scan(&SM_ORDER, GCRYMPI_FMT_HEX,
+ (const unsigned char *)SM_ORDER_S, 0, NULL);
+ gcry_mpi_scan(&SM_GENERATOR, GCRYMPI_FMT_HEX,
+ (const unsigned char *)SM_GENERATOR_S, 0, NULL);
SM_MODULUS_MINUS_2 = gcry_mpi_new(SM_MOD_LEN_BITS);
gcry_mpi_sub_ui(SM_MODULUS_MINUS_2, SM_MODULUS, 2);
}
@@ -106,7 +134,7 @@ void otrl_sm_state_new(OtrlSMState *smst)
void otrl_sm_state_init(OtrlSMState *smst)
{
otrl_sm_state_free(smst);
- smst->secret = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->secret = gcry_mpi_snew(SM_MOD_LEN_BITS);
smst->x2 = NULL;
smst->x3 = NULL;
smst->g1 = gcry_mpi_copy(SM_GENERATOR);
@@ -156,7 +184,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;
}
@@ -223,7 +251,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;
@@ -255,7 +283,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) {
@@ -306,9 +334,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;
@@ -320,15 +348,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);
@@ -344,8 +372,8 @@ static gcry_error_t serialize_mpi_array(unsigned char **buffer, int *buflen,
static gcry_error_t unserialize_mpi_array(gcry_mpi_t **mpis,
unsigned int expcount, const unsigned char *buffer, const int buflen)
{
- int i;
- int lenp = buflen;
+ unsigned int i;
+ size_t lenp = buflen;
unsigned int thecount = 0;
const unsigned char* bufp = buffer;
*mpis = NULL;
@@ -400,10 +428,11 @@ 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);
+ gcry_mpi_t temp = gcry_mpi_snew(SM_MOD_LEN_BITS);
gcry_mpi_powm(temp, g, r, SM_MODULUS);
otrl_sm_hash(c, version, temp, NULL);
gcry_mpi_mulm(temp, x, *c, SM_ORDER);
@@ -415,9 +444,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;
@@ -430,7 +461,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);
@@ -443,7 +474,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();
@@ -475,7 +508,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;
@@ -518,7 +553,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);
@@ -543,7 +579,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;
@@ -555,7 +593,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
@@ -601,11 +639,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);
@@ -631,7 +669,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;
@@ -645,21 +684,23 @@ 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);
+ otrl_sm_msg_free(&msg1, SM_MSG1_LEN);
+ 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)) {
+ otrl_sm_msg_free(&msg1, SM_MSG1_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Create Bob's half of the generators g2 and g3 */
@@ -671,6 +712,8 @@ gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input,
gcry_mpi_powm(bstate->g3, msg1[3], bstate->x3, SM_MODULUS);
bstate->sm_prog_state = OTRL_SMP_PROG_OK;
+
+ otrl_sm_msg_free(&msg1, SM_MSG1_LEN);
return gcry_error(GPG_ERR_NO_ERROR);
}
@@ -684,7 +727,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;
@@ -693,7 +737,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);
@@ -717,7 +761,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);
@@ -738,18 +783,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;
@@ -757,7 +803,8 @@ 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);
+ otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
otrl_sm_msg3_init(&msg3);
@@ -765,10 +812,12 @@ gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input,
/* Store Bob's g3a value for later in the protocol */
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);
+ /* Verify Bob's knowledge of discrete 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)) {
+ otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
}
/* Combine the two halves from Bob and Alice and determine g2 and g3 */
@@ -776,8 +825,12 @@ 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)) {
+ otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
/* Calculate P and Q values for Alice */
r = randomExponent();
@@ -790,7 +843,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);
@@ -822,7 +876,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;
@@ -835,7 +890,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);
@@ -843,12 +898,18 @@ 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);
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ 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)) {
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
/* Find Pa/Pb and Qa/Qb */
inv = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -858,8 +919,12 @@ gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, c
gcry_mpi_mulm(bstate->qab, msg3[1], inv, SM_MODULUS);
/* 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);
+ if (otrl_sm_check_equal_logs(msg3[6], msg3[7], msg3[5], bstate, 7)) {
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ gcry_mpi_release(inv);
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
/* Calculate Rb and proof */
gcry_mpi_powm(msg4[0], bstate->qab, bstate->x3, SM_MODULUS);
@@ -882,16 +947,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;
@@ -900,16 +966,19 @@ 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);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ 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);
+ if (otrl_sm_check_equal_logs(msg4[1], msg4[2], msg4[0], astate, 8)) {
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
/* Calculate Rab and verify that secrets match */
rab = gcry_mpi_new(SM_MOD_LEN_BITS);
@@ -923,7 +992,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/plugins/MirOTR/libotr/src/sm.h b/plugins/MirOTR/libotr/src/sm.h
index 2e94f07e64..53703ff301 100644
--- a/plugins/MirOTR/libotr/src/sm.h
+++ b/plugins/MirOTR/libotr/src/sm.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __SM_H__
diff --git a/plugins/MirOTR/libotr/src/tests.c b/plugins/MirOTR/libotr/src/tests.c
new file mode 100644
index 0000000000..d64ac354a1
--- /dev/null
+++ b/plugins/MirOTR/libotr/src/tests.c
@@ -0,0 +1,275 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "proto.h"
+#include "privkey.h"
+#include "message.h"
+#include "instag.h"
+
+#define ALICE "alice"
+#define BOB "bob"
+#define PROTO "prpl-oscar"
+
+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 {
+ struct s_node *next;
+ char *from, *to, *msg;
+} MsgNode;
+
+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) {
+ MsgNode *node = noderoot;
+
+ receiving(node->from, node->to, node->msg);
+ free(node->from);
+ free(node->to);
+ free(node->msg);
+ noderoot = node->next;
+ free(node);
+ if (noderoot == NULL) nodeend = &noderoot;
+ }
+}
+
+static void inject(const char *from, const char *to, const char *msg)
+{
+ MsgNode *node = malloc(sizeof(*node));
+ node->from = strdup(from);
+ node->to = strdup(to);
+ node->msg = strdup(msg);
+ node->next = NULL;
+ *nodeend = node;
+ nodeend = &(node->next);
+ printf("[%s->%s: %s]\n\n", from, to, msg);
+}
+
+static OtrlPolicy op_policy(void *opdata, ConnContext *context)
+{
+ if (!strcmp(context->accountname, ALICE)) return ALICEPOLICY;
+ if (!strcmp(context->accountname, BOB)) return BOBPOLICY;
+ return OTRL_POLICY_DEFAULT;
+}
+
+static void op_inject(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient, const char *message)
+{
+ inject(accountname, recipient, message);
+}
+
+static void op_notify(void *opdata, OtrlNotifyLevel level,
+ const char *accountname, const char *protocol,
+ const char *username, const char *title,
+ const char *primary, const char *secondary)
+{
+}
+
+static int op_display_otr_message(void *opdata, const char *accountname,
+ const char *protocol, const char *username, const char *msg)
+{
+ return -1;
+}
+
+static void op_gone_secure(void *opdata, ConnContext *context)
+{
+ printf("SECURE (%d): %s / %s\n\n", context->protocol_version,
+ context->accountname, context->username);
+}
+
+static void op_gone_insecure(void *opdata, ConnContext *context)
+{
+ printf("INSECURE: %s / %s\n\n", context->accountname, context->username);
+}
+
+static void op_still_secure(void *opdata, ConnContext *context, int is_reply)
+{
+ printf("REFRESH (%d/%d): %s / %s\n\n", is_reply, context->protocol_version,
+ context->accountname, context->username);
+}
+
+static OtrlMessageAppOps ops = {
+ op_policy,
+ NULL,
+ NULL,
+ op_inject,
+ op_notify,
+ op_display_otr_message,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ op_gone_secure,
+ op_gone_insecure,
+ op_still_secure,
+ NULL
+};
+
+static OtrlUserState us;
+
+void receiving(const char *from, const char *to, const char *msg)
+{
+ int ignore;
+ char *newmsg;
+ OtrlTLV *tlvs;
+
+ ignore = otrl_message_receiving(us, &ops, NULL, to, PROTO, from, msg,
+ &newmsg, &tlvs, NULL, NULL);
+
+ if (!ignore) {
+ printf("%s> %s\n\n", from, newmsg ? newmsg : msg);
+ }
+
+ otrl_message_free(newmsg);
+ otrl_tlv_free(tlvs);
+}
+
+static void sending(const char *from, const char *to, const char *msg)
+{
+ gcry_error_t err;
+ OtrlTLV *tlvs = NULL;
+ char *newmsg;
+
+ err = otrl_message_sending(us, &ops, NULL, from, PROTO, to, msg,
+ tlvs, &newmsg, NULL, NULL, NULL);
+
+ if (!err) {
+ inject(from, to, newmsg ? newmsg : msg);
+ }
+
+ otrl_message_free(newmsg);
+ otrl_tlv_free(tlvs);
+}
+
+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);
+ 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?");
+ }
+ dispatch();
+ sending(ALICE, BOB, "Hi there");
+ dispatch();
+}
+
+void test_unreadable(void)
+{
+ ConnContext *bobcontext;
+
+ printf("\n\n*** Testing Bob receiving unreadable messages from "
+ "Alice ***\n\n");
+
+ bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL);
+ otrl_context_force_plaintext(bobcontext);
+ sending(ALICE, BOB, "unreadable text");
+ dispatch();
+
+}
+
+void test_crash1(void)
+{
+ ConnContext *alicecontext, *bobcontext;
+
+ printf("\n\n*** Testing old double gcry_cipher_release case ***\n\n");
+
+ otrl_context_forget_all(us);
+ ALICEPOLICY = OTRL_POLICY_DEFAULT;
+ sending(ALICE, BOB, "?OTR?");
+ dispatch();
+
+ 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();
+ sending(ALICE, BOB, "You!"); dispatch();
+ otrl_context_force_plaintext(bobcontext);
+ sending(BOB, ALICE, "?OTR?"); dispatch();
+ sending(ALICE, BOB, "now."); dispatch();
+ printf("%d %p %p\n", alicecontext->our_keyid, alicecontext->their_y, alicecontext->their_old_y);
+ printf("%p %p %p %p\n",
+ alicecontext->sesskeys[0][0].sendenc,
+ alicecontext->sesskeys[0][1].sendenc,
+ alicecontext->sesskeys[1][0].sendenc,
+ alicecontext->sesskeys[1][1].sendenc);
+ sending(BOB, ALICE, "then."); dispatch();
+}
+
+void test_refresh(int vers)
+{
+ ConnContext *alicecontext, *bobcontext;
+
+ printf("\n\n*** Testing refresh ***\n\n");
+
+ otrl_context_forget_all(us);
+ 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, 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();
+ sending(BOB, ALICE, "There!"); dispatch();
+ sending(ALICE, BOB, "You!"); dispatch();
+ sending(ALICE, BOB, "Guys!"); dispatch();
+ sending(BOB, ALICE, "?OTR?"); dispatch();
+ sending(ALICE, BOB, "Refreshed!"); dispatch();
+ sending(BOB, ALICE, "Also refreshed!"); dispatch();
+}
+
+int main(int argc, char **argv)
+{
+ OTRL_INIT;
+ 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);
+
+ otrl_userstate_free(us);
+
+ return 0;
+}
diff --git a/plugins/MirOTR/libotr/src/tlv.c b/plugins/MirOTR/libotr/src/tlv.c
index 0cea7b5fa2..fa76c3d76c 100644
--- a/plugins/MirOTR/libotr/src/tlv.c
+++ b/plugins/MirOTR/libotr/src/tlv.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
diff --git a/plugins/MirOTR/libotr/src/tlv.h b/plugins/MirOTR/libotr/src/tlv.h
index affe0d58e1..0bfeeb27fd 100644
--- a/plugins/MirOTR/libotr/src/tlv.h
+++ b/plugins/MirOTR/libotr/src/tlv.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __TLV_H__
@@ -35,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
@@ -44,6 +45,13 @@ typedef struct s_OtrlTLV {
/* Like OTRL_TLV_SMP1, but there's a question for the buddy at the
* beginning */
#define OTRL_TLV_SMP1Q 0x0007
+/* Tell the application the current "extra" symmetric key */
+/* XXX: Document this in the protocol spec:
+ * The body of the TLV will begin with a 4-byte indication of what this
+ * symmetric key will be used for (file transfer, voice encryption,
+ * etc.). After that, the contents are use-specific (which file, etc.).
+ * There are no currently defined uses. */
+#define OTRL_TLV_SYMKEY 0x0008
/* Make a single TLV, copying the supplied data */
OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len,
diff --git a/plugins/MirOTR/libotr/src/userstate.c b/plugins/MirOTR/libotr/src/userstate.c
index 6de95b8e42..58f5a05578 100644
--- a/plugins/MirOTR/libotr/src/userstate.c
+++ b/plugins/MirOTR/libotr/src/userstate.c
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* system headers */
@@ -38,14 +39,19 @@ 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;
+ us->timer_running = 0;
return us;
}
-/* Free a OtrlUserState */
+/* Free a OtrlUserState. If you have a timer running for this userstate,
+stop it before freeing the userstate. */
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/plugins/MirOTR/libotr/src/userstate.h b/plugins/MirOTR/libotr/src/userstate.h
index 8525df0518..3f1e3b89cf 100644
--- a/plugins/MirOTR/libotr/src/userstate.h
+++ b/plugins/MirOTR/libotr/src/userstate.h
@@ -1,6 +1,7 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander,
+ * Willy Lew, Lisa Du, Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __USERSTATE_H__
@@ -22,12 +23,16 @@
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;
+ int timer_running;
};
/* Create a new OtrlUserState. Most clients will only need one of
@@ -39,7 +44,8 @@ struct s_OtrlUserState {
* OtrlUserState. */
OtrlUserState otrl_userstate_create(void);
-/* Free a OtrlUserState */
+/* Free a OtrlUserState. If you have a timer running for this userstate,
+stop it before freeing the userstate. */
void otrl_userstate_free(OtrlUserState us);
#endif
diff --git a/plugins/MirOTR/libotr/src/version.h b/plugins/MirOTR/libotr/src/version.h
index 11cb586699..ae2f7ff3dd 100644
--- a/plugins/MirOTR/libotr/src/version.h
+++ b/plugins/MirOTR/libotr/src/version.h
@@ -1,6 +1,8 @@
/*
* Off-the-Record Messaging library
- * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
@@ -14,16 +16,16 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __VERSION_H__
#define __VERSION_H__
-#define OTRL_VERSION "3.2.0"
+#define OTRL_VERSION "4.1.0"
-#define OTRL_VERSION_MAJOR 3
-#define OTRL_VERSION_MINOR 2
+#define OTRL_VERSION_MAJOR 4
+#define OTRL_VERSION_MINOR 1
#define OTRL_VERSION_SUB 0
#endif