diff options
author | René Schümann <white06tiger@gmail.com> | 2015-03-14 19:56:55 +0000 |
---|---|---|
committer | René Schümann <white06tiger@gmail.com> | 2015-03-14 19:56:55 +0000 |
commit | c60aed5432e9cda277b9351de51e82dfb8e02475 (patch) | |
tree | 97ccd1ea8e2544f6a9673ee7d04c18b714877a35 /plugins/MirOTR/libotr-3.2.0/src/proto.c | |
parent | d2b26b1f86326362f56540b5185fa09ab5f2779c (diff) |
MirOTR: part one of many file/folder structure changes
git-svn-id: http://svn.miranda-ng.org/main/trunk@12402 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/MirOTR/libotr-3.2.0/src/proto.c')
-rw-r--r-- | plugins/MirOTR/libotr-3.2.0/src/proto.c | 906 |
1 files changed, 0 insertions, 906 deletions
diff --git a/plugins/MirOTR/libotr-3.2.0/src/proto.c b/plugins/MirOTR/libotr-3.2.0/src/proto.c deleted file mode 100644 index 307ab047ff..0000000000 --- a/plugins/MirOTR/libotr-3.2.0/src/proto.c +++ /dev/null @@ -1,906 +0,0 @@ -/* - * Off-the-Record Messaging library - * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* OTR Protocol implementation. This file should be independent of - * gaim, so that it can be used to make other clients. */ - -/* system headers */ -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -/* libgcrypt headers */ -#include <gcrypt.h> - -/* libotr headers */ -#include "b64.h" -#include "privkey.h" -#include "proto.h" -#include "mem.h" -#include "version.h" -#include "tlv.h" -#include "serial.h" - -/* 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, - unsigned int ver_sub) -{ - unsigned int api_version; - - /* The major versions have to match, and you can't be using a newer - * minor version than we expect. */ - if (ver_major != OTRL_VERSION_MAJOR || ver_minor > OTRL_VERSION_MINOR) { - fprintf(stderr, "Expected libotr API version %u.%u.%u incompatible " - "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); - } - - /* Set the API version. If we get called multiple times for some - * reason, take the smallest value. */ - api_version = (ver_major << 16) | (ver_minor << 8) | (ver_sub); - if (otrl_api_version == 0 || otrl_api_version > api_version) { - otrl_api_version = api_version; - } - - /* Initialize the memory module */ - otrl_mem_init(); - - /* Initialize the DH module */ - otrl_dh_init(); - - /* Initialize the SM module */ - otrl_sm_init(); -} - -/* Return a pointer to a static string containing the version number of - * the OTR library. */ -const char *otrl_version(void) -{ - return OTRL_VERSION; -} - -/* Store some MAC keys to be revealed later */ -static gcry_error_t reveal_macs(ConnContext *context, - DH_sesskeys *sess1, DH_sesskeys *sess2) -{ - unsigned int numnew = sess1->rcvmacused + sess1->sendmacused + - 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 * 20); - if (!newmacs) { - return gcry_error(GPG_ERR_ENOMEM); - } - if (sess1->rcvmacused) { - memmove(newmacs + context->numsavedkeys * 20, sess1->rcvmackey, 20); - context->numsavedkeys++; - } - if (sess1->sendmacused) { - memmove(newmacs + context->numsavedkeys * 20, sess1->sendmackey, 20); - context->numsavedkeys++; - } - if (sess2->rcvmacused) { - memmove(newmacs + context->numsavedkeys * 20, sess2->rcvmackey, 20); - context->numsavedkeys++; - } - if (sess2->sendmacused) { - memmove(newmacs + context->numsavedkeys * 20, sess2->sendmackey, 20); - context->numsavedkeys++; - } - context->saved_mac_keys = newmacs; - - return gcry_error(GPG_ERR_NO_ERROR); -} - -/* Make a new DH key for us, and rotate old old ones. Be sure to keep - * the sesskeys array in sync. */ -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), - sizeof(DH_keypair)); - - /* Rotate the session keys */ - err = reveal_macs(context, &(context->sesskeys[1][0]), - &(context->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]), - sizeof(DH_sesskeys)); - memmove(&(context->sesskeys[1][1]), &(context->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++; - - /* Make the session keys */ - if (context->their_y) { - err = otrl_dh_session(&(context->sesskeys[0][0]), - &(context->our_dh_key), context->their_y); - if (err) return err; - } else { - otrl_dh_session_blank(&(context->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 (err) return err; - } else { - otrl_dh_session_blank(&(context->sesskeys[0][1])); - } - return gcry_error(GPG_ERR_NO_ERROR); -} - -/* Rotate in a new DH public key for our correspondent. Be sure to keep - * the sesskeys array in sync. */ -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; - - /* Rotate the session keys */ - err = reveal_macs(context, &(context->sesskeys[0][1]), - &(context->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]), - sizeof(DH_sesskeys)); - memmove(&(context->sesskeys[1][1]), &(context->sesskeys[1][0]), - sizeof(DH_sesskeys)); - - /* Copy in the new public key */ - context->their_y = gcry_mpi_copy(new_y); - context->their_keyid++; - - /* Make the session keys */ - err = otrl_dh_session(&(context->sesskeys[0][0]), - &(context->our_dh_key), context->their_y); - if (err) return err; - err = otrl_dh_session(&(context->sesskeys[1][0]), - &(context->our_old_dh_key), context->their_y); - if (err) return err; - - return gcry_error(GPG_ERR_NO_ERROR); -} - -/* Return a pointer to a newly-allocated OTR query message, customized - * with our name. The caller should free() the result when he's done - * with it. */ -char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy) -{ - char *msg; - int v1_supported, v2_supported; - const char *version_tag; - /* 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 " - "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."; - - /* Figure out the version tag */ - v1_supported = (policy & OTRL_POLICY_ALLOW_V1); - v2_supported = (policy & OTRL_POLICY_ALLOW_V2); - if (v1_supported) { - if (v2_supported) { - version_tag = "?v2?"; - } else { - version_tag = "?"; - } - } else { - if (v2_supported) { - version_tag = "v2?"; - } else { - version_tag = "v?"; - } - } - - /* Remove two "%s", add '\0' */ - msg = malloc(strlen(format) + strlen(version_tag) + strlen(ourname) - 3); - if (!msg) return NULL; - sprintf(msg, format, version_tag, ourname); - 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, - OtrlPolicy policy) -{ - char *otrtag; - unsigned int query_versions = 0; - - otrtag = strstr(querymsg, "?OTR"); - otrtag += 4; - if (*otrtag == '?') { - query_versions = (1<<0); - ++otrtag; - } - if (*otrtag == 'v') { - for(++otrtag; *otrtag && *otrtag != '?'; ++otrtag) { - switch(*otrtag) { - case '2': - query_versions |= (1<<1); - break; - } - } - } - - if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) { - return 2; - } - if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) { - return 1; - } - return 0; -} - -/* Locate any whitespace tag in this message, and return the best - * version of OTR support on both sides. Set *starttagp and *endtagp to - * the start and end of the located tag, so that it can be snipped out. */ -unsigned int otrl_proto_whitespace_bestversion(const char *msg, - const char **starttagp, const char **endtagp, OtrlPolicy policy) -{ - const char *starttag, *endtag; - unsigned int query_versions = 0; - - *starttagp = NULL; - *endtagp = NULL; - - starttag = strstr(msg, OTRL_MESSAGE_TAG_BASE); - if (!starttag) return 0; - - endtag = starttag + strlen(OTRL_MESSAGE_TAG_BASE); - - /* Look for groups of 8 spaces and/or tabs */ - while(1) { - int i; - int allwhite = 1; - for(i=0;i<8;++i) { - if (endtag[i] != ' ' && endtag[i] != '\t') { - allwhite = 0; - break; - } - } - if (allwhite) { - if (!strncmp(endtag, OTRL_MESSAGE_TAG_V1, 8)) { - query_versions |= (1<<0); - } - if (!strncmp(endtag, OTRL_MESSAGE_TAG_V2, 8)) { - query_versions |= (1<<1); - } - endtag += 8; - } else { - break; - } - } - - *starttagp = starttag; - *endtagp = endtag; - - if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) { - return 2; - } - if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) { - return 1; - } - return 0; -} - -/* Return the Message type of the given message. */ -OtrlMessageType otrl_proto_message_type(const char *message) -{ - char *otrtag; - - otrtag = strstr(message, "?OTR"); - - if (!otrtag) { - if (strstr(message, OTRL_MESSAGE_TAG_BASE)) { - return OTRL_MSGTYPE_TAGGEDPLAINTEXT; - } else { - return OTRL_MSGTYPE_NOTOTR; - } - } - - 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; - - return OTRL_MSGTYPE_UNKNOWN; -} - -/* 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. */ -gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context, - const char *msg, const OtrlTLV *tlvs, unsigned char flags) -{ - size_t justmsglen = strlen(msg); - size_t msglen = justmsglen + 1 + otrl_tlv_seriallen(tlvs); - size_t buflen; - size_t pubkeylen; - unsigned char *buf = NULL; - unsigned char *bufp; - size_t lenp; - DH_sesskeys *sess = &(context->sesskeys[1][0]); - gcry_error_t err; - size_t reveallen = 20 * context->numsavedkeys; - size_t base64len; - char *base64buf = NULL; - unsigned char *msgbuf = NULL; - enum gcry_mpi_format format = GCRYMPI_FMT_USG; - char *msgdup; - int version = context->protocol_version; - - /* Make sure we're actually supposed to be able to encrypt */ - if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED || - context->their_keyid == 0) { - return gcry_error(GPG_ERR_CONFLICT); - } - - /* We need to copy the incoming msg, since it might be an alias for - * context->lastmessage, which we'll be freeing soon. */ - msgdup = gcry_malloc_secure(justmsglen + 1); - if (msgdup == NULL) { - return gcry_error(GPG_ERR_ENOMEM); - } - strcpy(msgdup, msg); - - *encmessagep = NULL; - - /* Header, 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 += pubkeylen + 4; - buf = malloc(buflen); - msgbuf = gcry_malloc_secure(msglen); - if (buf == NULL || msgbuf == NULL) { - free(buf); - gcry_free(msgbuf); - gcry_free(msgdup); - return gcry_error(GPG_ERR_ENOMEM); - } - memmove(msgbuf, msgdup, justmsglen); - msgbuf[justmsglen] = '\0'; - otrl_tlv_serialize(msgbuf + justmsglen + 1, tlvs); - bufp = buf; - lenp = buflen; - if (version == 1) { - memcpy(bufp, "\x00\x01\x03", 3); /* header */ - } else { - memcpy(bufp, "\x00\x02\x03", 3); /* header */ - } - debug_data("Header", bufp, 3); - bufp += 3; lenp -= 3; - if (version == 2) { - bufp[0] = flags; - bufp += 1; lenp -= 1; - } - write_int(context->our_keyid-1); /* sender keyid */ - debug_int("Sender keyid", bufp-4); - write_int(context->their_keyid); /* recipient keyid */ - debug_int("Recipient keyid", bufp-4); - - write_mpi(context->our_dh_key.pub, pubkeylen, "Y"); /* Y */ - - otrl_dh_incctr(sess->sendctr); - memcpy(bufp, sess->sendctr, 8); /* Counter (top 8 bytes only) */ - debug_data("Counter", bufp, 8); - bufp += 8; lenp -= 8; - - write_int(msglen); /* length of encrypted data */ - debug_int("Msg len", bufp-4); - - err = gcry_cipher_reset(sess->sendenc); - if (err) goto err; - err = gcry_cipher_setctr(sess->sendenc, sess->sendctr, 16); - if (err) goto err; - err = gcry_cipher_encrypt(sess->sendenc, bufp, msglen, msgbuf, msglen); - if (err) goto err; /* encrypted data */ - debug_data("Enc data", bufp, msglen); - bufp += msglen; - lenp -= msglen; - - gcry_md_reset(sess->sendmac); - gcry_md_write(sess->sendmac, buf, bufp-buf); - memcpy(bufp, gcry_md_read(sess->sendmac, GCRY_MD_SHA1), 20); - debug_data("MAC", bufp, 20); - bufp += 20; /* MAC */ - lenp -= 20; - - write_int(reveallen); /* length of revealed MAC keys */ - debug_int("Revealed MAC length", bufp-4); - - if (reveallen > 0) { - memcpy(bufp, context->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; - } - - assert(lenp == 0); - - /* Make the base64-encoding. */ - base64len = ((buflen + 2) / 3) * 4; - base64buf = malloc(5 + base64len + 1 + 1); - 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; - 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); - } - } - gcry_free(msgdup); - return gcry_error(GPG_ERR_NO_ERROR); -err: - free(buf); - gcry_free(msgbuf); - gcry_free(msgdup); - *encmessagep = NULL; - return err; -} - -/* Extract the flags from an otherwise unreadable Data Message. */ -gcry_error_t otrl_proto_data_read_flags(const char *datamsg, - unsigned char *flagsp) -{ - char *otrtag, *endtag; - unsigned char *rawmsg = NULL; - unsigned char *bufp; - size_t msglen, rawlen, lenp; - unsigned char version; - - if (flagsp) *flagsp = 0; - otrtag = strstr(datamsg, "?OTR:"); - if (!otrtag) { - goto invval; - } - endtag = strchr(otrtag, '.'); - if (endtag) { - msglen = endtag-otrtag; - } else { - msglen = strlen(otrtag); - } - - /* Base64-decode the message */ - rawlen = ((msglen-5) / 4) * 3; /* 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 */ - - 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; - - if (version == 2) { - require_len(1); - if (flagsp) *flagsp = bufp[0]; - bufp += 1; lenp -= 1; - } - - free(rawmsg); - return gcry_error(GPG_ERR_NO_ERROR); - -invval: - free(rawmsg); - return gcry_error(GPG_ERR_INV_VALUE); -} - -/* 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). */ -gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp, - ConnContext *context, const char *datamsg, unsigned char *flagsp) -{ - char *otrtag, *endtag; - gcry_error_t err; - unsigned char *rawmsg = NULL; - size_t msglen, rawlen, lenp; - unsigned char *macstart, *macend; - unsigned char *bufp; - unsigned int sender_keyid, recipient_keyid; - gcry_mpi_t sender_next_y = NULL; - unsigned char ctr[8]; - unsigned int datalen, reveallen; - unsigned char *data = NULL; - unsigned char *nul = NULL; - unsigned char givenmac[20]; - DH_sesskeys *sess; - unsigned char version; - - *plaintextp = NULL; - *tlvsp = NULL; - if (flagsp) *flagsp = 0; - otrtag = strstr(datamsg, "?OTR:"); - if (!otrtag) { - goto invval; - } - endtag = strchr(otrtag, '.'); - if (endtag) { - msglen = endtag-otrtag; - } else { - msglen = strlen(otrtag); - } - - /* Base64-decode the message */ - rawlen = ((msglen-5) / 4) * 3; /* 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 */ - - 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) { - 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); - bufp += 8; lenp -= 8; - read_int(datalen); - require_len(datalen); - data = malloc(datalen+1); - if (!data) { - err = gcry_error(GPG_ERR_ENOMEM); - goto err; - } - memcpy(data, bufp, datalen); - data[datalen] = '\0'; - bufp += datalen; lenp -= datalen; - macend = bufp; - require_len(20); - memcpy(givenmac, bufp, 20); - bufp += 20; lenp -= 20; - read_int(reveallen); - require_len(reveallen); - /* Just skip over the revealed MAC keys, which we don't need. They - * were published for deniability of transcripts. */ - bufp += reveallen; lenp -= reveallen; - - /* That should be everything */ - if (lenp != 0) goto invval; - - /* 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) || - sender_keyid == 0 || recipient_keyid == 0) { - goto conflict; - } - - if (sender_keyid == context->their_keyid - 1 && - context->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]); - - 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)) { - /* The MACs didn't match! */ - goto conflict; - } - sess->rcvmacused = 1; - - /* Check to see that the counter is increasing; i.e. that this isn't - * a replay. */ - if (otrl_dh_cmpctr(ctr, sess->rcvctr) <= 0) { - goto conflict; - } - - /* Decrypt the message */ - memcpy(sess->rcvctr, ctr, 8); - err = gcry_cipher_reset(sess->rcvenc); - if (err) goto err; - err = gcry_cipher_setctr(sess->rcvenc, sess->rcvctr, 16); - if (err) goto err; - err = gcry_cipher_decrypt(sess->rcvenc, data, datalen, NULL, 0); - if (err) goto err; - - /* See if either set of keys needs rotating */ - - if (recipient_keyid == context->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) { - /* They've sent us a new public key */ - err = rotate_y_keys(context, sender_next_y); - if (err) goto err; - } - - gcry_mpi_release(sender_next_y); - *plaintextp = (char *)data; - - /* See if there are TLVs */ - nul = data; - while (nul < data+datalen && *nul) ++nul; - /* If we stopped before the end, skip the NUL we stopped at */ - if (nul < data+datalen) ++nul; - *tlvsp = otrl_tlv_parse(nul, (data+datalen)-nul); - - free(rawmsg); - return gcry_error(GPG_ERR_NO_ERROR); - -invval: - err = gcry_error(GPG_ERR_INV_VALUE); - goto err; -conflict: - err = gcry_error(GPG_ERR_CONFLICT); - goto err; -err: - gcry_mpi_release(sender_next_y); - free(data); - free(rawmsg); - return err; -} - -/* Accumulate a potential fragment into the current context. */ -OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep, - ConnContext *context, const char *msg) -{ - OtrlFragmentResult res = OTRL_FRAGMENT_INCOMPLETE; - const char *tag; - - tag = strstr(msg, "?OTR,"); - if (tag) { - unsigned short n = 0, k = 0; - int start = 0, end = 0; - - 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 { - free(context->fragment); - context->fragment = NULL; - context->fragment_len = 0; - context->fragment_n = 0; - context->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; - } - - return res; -} - -/* Create a fragmented message. */ -gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count, - char ***fragments, const char *message) -{ - char *fragdata; - int fragdatalen = 0; - unsigned short curfrag = 0; - int index = 0; - int msglen = strlen(message); - int headerlen = 19; /* Should vary by number of msgs */ - - char **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; - - if (msglen - index < mms - headerlen) { - fragdatalen = msglen - index; - } else { - fragdatalen = mms - headerlen; - } - fragdata = malloc(fragdatalen + 1); - 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) { - 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; - message += fragdatalen; - } - - *fragments = fragmentarray; - return gcry_error(GPG_ERR_NO_ERROR); -} - -/* Free a string array containing fragment messages. */ -void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen) -{ - int i; - char **fragmentarray = *fragments; - if(fragmentarray) { - for(i = 0; i < arraylen; i++) - { - if(fragmentarray[i]) { - free(fragmentarray[i]); - } - } - free(fragmentarray); - } -} - |