diff options
Diffstat (limited to '')
-rw-r--r-- | comm/third_party/libotr/toolkit/parse.c | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/comm/third_party/libotr/toolkit/parse.c b/comm/third_party/libotr/toolkit/parse.c new file mode 100644 index 0000000000..aaa0ad59c9 --- /dev/null +++ b/comm/third_party/libotr/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; +} |