diff options
Diffstat (limited to 'plugins/MirOTR/libotr/src')
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 |