summary refs log tree commit diff stats
path: root/libotr/libotr-4.1.1/toolkit/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'libotr/libotr-4.1.1/toolkit/parse.c')
-rw-r--r--libotr/libotr-4.1.1/toolkit/parse.c654
1 files changed, 654 insertions, 0 deletions
diff --git a/libotr/libotr-4.1.1/toolkit/parse.c b/libotr/libotr-4.1.1/toolkit/parse.c
new file mode 100644
index 0000000..aaa0ad5
--- /dev/null
+++ b/libotr/libotr-4.1.1/toolkit/parse.c
@@ -0,0 +1,654 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits, Chris Alexander,
+ *                           Nikita Borisov
+ *                           <otr@cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; 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>
+
+/* libotr headers */
+#include "b64.h"
+
+/* toolkit headers */
+#include "sha1hmac.h"
+#include "parse.h"
+
+/* Dump an unsigned int to a FILE * */
+void dump_int(FILE *stream, const char *title, unsigned int val)
+{
+    fprintf(stream, "%s: %u\n", title, val);
+}
+
+/* Dump an mpi to a FILE * */
+void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val)
+{
+    size_t plen;
+    unsigned char *d;
+
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &plen, val);
+    d = malloc(plen);
+    gcry_mpi_print(GCRYMPI_FMT_USG, d, plen, NULL, val);
+    dump_data(stream, title, d, plen);
+    free(d);
+}
+
+/* Dump data to a FILE * */
+void dump_data(FILE *stream, const char *title, const unsigned char *data,
+	size_t datalen)
+{
+    size_t i;
+    fprintf(stream, "%s: ", title);
+    for(i=0;i<datalen;++i) {
+	fprintf(stream, "%02x", data[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+/* base64 decode the message, and put the resulting size into *lenp */
+static unsigned char *decode(const char *msg, size_t *lenp)
+{
+    const char *header, *footer;
+    unsigned char *raw;
+    size_t rawlen;
+
+    /* Find the header */
+    header = strstr(msg, "?OTR:");
+    if (!header) return NULL;
+    /* Skip the header */
+    header += 5;
+
+    /* Find the trailing '.' */
+    footer = strchr(header, '.');
+    if (!footer) footer = header + strlen(header);
+
+    rawlen = OTRL_B64_MAX_DECODED_SIZE(footer-header);
+
+    raw = malloc(rawlen);
+    if (raw == NULL && rawlen > 0) return NULL;
+    *lenp = otrl_base64_decode(raw, header, footer-header);
+
+    return raw;
+}
+
+#define require_len(l) do { if (lenp < (l)) goto inv; } while(0)
+#define read_int(x) do { \
+	require_len(4); \
+	(x) = (((unsigned int)bufp[0]) << 24) | (bufp[1] << 16) | (bufp[2] << 8 ) | bufp[3]; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+#define read_mpi(x) do { \
+	size_t mpilen; \
+	read_int(mpilen); \
+	require_len(mpilen); \
+	gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \
+	bufp += mpilen; lenp -= mpilen; \
+    } while(0)
+#define read_raw(b, l) do { \
+	if (l) { \
+	  require_len(l); \
+	  memmove((b), bufp, (l)); \
+	  bufp += (l); lenp -= (l); \
+	} \
+    } while(0)
+#define write_int(x) do { \
+	bufp[0] = ((x) >> 24) & 0xff; \
+	bufp[1] = ((x) >> 16) & 0xff; \
+	bufp[2] = ((x) >> 8) & 0xff; \
+	bufp[3] = (x) & 0xff; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+#define write_mpi(x,l) do { \
+	write_int(l); \
+	gcry_mpi_print(GCRYMPI_FMT_USG, bufp, lenp, NULL, (x)); \
+	bufp += (l); lenp -= (l); \
+    } while(0)
+#define write_raw(x,l) do { \
+	memmove(bufp, (x), (l)); \
+	bufp += (l); lenp -= (l); \
+    } while(0)
+
+/* Parse a Key Exchange Message into a newly-allocated KeyExchMsg structure */
+KeyExchMsg parse_keyexch(const char *msg)
+{
+    KeyExchMsg kem = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    kem = calloc(1, sizeof(struct s_KeyExchMsg));
+    if (!kem) {
+	free(raw);
+	goto inv;
+    }
+
+    kem->raw = raw;
+    kem->sigstart = bufp;
+
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x0a", 3)) goto inv;
+    bufp += 3; lenp -= 3;
+
+    require_len(1);
+    kem->reply = *bufp;
+    bufp += 1; lenp -= 1;
+
+    read_mpi(kem->p);
+    read_mpi(kem->q);
+    read_mpi(kem->g);
+    read_mpi(kem->e);
+
+    read_int(kem->keyid);
+
+    read_mpi(kem->y);
+
+    kem->sigend = bufp;
+
+    require_len(40);
+    gcry_mpi_scan(&kem->r, GCRYMPI_FMT_USG, bufp, 20, NULL);
+    gcry_mpi_scan(&kem->s, GCRYMPI_FMT_USG, bufp+20, 20, NULL);
+    bufp += 40; lenp -= 40;
+
+    if (lenp != 0) goto inv;
+
+    return kem;
+inv:
+    free_keyexch(kem);
+    return NULL;
+}
+
+/* Deallocate a KeyExchMsg and all of the data it points to */
+void free_keyexch(KeyExchMsg keyexch)
+{
+    if (!keyexch) return;
+    free(keyexch->raw);
+    gcry_mpi_release(keyexch->p);
+    gcry_mpi_release(keyexch->q);
+    gcry_mpi_release(keyexch->g);
+    gcry_mpi_release(keyexch->e);
+    gcry_mpi_release(keyexch->y);
+    gcry_mpi_release(keyexch->r);
+    gcry_mpi_release(keyexch->s);
+    free(keyexch);
+}
+
+/* Parse a D-H Commit Message into a newly-allocated CommitMsg structure */
+CommitMsg parse_commit(const char *msg)
+{
+    CommitMsg cmsg = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    cmsg = calloc(1, sizeof(struct s_CommitMsg));
+    if (!cmsg) {
+	free(raw);
+	goto inv;
+    }
+
+    cmsg->raw = raw;
+
+    require_len(3);
+
+    cmsg->version = bufp[1];
+
+    if (!memcmp(bufp, "\x00\x03\x02", 3)) {
+	bufp += 3; lenp -= 3;
+	read_int(cmsg->sender_instance);
+	read_int(cmsg->receiver_instance);
+    } else if (!memcmp(bufp, "\x00\x02\x02", 3)) {
+	bufp += 3; lenp -= 3;
+	cmsg->sender_instance = 0;
+	cmsg->receiver_instance = 0;
+    } else goto inv;
+
+    read_int(cmsg->enckeylen);
+    cmsg->enckey = malloc(cmsg->enckeylen);
+    if (!cmsg->enckey && cmsg->enckeylen > 0) goto inv;
+    read_raw(cmsg->enckey, cmsg->enckeylen);
+
+    read_int(cmsg->hashkeylen);
+    cmsg->hashkey = malloc(cmsg->hashkeylen);
+    if (!cmsg->hashkey && cmsg->hashkeylen > 0) goto inv;
+    read_raw(cmsg->hashkey, cmsg->hashkeylen);
+
+    if (lenp != 0) goto inv;
+
+    return cmsg;
+inv:
+    free_commit(cmsg);
+    return NULL;
+}
+
+/* Deallocate a CommitMsg and all of the data it points to */
+void free_commit(CommitMsg cmsg)
+{
+    if (!cmsg) return;
+    free(cmsg->raw);
+    free(cmsg->enckey);
+    free(cmsg->hashkey);
+    free(cmsg);
+}
+
+/* Parse a D-H Key Message into a newly-allocated KeyMsg structure */
+KeyMsg parse_key(const char *msg)
+{
+    KeyMsg kmsg = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    kmsg = calloc(1, sizeof(struct s_KeyMsg));
+    if (!kmsg) {
+	free(raw);
+	goto inv;
+    }
+
+    kmsg->raw = raw;
+
+    require_len(3);
+
+    kmsg->version = bufp[1];
+
+    if (!memcmp(bufp, "\x00\x03\x0a", 3)) {
+	bufp += 3; lenp -= 3;
+	read_int(kmsg->sender_instance);
+	read_int(kmsg->receiver_instance);
+    } else if (!memcmp(bufp, "\x00\x02\x0a", 3)) {
+	bufp += 3; lenp -= 3;
+	kmsg->sender_instance = 0;
+	kmsg->receiver_instance = 0;
+    } else goto inv;
+
+    read_mpi(kmsg->y);
+
+    if (lenp != 0) goto inv;
+
+    return kmsg;
+inv:
+    free_key(kmsg);
+    return NULL;
+}
+
+/* Deallocate a KeyMsg and all of the data it points to */
+void free_key(KeyMsg kmsg)
+{
+    if (!kmsg) return;
+    free(kmsg->raw);
+    gcry_mpi_release(kmsg->y);
+    free(kmsg);
+}
+
+/* Parse a Reveal Signature Message into a newly-allocated RevealSigMsg
+ * structure */
+RevealSigMsg parse_revealsig(const char *msg)
+{
+    RevealSigMsg rmsg = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    rmsg = calloc(1, sizeof(struct s_RevealSigMsg));
+    if (!rmsg) {
+	free(raw);
+	goto inv;
+    }
+
+    rmsg->raw = raw;
+
+    require_len(3);
+
+    rmsg->version = bufp[1];
+
+    if (!memcmp(bufp, "\x00\x03\x11", 3)) {
+	bufp += 3; lenp -= 3;
+	read_int(rmsg->sender_instance);
+	read_int(rmsg->receiver_instance);
+    } else if (!memcmp(bufp, "\x00\x02\x11", 3)) {
+	bufp += 3; lenp -= 3;
+	rmsg->sender_instance = 0;
+	rmsg->receiver_instance = 0;
+    } else goto inv;
+
+    read_int(rmsg->keylen);
+    rmsg->key = malloc(rmsg->keylen);
+    if (!rmsg->key && rmsg->keylen > 0) goto inv;
+    read_raw(rmsg->key, rmsg->keylen);
+
+    read_int(rmsg->encsiglen);
+    rmsg->encsig = malloc(rmsg->encsiglen);
+    if (!rmsg->encsig && rmsg->encsiglen > 0) goto inv;
+    read_raw(rmsg->encsig, rmsg->encsiglen);
+
+    read_raw(rmsg->mac, 20);
+
+    if (lenp != 0) goto inv;
+
+    return rmsg;
+inv:
+    free_revealsig(rmsg);
+    return NULL;
+}
+
+/* Deallocate a RevealSigMsg and all of the data it points to */
+void free_revealsig(RevealSigMsg rmsg)
+{
+    if (!rmsg) return;
+    free(rmsg->raw);
+    free(rmsg->key);
+    free(rmsg->encsig);
+    free(rmsg);
+}
+
+/* Parse a Signature Message into a newly-allocated SignatureMsg structure */
+SignatureMsg parse_signature(const char *msg)
+{
+    SignatureMsg smsg = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    smsg = calloc(1, sizeof(struct s_SignatureMsg));
+    if (!smsg) {
+	free(raw);
+	goto inv;
+    }
+
+    smsg->raw = raw;
+
+    require_len(3);
+
+    smsg->version = bufp[1];
+
+    if (!memcmp(bufp, "\x00\x03\x12", 3)) {
+	bufp += 3; lenp -= 3;
+	read_int(smsg->sender_instance);
+	read_int(smsg->receiver_instance);
+    } else if (!memcmp(bufp, "\x00\x02\x12", 3)) {
+	bufp += 3; lenp -= 3;
+	smsg->sender_instance = 0;
+	smsg->receiver_instance = 0;
+    } else goto inv;
+
+    read_int(smsg->encsiglen);
+    smsg->encsig = malloc(smsg->encsiglen);
+    if (!smsg->encsig && smsg->encsiglen > 0) goto inv;
+    read_raw(smsg->encsig, smsg->encsiglen);
+
+    read_raw(smsg->mac, 20);
+
+    if (lenp != 0) goto inv;
+
+    return smsg;
+inv:
+    free_signature(smsg);
+    return NULL;
+}
+
+/* Deallocate a SignatureMsg and all of the data it points to */
+void free_signature(SignatureMsg smsg)
+{
+    if (!smsg) return;
+    free(smsg->raw);
+    free(smsg->encsig);
+    free(smsg);
+}
+
+/* Parse a Data Message into a newly-allocated DataMsg structure */
+DataMsg parse_datamsg(const char *msg)
+{
+    DataMsg datam = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    unsigned char version;
+    if (!raw) goto inv;
+
+    datam = calloc(1, sizeof(struct s_DataMsg));
+    if (!datam) {
+	free(raw);
+	goto inv;
+    }
+
+    datam->raw = raw;
+    datam->rawlen = lenp;
+    datam->macstart = bufp;
+
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x03\x03", 3) &&
+	memcmp(bufp, "\x00\x02\x03", 3)) goto inv;
+
+    version = bufp[1];
+
+    datam->sender_instance = 0;
+    datam->receiver_instance = 0;
+    datam->version = version;
+    datam->flags = -1;
+    bufp += 3; lenp -= 3;
+
+    if (version == 3) {
+	read_int(datam->sender_instance);
+	read_int(datam->receiver_instance);
+    }
+
+    if (version == 2 || version == 3) {
+	require_len(1);
+	datam->flags = bufp[0];
+	bufp += 1; lenp -= 1;
+    }
+
+    read_int(datam->sender_keyid);
+    read_int(datam->rcpt_keyid);
+    read_mpi(datam->y);
+    read_raw(datam->ctr, 8);
+    read_int(datam->encmsglen);
+    datam->encmsg = malloc(datam->encmsglen);
+    if (!datam->encmsg && datam->encmsglen > 0) goto inv;
+    read_raw(datam->encmsg, datam->encmsglen);
+    datam->macend = bufp;
+    read_raw(datam->mac, 20);
+    read_int(datam->mackeyslen);
+    datam->mackeys = malloc(datam->mackeyslen);
+
+    if (!datam->mackeys && datam->mackeyslen > 0) goto inv;
+
+    read_raw(datam->mackeys, datam->mackeyslen);
+    if (lenp != 0) goto inv;
+
+    return datam;
+inv:
+    free_datamsg(datam);
+    return NULL;
+}
+
+/* Recalculate the MAC on the message, base64-encode the resulting MAC'd
+ * message, and put on the appropriate header and footer.  Return a
+ * newly-allocated pointer to the result, which the caller will have to
+ * free(). */
+char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
+{
+    size_t rawlen, lenp;
+    size_t ylen;
+    size_t base64len;
+    char *outmsg;
+    unsigned char *raw, *bufp;
+    unsigned char version = datamsg->version;
+
+    /* Calculate the size of the message that will result */
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &ylen, datamsg->y);
+    rawlen = 3 + (version == 3 ? 8 : 0) + (version == 2 ||
+	version == 3 ? 1 : 0) + 4 + 4 + 4 + ylen + 8 + 4 +
+	datamsg->encmsglen + 20 + 4 + datamsg->mackeyslen;
+
+    /* Construct the new raw message (note that some of the pieces may
+     * have been altered, so we construct it from scratch). */
+    raw = malloc(rawlen);
+    if (!raw) {
+	fprintf(stderr, "Out of memory!\n");
+	exit(1);
+    }
+    bufp = raw;
+    lenp = rawlen;
+    datamsg->macstart = raw;
+    datamsg->macend = NULL;
+    free(datamsg->raw);
+    datamsg->raw = raw;
+    datamsg->rawlen = rawlen;
+
+
+    memmove(bufp, "\x00", 1);
+    memmove(bufp+1, &version, 1);
+    memmove(bufp+2, "\x03", 1);
+    bufp += 3; lenp -= 3;
+
+    if (version == 3) {
+	write_int(datamsg->sender_instance);
+	write_int(datamsg->receiver_instance);
+    }
+
+    if (version == 2 || version == 3) {
+	bufp[0] = datamsg->flags;
+	bufp += 1; lenp -= 1;
+    }
+
+    write_int(datamsg->sender_keyid);
+    write_int(datamsg->rcpt_keyid);
+    write_mpi(datamsg->y, ylen);
+    write_raw(datamsg->ctr, 8);
+    write_int(datamsg->encmsglen);
+    write_raw(datamsg->encmsg, datamsg->encmsglen);
+    datamsg->macend = bufp;
+
+    /* Recalculate the MAC */
+    sha1hmac(datamsg->mac, mackey, datamsg->macstart,
+	    datamsg->macend - datamsg->macstart);
+
+    write_raw(datamsg->mac, 20);
+    write_int(datamsg->mackeyslen);
+    write_raw(datamsg->mackeys, datamsg->mackeyslen);
+
+    if (lenp != 0) {
+	fprintf(stderr, "Error creating OTR Data Message.\n");
+	exit(1);
+    }
+
+    base64len = 5 + ((datamsg->rawlen + 2) / 3) * 4 + 1 + 1;
+    outmsg = malloc(base64len);
+    if (!outmsg) return NULL;
+
+    memmove(outmsg, "?OTR:", 5);
+    otrl_base64_encode(outmsg + 5, datamsg->raw, datamsg->rawlen);
+    strcpy(outmsg + base64len - 2, ".");
+    return outmsg;
+}
+
+/* Assemble a new Data Message from its pieces.  Return a
+ * newly-allocated string containing the base64 representation. */
+char *assemble_datamsg(unsigned char mackey[20],
+	unsigned char version, unsigned int sender_instance,
+	unsigned int receiver_instance, int flags, unsigned int sender_keyid,
+	unsigned int rcpt_keyid, gcry_mpi_t y,
+	unsigned char ctr[8], unsigned char *encmsg, size_t encmsglen,
+	unsigned char *mackeys, size_t mackeyslen)
+{
+    DataMsg datam = calloc(1, sizeof(struct s_DataMsg));
+    char *newmsg = NULL;
+    if (!datam) goto inv;
+    datam->version = version;
+    datam->flags = flags;
+    datam->sender_instance = sender_instance;
+    datam->receiver_instance = receiver_instance;
+    datam->sender_keyid = sender_keyid;
+    datam->rcpt_keyid = rcpt_keyid;
+    datam->y = gcry_mpi_copy(y);
+    memmove(datam->ctr, ctr, 8);
+    datam->encmsg = malloc(encmsglen);
+    if (!datam->encmsg && encmsglen > 0) goto inv;
+    memmove(datam->encmsg, encmsg, encmsglen);
+    datam->encmsglen = encmsglen;
+    datam->mackeys = malloc(mackeyslen);
+    if (!datam->mackeys && mackeyslen > 0) goto inv;
+    memmove(datam->mackeys, mackeys, mackeyslen);
+    datam->mackeyslen = mackeyslen;
+
+    /* Recalculate the MAC and base64-encode the result */
+    newmsg = remac_datamsg(datam, mackey);
+    free_datamsg(datam);
+    return newmsg;
+inv:
+    free_datamsg(datam);
+    return NULL;
+}
+
+/* Deallocate a DataMsg and all of the data it points to */
+void free_datamsg(DataMsg datamsg)
+{
+    if (!datamsg) return;
+    free(datamsg->raw);
+    gcry_mpi_release(datamsg->y);
+    free(datamsg->encmsg);
+    free(datamsg->mackeys);
+    free(datamsg);
+}
+
+static 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 -1;
+}
+
+/* Convert a string of hex chars to a buffer of unsigned chars. */
+void argv_to_buf(unsigned char **bufp, size_t *lenp, char *arg)
+{
+    unsigned char *buf;
+    size_t len, i;
+
+    *bufp = NULL;
+    *lenp = 0;
+
+    len = strlen(arg);
+    if (len % 2) {
+	fprintf(stderr, "Argument ``%s'' must have even length.\n", arg);
+	return;
+    }
+    buf = malloc(len/2);
+    if (buf == NULL && len > 0) {
+	fprintf(stderr, "Out of memory!\n");
+	return;
+    }
+
+    for(i=0;i<len/2;++i) {
+	int hi = ctoh(arg[2*i]);
+	int lo = ctoh(arg[2*i+1]);
+	if (hi < 0 || lo < 0) {
+	    free(buf);
+	    fprintf(stderr, "Illegal hex char in argument ``%s''.\n", arg);
+	    return;
+	}
+	buf[i] = (hi << 4) + lo;
+    }
+    *bufp = buf;
+    *lenp = len/2;
+}