예제 #1
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;
}
예제 #2
0
/* 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);
}
예제 #3
0
파일: parse.c 프로젝트: Ri0n/libotr
/* 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;
}
예제 #4
0
/* 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).  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,
	unsigned char *extrakey)
{
    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];
    size_t 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);
    }

    /* 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) {
	err = gcry_error(GPG_ERR_ENOMEM);
	goto err;
    }
    rawlen = otrl_base64_decode(rawmsg, otrtag, msglen);  /* actual size */

    bufp = rawmsg;
    lenp = rawlen;

    macstart = bufp;
    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;
    }

    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->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->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->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 (otrl_mem_differ(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;

    /* 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->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->context_priv->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;
}