/* 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); } /* Skip over the "?OTR:" */ otrtag += 5; msglen -= 5; /* Base64-decode the message */ 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, msglen); /* actual size */ bufp = rawmsg; lenp = rawlen; require_len(3); version = bufp[1]; 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; } free(rawmsg); return gcry_error(GPG_ERR_NO_ERROR); invval: free(rawmsg); return gcry_error(GPG_ERR_INV_VALUE); }
/* 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); }
/* 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; }
void net_msg_write_string(net_outgoing_message *msg, const char *data) { short slen = strlen(data); require_len(msg, slen + sizeof(int16_t)); net_msg_write_int16(msg, slen); strncpy(msg->buffer + msg->pos, data, (size_t)slen); msg->pos += slen; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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); memmove(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; } memmove(data, bufp, datalen); data[datalen] = '\0'; bufp += datalen; lenp -= datalen; macend = bufp; require_len(20); memmove(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 */ memmove(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; }
void net_msg_write_int32(net_outgoing_message *msg, int32_t data) { require_len(msg, sizeof(int32_t)); *((int32_t *)(msg->buffer + msg->pos)) = htonl(data); msg->pos += sizeof(int32_t); }
// Integer data types void net_msg_write_byte(net_outgoing_message *msg, char data) { require_len(msg, sizeof(char)); msg->buffer[msg->pos] = data; msg->pos += sizeof(char); }
/* 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; }