diff --git a/src/libomemo.c b/src/libomemo.c index 61f7cef..c45eb07 100644 --- a/src/libomemo.c +++ b/src/libomemo.c @@ -41,6 +41,7 @@ #define ENCRYPTED_NODE_NAME "encrypted" #define HEADER_NODE_NAME "header" #define IV_NODE_NAME "iv" +#define RECIPIENT_NODE_NAME "keys" #define KEY_NODE_NAME "key" #define PAYLOAD_NODE_NAME "payload" #define STORE_NODE_NAME "store" @@ -61,7 +62,9 @@ #define MESSAGE_NODE_FROM_ATTR_NAME "from" #define MESSAGE_NODE_TO_ATTR_NAME "to" #define HEADER_NODE_SID_ATTR_NAME "sid" +#define RECIPIENT_NODE_JID_ATTR_NAME "jid" #define KEY_NODE_RID_ATTR_NAME "rid" +#define KEY_NODE_KEX_ATTR_NAME "kex" #define PUBLISH_NODE_NODE_ATTR_NAME "node" #define SIGNED_PRE_KEY_NODE_ID_ATTR_NAME "signedPreKeyId" #define PRE_KEY_NODE_ID_ATTR_NAME "preKeyId" @@ -1090,45 +1093,43 @@ int omemo_message_prepare_encryption(char * outgoing_message, uint32_t sender_de msg_p->message_node_p = msg_node_p; body_node_p = mxmlFindPath(msg_node_p, BODY_NODE_NAME); - if (!body_node_p) { - ret_val = OMEMO_ERR_MALFORMED_XML; - goto cleanup; - } + if (body_node_p) { + msg_text = mxmlGetOpaque(body_node_p); + if (!msg_text) { + ret_val = OMEMO_ERR_MALFORMED_XML; + goto cleanup; + } - msg_text = mxmlGetOpaque(body_node_p); - if (!msg_text) { - ret_val = OMEMO_ERR_MALFORMED_XML; - goto cleanup; - } - ret_val = crypto_p->aes_gcm_encrypt_func((uint8_t *) msg_text, strlen(msg_text), - msg_p->iv_p, msg_p->iv_len, - msg_p->key_p, msg_p->key_len, - OMEMO_AES_GCM_TAG_LENGTH, - crypto_p->user_data_p, - &ct_p, &ct_len, - &tag_p); - if (ret_val) { - goto cleanup; - } + ret_val = crypto_p->aes_gcm_encrypt_func((uint8_t *) msg_text, strlen(msg_text), + msg_p->iv_p, msg_p->iv_len, + msg_p->key_p, msg_p->key_len, + OMEMO_AES_GCM_TAG_LENGTH, + crypto_p->user_data_p, + &ct_p, &ct_len, + &tag_p); + if (ret_val) { + goto cleanup; + } - msg_p->tag_len = OMEMO_AES_GCM_TAG_LENGTH; - memcpy(msg_p->key_p + msg_p->key_len, tag_p, msg_p->tag_len); + msg_p->tag_len = OMEMO_AES_GCM_TAG_LENGTH; + memcpy(msg_p->key_p + msg_p->key_len, tag_p, msg_p->tag_len); - ret_val = expect_next_node(body_node_p, mxmlGetParent, BODY_NODE_NAME, &body_node_p); - if (ret_val) { - goto cleanup; - } + ret_val = expect_next_node(body_node_p, mxmlGetParent, BODY_NODE_NAME, &body_node_p); + if (ret_val) { + goto cleanup; + } - mxmlRemove(body_node_p); + mxmlRemove(body_node_p); - payload_b64 = g_base64_encode(ct_p, ct_len); - payload_node_p = mxmlNewElement(MXML_NO_PARENT, PAYLOAD_NODE_NAME); - (void) mxmlNewOpaque(payload_node_p, payload_b64); - msg_p->payload_node_p = payload_node_p; + payload_b64 = g_base64_encode(ct_p, ct_len); + payload_node_p = mxmlNewElement(MXML_NO_PARENT, PAYLOAD_NODE_NAME); + (void) mxmlNewOpaque(payload_node_p, payload_b64); + msg_p->payload_node_p = payload_node_p; - if (strip == OMEMO_STRIP_ALL) { - omemo_message_strip_possible_plaintext(msg_p); + if (strip == OMEMO_STRIP_ALL) { + omemo_message_strip_possible_plaintext(msg_p); + } } *message_pp = msg_p; @@ -1153,7 +1154,7 @@ size_t omemo_message_get_key_len(omemo_message * msg_p) { return msg_p->key_len + msg_p->tag_len; } -int omemo_message_add_recipient(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len) { +int omemo_message_add_recipient(omemo_message * msg_p, const char * recipient_name_bare, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len, int kex) { if (!msg_p || !msg_p->header_node_p || !encrypted_key_p) { return OMEMO_ERR_NULL; } @@ -1163,19 +1164,29 @@ int omemo_message_add_recipient(omemo_message * msg_p, uint32_t device_id, const return OMEMO_ERR; } + mxml_node_t * recipient_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, + RECIPIENT_NODE_NAME, RECIPIENT_NODE_JID_ATTR_NAME, + recipient_name_bare, MXML_DESCEND); + if (!recipient_node_p) { + recipient_node_p = mxmlNewElement(MXML_NO_PARENT, RECIPIENT_NODE_NAME); + mxmlElementSetAttr(recipient_node_p, RECIPIENT_NODE_JID_ATTR_NAME, recipient_name_bare); + mxmlAdd(msg_p->header_node_p, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT, recipient_node_p); + } gchar * key_b64 = g_base64_encode(encrypted_key_p, key_len); - mxml_node_t * key_node_p = mxmlNewElement(MXML_NO_PARENT, KEY_NODE_NAME); + mxml_node_t * key_node_p = mxmlNewElement(MXML_NO_PARENT, KEY_NODE_NAME); mxmlElementSetAttr(key_node_p, KEY_NODE_RID_ATTR_NAME, device_id_string); free(device_id_string); + if (kex) + mxmlElementSetAttr(key_node_p, KEY_NODE_KEX_ATTR_NAME, "true"); (void) mxmlNewOpaque(key_node_p, key_b64); - mxmlAdd(msg_p->header_node_p, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT, key_node_p); + mxmlAdd(recipient_node_p, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT, key_node_p); g_free(key_b64); return 0; } int omemo_message_export_encrypted(omemo_message * msg_p, int add_msg, char ** msg_xml) { - if (!msg_p || !msg_p->message_node_p || !msg_p->header_node_p || !msg_p->payload_node_p || !msg_xml) { + if (!msg_p || !msg_p->message_node_p || !msg_p->header_node_p || !msg_xml) { return OMEMO_ERR_NULL; } @@ -1196,7 +1207,8 @@ int omemo_message_export_encrypted(omemo_message * msg_p, int add_msg, char ** m mxmlElementSetAttr(encrypted_node_p, XMLNS_ATTR_NAME, OMEMO_NS); mxmlAdd(encrypted_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, msg_p->header_node_p); - mxmlAdd(encrypted_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, msg_p->payload_node_p); + if (msg_p->payload_node_p) + mxmlAdd(encrypted_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, msg_p->payload_node_p); if (add_msg == OMEMO_ADD_MSG_EME || add_msg == OMEMO_ADD_MSG_BOTH) { eme_node_p = mxmlNewElement(msg_p->message_node_p, EME_NODE_NAME); @@ -1352,19 +1364,35 @@ char * omemo_message_get_recipient_name_bare(omemo_message * msg_p) { return jid_strip_resource(omemo_message_get_recipient_name_full(msg_p)); } -int omemo_message_get_encrypted_key(omemo_message * msg_p, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ) { +int omemo_message_get_encrypted_key(omemo_message * msg_p, const char * recipient_name, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ) { if (!msg_p || !key_pp) { return OMEMO_ERR_NULL; } int ret_val = 0; + gchar * recipient_name_bare = (void *) 0; + mxml_node_t * recipient_node_p = (void *) 0; mxml_node_t * key_node_p = (void *) 0; char * rid_string = (void *) 0; const char * key_b64 = (void *) 0; size_t key_len = 0; - key_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, KEY_NODE_NAME, NULL, NULL, MXML_DESCEND); + if (recipient_name) { + recipient_name_bare = jid_strip_resource(recipient_name); + } else { + recipient_name_bare = omemo_message_get_recipient_name_bare(msg_p); + } + recipient_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, RECIPIENT_NODE_NAME, + RECIPIENT_NODE_JID_ATTR_NAME, recipient_name_bare,MXML_DESCEND); + if (recipient_node_p) { + key_node_p = mxmlFindElement(recipient_node_p, recipient_node_p, KEY_NODE_NAME, + NULL, NULL, MXML_DESCEND); + } else { + // Assuming there is no RECIPIENT_NODEs in the message. + key_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, KEY_NODE_NAME, + NULL, NULL, MXML_DESCEND); + } if (!key_node_p) { // if there is not at least one key, skip the rest of the function ret_val = 0; @@ -1405,6 +1433,7 @@ int omemo_message_get_encrypted_key(omemo_message * msg_p, uint32_t own_device_i *key_len_p = key_len; cleanup: + g_free(recipient_name_bare) free(rid_string); return ret_val; } diff --git a/src/libomemo.h b/src/libomemo.h index 92f82c7..f771e79 100644 --- a/src/libomemo.h +++ b/src/libomemo.h @@ -152,7 +152,7 @@ int omemo_bundle_set_signed_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id * * @param bundle_p Pointer to the bundle. * @param pre_key_id_p Will be set to the pre key id as returned by strtol. - * @param data_pp Will be set to a pointer to the serialized key data. Has to be free()d when done. + * @param data_pp Will be set to a pointer to the serialized key data. Has to be g_free()d when done. * @param data_len_p Will be set to the length of the data. * @return 0 on success, negative on error. */ @@ -172,7 +172,7 @@ int omemo_bundle_set_signature(omemo_bundle * bundle_p, uint8_t * data_p, size_t * Gets the signature from the specified bundle. * * @param bundle_p Pointer to the bundle. - * @param data_pp Will be set to a pointer to the signature data. Has to be free()d when done. + * @param data_pp Will be set to a pointer to the signature data. Has to be g_free()d when done. * @param data_len_p Will be set to the length of the data. * @return 0 on succcess, negative on error. */ @@ -192,7 +192,7 @@ int omemo_bundle_set_identity_key(omemo_bundle * bundle_p, uint8_t * data_p, siz * Gets the identity key from the specified bundle. * * @param bundle_p Pointer to the bundle. - * @param data_pp Will be set to a pointer to the identity key data. Has to be free()d when done. + * @param data_pp Will be set to a pointer to the identity key data. Has to be g_free()d when done. * @param data_len_p Will be set to the length of the data. * @return 0 on success, negative on error. */ @@ -214,7 +214,7 @@ int omemo_bundle_add_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id, uint8 * * @param bundle_p Pointer to the bundle. * @param pre_key_id_p Will be set to the ID of the selected pre key. - * @param data_pp Will be set to a pointer to the serialized public key data. Has to be free()d when done. + * @param data_pp Will be set to a pointer to the serialized public key data. Has to be g_free()d when done. * @param data_len_p Will be set to the length of the data. * @return 0 on success, negative on error. */ @@ -440,16 +440,18 @@ const uint8_t * omemo_message_get_key(omemo_message * msg_p); size_t omemo_message_get_key_len(omemo_message * msg_p); /** - * Add the encrypted symmetric key for a specific device id to the message. + * Add the encrypted symmetric key for a specific device to the message. * Only makes sense on outgoing messages. * * @param msg_p Pointer to the message to add to. + * @param recipient_name_bare The bare JID of the recipient. * @param device_id The recipient device ID. * @param encrypted_key_p The encrypted key data. * @param key_len Length of the encrypted key data. + * @param kex Whether the added key is intended for key exchange, default to 0. * @return 0 on success, negative on error. */ -int omemo_message_add_recipient(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len); +int omemo_message_add_recipient(omemo_message * msg_p, const char * recipient_name_bare, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len, int kex); /** * After all recipients have been added, this function can be used to export the resulting stanza. @@ -502,7 +504,7 @@ const char * omemo_message_get_sender_name_full(omemo_message * msg_p); * Note that there is no "from" attribute in outgoing messages. * * @param msg_p Pointer to the message. - * @return The bare JID. Has to be free()d. + * @return The bare JID. Has to be g_free()d. */ char * omemo_message_get_sender_name_bare(omemo_message * msg_p); @@ -523,16 +525,17 @@ const char * omemo_message_get_recipient_name_full(omemo_message * msg_p); char * omemo_message_get_recipient_name_bare(omemo_message * msg_p); /** - * Gets the encrypted key data for a specific device ID (usually your own so you can decrypt it). + * Gets the encrypted key data for a specific device (usually your own so you can decrypt it). * * @param msg_p Pointer to the message. + * @param recipient_name The JID get the encrypted key for, could be 0 for the intended recipient of the message. * @param own_device_id The device ID to get the encrypted key for. * @param key_pp Will be set to the encrypted key data, or NULL if there is no data for the specified ID. - * Has to be free()d. + * Has to be g_free()d. * @param key_len_p Will be set to length of encrypted key data. * @return 0 on success, negative on error. Note that success does not mean a key was found. */ -int omemo_message_get_encrypted_key(omemo_message * msg_p, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ); +int omemo_message_get_encrypted_key(omemo_message * msg_p, const char * recipient_name, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ); /** * Using the decrypted symmetric key, this method decrypts the payload and exports the original stanza.