Merge remote-tracking branch 'private/dev_lime'

Conflicts:
	coreapi/linphonecall.c
This commit is contained in:
Jehan Monnier 2015-03-12 15:08:35 +01:00
commit 5035992c64
22 changed files with 1769 additions and 61 deletions

View file

@ -58,6 +58,7 @@ LOCAL_SRC_FILES := \
linphone_tunnel_config.c \
localplayer.c \
lpc2xml.c \
lime.c \
lpconfig.c \
message_storage.c \
misc.c \
@ -118,7 +119,8 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../../belle-sip/include \
$(LOCAL_PATH)/../../../gen \
$(LOCAL_PATH)/../../externals/libxml2/include \
$(LOCAL_PATH)/../../externals/build/libxml2
$(LOCAL_PATH)/../../externals/build/libxml2 \
$(LOCAL_PATH)/../../externals/polarssl/include
LOCAL_LDLIBS += -llog -ldl -lz

View file

@ -76,7 +76,7 @@ case $target in
LIBS="$LIBS -framework CoreFoundation -framework AudioToolbox -framework CoreAudio -framework Foundation -framework QuartzCore -framework OpenGLES -framework UIKit -framework AVFoundation"
ios_found=yes
;;
x86_64-apple-darwin|i686-apple-darwin*)
x86_64-apple-darwin*|i686-apple-darwin*)
MSPLUGINS_CFLAGS=""
dnl use macport installation
AS_IF([test -d "/opt/local/share/aclocal"], [ACLOCAL_MACOS_FLAGS="-I /opt/local/share/aclocal"])

View file

@ -65,6 +65,7 @@ liblinphone_la_SOURCES=\
linphonecore_utils.h \
localplayer.c \
lpc2xml.c \
lime.c \
lpconfig.c lpconfig.h \
lsd.c \
message_storage.c \

View file

@ -18,6 +18,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sal_impl.h"
#include "linphonecore.h"
#include "private.h"
#include "lime.h"
#include <libxml/xmlwriter.h>
static void process_error( SalOp* op) {
if (op->dir == SalOpDirOutgoing) {
op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
@ -56,14 +61,22 @@ static void process_response_event(void *op_base, const belle_sip_response_event
}
static bool_t is_rcs_filetransfer(belle_sip_header_content_type_t* content_type) {
return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0;
return (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0)
&& ((strcmp("vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0) || (strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0));
}
static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) {
return strcmp("text",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0;
}
static bool_t is_cipher_xml(belle_sip_header_content_type_t* content_type) {
return (strcmp("xml",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("cipher",belle_sip_header_content_type_get_subtype(content_type))==0)
|| (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0);
}
static bool_t is_external_body(belle_sip_header_content_type_t* content_type) {
return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0;
@ -74,7 +87,7 @@ static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) {
}
static void add_message_accept(belle_sip_message_t *msg){
belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, application/vnd.gsma.rcs-ft-http+xml"));
belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, xml/cipher, application/vnd.gsma.rcs-ft-http+xml, application/cipher.vnd.gsma.rcs-ft-http+xml"));
}
void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
@ -90,13 +103,65 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
char* from;
bool_t plain_text=FALSE;
bool_t external_body=FALSE;
bool_t cipher_xml=FALSE;
bool_t rcs_filetransfer=FALSE;
uint8_t *decryptedMessage = NULL;
from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
/* check if we have a xml/cipher message to be decrypted */
if (content_type && (cipher_xml=is_cipher_xml(content_type))) {
/* access the zrtp cache to get keys needed to decipher the message */
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
FILE *CACHEFD = fopen(lc->zrtp_secrets_cache, "r+");
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to decrypt message");
} else {
int cacheSize;
uint8_t *cacheString;
int retval;
xmlDocPtr cacheXml;
fseek(CACHEFD, 0L, SEEK_END); /* Position to end of file */
cacheSize = ftell(CACHEFD); /* Get file length */
rewind(CACHEFD); /* Back to start of file */
cacheString = (uint8_t *)malloc(cacheSize*sizeof(uint8_t)+1); /* string must be null terminated */
fread(cacheString, 1, cacheSize, CACHEFD);
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc(cacheString);
free(cacheString);
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), &decryptedMessage);
if (retval != 0) {
ms_warning("Unable to decrypt message, reason : %s - op [%p]", lime_error_code_to_string(retval), op);
free(decryptedMessage);
xmlFreeDoc(cacheXml);
resp = belle_sip_response_create_from_request(req,488);
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(lc->zrtp_secrets_cache, "w+");
fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD);
xmlFree(xmlStringOutput);
fclose(CACHEFD);
}
xmlFreeDoc(cacheXml);
}
}
rcs_filetransfer=is_rcs_filetransfer(content_type);
if (content_type && ((plain_text=is_plain_text(content_type))
|| (external_body=is_external_body(content_type))
|| (rcs_filetransfer=is_rcs_filetransfer(content_type)))) {
|| (decryptedMessage!=NULL)
|| rcs_filetransfer)) {
SalMessage salmsg;
char message_id[256]={0};
@ -111,7 +176,12 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
,belle_sip_header_call_id_get_call_id(call_id)
,belle_sip_header_cseq_get_seq_number(cseq));
salmsg.from=from;
salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
if (cipher_xml) {
salmsg.text = (char *)decryptedMessage;
} else { /* message body wasn't ciphered */
salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
}
salmsg.url=NULL;
salmsg.content_type = NULL;
if (rcs_filetransfer) { /* if we have a rcs file transfer, set the type, message body (stored in salmsg.text) contains all needed information to retrieve the file */
@ -125,6 +195,8 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
salmsg.message_id=message_id;
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
op->base.root->callbacks.text_received(op,&salmsg);
free(decryptedMessage);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
@ -154,11 +226,13 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
sal_process_incoming_message(op,event);
}
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg){
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg, const char *peer_uri){
belle_sip_request_t* req;
char content_type_raw[256];
size_t content_length = msg?strlen(msg):0;
time_t curtime=time(NULL);
uint8_t *multipartEncryptedMessage = NULL;
int retval;
if (op->dialog){
/*for SIP MESSAGE that are sent in call's dialog*/
@ -179,13 +253,66 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op)));
}
}
/* shall we try to encrypt the message?*/
if ((strcmp(content_type, "xml/cipher") == 0) || ((strcmp(content_type, "application/cipher.vnd.gsma.rcs-ft-http+xml") == 0))) {
/* access the zrtp cache to get keys needed to cipher the message */
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
FILE *CACHEFD = fopen(lc->zrtp_secrets_cache, "r+");
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to encrypt message");
sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
return 0;
} else {
int cacheSize;
uint8_t *cacheString;
xmlDocPtr cacheXml;
int retval;
fseek(CACHEFD, 0L, SEEK_END); /* Position to end of file */
cacheSize = ftell(CACHEFD); /* Get file length */
rewind(CACHEFD); /* Back to start of file */
cacheString = (uint8_t *)malloc(cacheSize*sizeof(uint8_t)+1); /* string must be null terminated */
fread(cacheString, 1, cacheSize, CACHEFD);
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc(cacheString);
free(cacheString);
retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg, (uint8_t *)peer_uri, &multipartEncryptedMessage);
if (retval != 0) {
ms_warning("Unable to encrypt message for %s : %s - op [%p]", peer_uri, lime_error_code_to_string(retval), op);
xmlFreeDoc(cacheXml);
free(multipartEncryptedMessage);
sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
return 0;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(lc->zrtp_secrets_cache, "w+");
fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD);
xmlFree(xmlStringOutput);
fclose(CACHEFD);
content_length = strlen((const char *)multipartEncryptedMessage);
}
xmlFreeDoc(cacheXml);
}
}
snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type);
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),msg,content_length);
return sal_op_send_request(op,req);
belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),(multipartEncryptedMessage==NULL)?msg:(const char *)multipartEncryptedMessage,content_length);
retval = sal_op_send_request(op,req);
free(multipartEncryptedMessage);
return retval;
}
int sal_message_reply(SalOp *op, SalReason reason){
@ -200,7 +327,7 @@ int sal_message_reply(SalOp *op, SalReason reason){
}
int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) {
return sal_message_send(op,from,to,"text/plain",msg);
return sal_message_send(op,from,to,"text/plain",msg, NULL);
}
static belle_sip_listener_callbacks_t op_message_callbacks={0};

View file

@ -26,6 +26,8 @@
#include "private.h"
#include "lpconfig.h"
#include "belle-sip/belle-sip.h"
#include "lime.h"
#include "ortp/b64.h"
#include <libxml/parser.h>
#include <libxml/xmlwriter.h>
@ -34,6 +36,7 @@
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
#define FILE_TRANSFER_KEY_SIZE 32
static LinphoneChatMessageCbs * linphone_chat_message_cbs_new(void) {
return belle_sip_object_new(LinphoneChatMessageCbs);
@ -176,6 +179,7 @@ static void process_io_error_upload(void *data, const belle_sip_io_error_event_t
if (msg->cb) {
msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->cb_ud);
}
if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, LinphoneChatMessageStateNotDelivered);
}
@ -244,18 +248,49 @@ static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_
/* if we've not reach the end of file yet, ask for more data*/
if (offset<linphone_content_get_size(chatMsg->file_transfer_information)){
/* get data from call back */
if (linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, *size);
if (lb == NULL) *size = 0;
else {
*size = linphone_buffer_get_size(lb);
memcpy(buffer, linphone_buffer_get_content(lb), *size);
linphone_buffer_unref(lb);
if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) { /* if we have a key to cipher the message, use it! */
char *plainBuffer;
/* get data from callback to a plainBuffer */
/* if this chunk is not the last one, the lenght must be a multiple of block cipher size(16 bytes)*/
if (offset+*size < linphone_content_get_size(chatMsg->file_transfer_information)) {
*size -=(*size%16);
}
plainBuffer = (char *)malloc(*size);
if (linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, *size);
if (lb == NULL) *size = 0;
else {
*size = linphone_buffer_get_size(lb);
memcpy(plainBuffer, linphone_buffer_get_content(lb), *size);
linphone_buffer_unref(lb);
}
} else {
/* Legacy */
linphone_core_notify_file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, plainBuffer, size);
}
lime_encryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), (unsigned char *)linphone_content_get_key(chatMsg->file_transfer_information), *size, plainBuffer, (char*)buffer);
free(plainBuffer);
/* check if we reach the end of file */
if (offset+*size >= linphone_content_get_size(chatMsg->file_transfer_information)) {
/* conclude file ciphering by calling it context with a zero size */
lime_encryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), NULL, 0, NULL, NULL);
}
} else {
/* Legacy */
linphone_core_notify_file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, buf, size);
/* get data from call back */
if (linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, *size);
if (lb == NULL) *size = 0;
else {
*size = linphone_buffer_get_size(lb);
memcpy(buffer, linphone_buffer_get_content(lb), *size);
linphone_buffer_unref(lb);
}
} else {
/* Legacy */
linphone_core_notify_file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, buf, size);
}
}
}
@ -286,8 +321,19 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co
char *first_part_header;
belle_sip_body_handler_t *first_part_bh;
/* temporary storage for the Content-disposition header value */
first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(msg->file_transfer_information));
/* shall we encrypt the file */
if (msg->chat_room->lc->lime == 1) {
char keyBuffer[FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of initial vector */
/* generate a random 192 bits key + 64 bits of initial vector and store it into the file_transfer_information->key field of the message */
sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
linphone_content_set_key(msg->file_transfer_information, keyBuffer, FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
/* temporary storage for the Content-disposition header value : use a generic filename to not leak it
* Actual filename stored in msg->file_transfer_information->name will be set in encrypted message sended to the */
first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"filename.txt\"");
} else {
/* temporary storage for the Content-disposition header value */
first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(msg->file_transfer_information));
}
/* create a user body handler to take care of the file and add the content disposition and content-type headers */
if (msg->file_transfer_filepath != NULL) {
@ -324,11 +370,65 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co
l=belle_http_request_listener_create_from_callbacks(&cbs,msg);
belle_http_provider_send_request(msg->chat_room->lc->http_provider,msg->http_request,l);
}
if (code == 200 ) { /* file has been uploaded correctly, get server reply and send it */
if (code == 200 ) { /* file has been uplaoded correctly, get server reply and send it */
const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
belle_sip_object_unref(msg->http_request);
msg->http_request = NULL;
msg->message = ms_strdup(body);
/* if we have an encryption key for the file, we must insert it into the message and restore the correct filename */
if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
/* parse the message body */
xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)body);
xmlNodePtr cur = xmlDocGetRootElement(xmlMessageBody);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
while (cur!=NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
if(!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for : add a file-key children node */
xmlNodePtr fileInfoNodeChildren = cur->xmlChildrenNode; /* need to parse the children node to update the file-name one */
/* convert key to base64 */
int b64Size = b64_encode(NULL, FILE_TRANSFER_KEY_SIZE, NULL, 0);
char *keyb64 = (char *)malloc(b64Size+1);
int xmlStringLength;
b64Size = b64_encode(linphone_content_get_key(msg->file_transfer_information), FILE_TRANSFER_KEY_SIZE, keyb64, b64Size);
keyb64[b64Size] = '\0'; /* libxml need a null terminated string */
/* add the node containing the key to the file-info node */
xmlNewTextChild(cur, NULL, (const xmlChar *)"file-key", (const xmlChar *)keyb64);
xmlFree(typeAttribute);
free(keyb64);
/* look for the file-name node and update its content */
while (fileInfoNodeChildren!=NULL) {
if (!xmlStrcmp(fileInfoNodeChildren->name, (const xmlChar *)"file-name")) { /* we found a the file-name node, update its content with the real filename */
/* update node content */
xmlNodeSetContent(fileInfoNodeChildren, (const xmlChar *)(linphone_content_get_name(msg->file_transfer_information)));
break;
}
fileInfoNodeChildren = fileInfoNodeChildren->next;
}
/* dump the xml into msg->message */
xmlDocDumpFormatMemoryEnc(xmlMessageBody, (xmlChar **)&msg->message, &xmlStringLength, "UTF-8", 0);
break;
}
xmlFree(typeAttribute);
}
cur = cur->next;
}
}
xmlFreeDoc(xmlMessageBody);
} else { /* no encryption key, transfer in plain, just copy the message sent by server */
msg->message = ms_strdup(body);
}
msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
if (msg->cb) {
msg->cb(msg, LinphoneChatMessageStateFileTransferDone, msg->cb_ud);
@ -339,7 +439,6 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co
_linphone_chat_room_send_message(msg->chat_room, msg);
}
}
}
@ -632,20 +731,29 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
linphone_configure_op(cr->lc,op,cr->peer_url,msg->custom_headers,lp_config_get_int(cr->lc->config,"sip","chat_msg_with_contact",0));
sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
}
if (msg->external_body_url) {
content_type=ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"",msg->external_body_url);
sal_message_send(op,identity,cr->peer,content_type, NULL);
sal_message_send(op,identity,cr->peer,content_type, NULL, NULL);
ms_free(content_type);
} else { /* the message is either text or have a file transfer using RCS recommendation */
if (msg->content_type == NULL) { /* if no content type is specified, it is a text message */
sal_text_send(op, identity, cr->peer,msg->message);
} else {
if (cr->lc->lime == 1) { /* shall we try to encrypt messages? */
linphone_chat_message_ref(msg); /* ref the message or it may be destroyed by callback if the encryption failed */
if ((msg->content_type != NULL) && (strcmp(msg->content_type, "application/vnd.gsma.rcs-ft-http+xml") == 0 )) { /* it's a file transfer, content type shall be set to application/cipher.vnd.gsma.rcs-ft-http+xml*/
sal_message_send(op, identity, cr->peer, "application/cipher.vnd.gsma.rcs-ft-http+xml", msg->message, linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr)));
} else {
sal_message_send(op, identity, cr->peer, "xml/cipher", msg->message, linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr)));
}
} else {
sal_message_send(op, identity, cr->peer, msg->content_type, msg->message);
// Remove the message to prevent the xml from the file uplaod to be stored in the database
ms_free(msg->message);
msg->message = NULL;
if (msg->content_type == NULL) {
sal_text_send(op, identity, cr->peer,msg->message);
} else { /* rcs file transfer */
sal_message_send(op, identity, cr->peer, msg->content_type, msg->message, NULL);
}
}
}
msg->dir=LinphoneChatMessageOutgoing;
msg->from=linphone_address_new(identity);
msg->storage_id=linphone_chat_message_store(msg);
@ -752,6 +860,18 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
file_url = xmlGetProp(cur, (const xmlChar *)"url");
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) { /* there is a key in the message: file has been encrypted */
/* convert the key from base 64 */
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
int keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
/* decode the key into local key buffer */
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength); /* duplicate key value into the linphone content private structure */
xmlFree(keyb64);
free(keyBuffer);
}
cur=cur->next;
}
xmlFree(typeAttribute);
@ -1069,7 +1189,7 @@ static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *
}
content = linphone_chat_room_create_is_composing_xml(cr);
if (content != NULL) {
sal_message_send(op, identity, cr->peer, "application/im-iscomposing+xml", content);
sal_message_send(op, identity, cr->peer, "application/im-iscomposing+xml", content, NULL);
ms_free(content);
}
}
@ -1217,14 +1337,36 @@ static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
LinphoneCore *lc = chatMsg->chat_room->lc;
if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size);
linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* Legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, (char *)buffer, size);
/* first call may be with a zero size, ignore it */
if (size == 0) {
return;
}
if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) { /* we have a key, we must decrypt the file */
/* get data from callback to a plainBuffer */
char *plainBuffer = (char *)malloc(size);
lime_decryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), (unsigned char *)linphone_content_get_key(chatMsg->file_transfer_information), size, plainBuffer, (char *)buffer);
if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new_from_data((unsigned char *)plainBuffer, size);
linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, plainBuffer, size);
}
free(plainBuffer);
} else { /* regular file, no deciphering */
if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size);
linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* Legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, (char *)buffer, size);
}
}
return;
}
@ -1293,6 +1435,10 @@ static void linphone_chat_process_response_from_get_file(void *data, const belle
if (code==200) {
LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
LinphoneCore *lc = chatMsg->chat_room->lc;
/* if the file was encrypted, finish the decryption and free context */
if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) {
lime_decryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), NULL, 0, NULL, NULL);
}
/* file downloaded succesfully, call again the callback with size at zero */
if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new();

View file

@ -29,6 +29,8 @@ static void linphone_content_destroy(LinphoneContent *content) {
if (content->lcp.data) belle_sip_free(content->lcp.data);
if (content->lcp.encoding) belle_sip_free(content->lcp.encoding);
if (content->lcp.name) belle_sip_free(content->lcp.name);
if (content->lcp.key) belle_sip_free(content->lcp.key);
/* note : crypto context is allocated/destroyed by the encryption function */
}
}
@ -38,6 +40,7 @@ static void linphone_content_clone(LinphoneContent *obj, const LinphoneContent *
linphone_content_set_subtype(obj, linphone_content_get_subtype(ref));
linphone_content_set_encoding(obj, linphone_content_get_encoding(ref));
linphone_content_set_name(obj, linphone_content_get_name(ref));
linphone_content_set_key(obj, linphone_content_get_key(ref), linphone_content_get_key_size(ref));
if (linphone_content_get_buffer(ref) != NULL) {
linphone_content_set_buffer(obj, linphone_content_get_buffer(ref), linphone_content_get_size(ref));
} else {
@ -161,12 +164,36 @@ void linphone_content_set_name(LinphoneContent *content, const char *name) {
}
}
size_t linphone_content_get_key_size(const LinphoneContent *content) {
return content->lcp.keyLength;
}
const char * linphone_content_get_key(const LinphoneContent *content) {
return content->lcp.key;
}
void linphone_content_set_key(LinphoneContent *content, const char *key, const size_t keyLength) {
if (content->lcp.key != NULL) {
belle_sip_free(content->lcp.key);
content->lcp.key = NULL;
}
if (key != NULL) {
content->lcp.key = belle_sip_malloc(keyLength);
memcpy(content->lcp.key, key, keyLength);
}
}
/* crypto context is managed(allocated/freed) by the encryption function, so provide the address of field in the private structure */
void ** linphone_content_get_cryptoContext_address(LinphoneContent *content) {
return &(content->lcp.cryptoContext);
}
LinphoneContent * linphone_content_new(void) {
LinphoneContent *content = belle_sip_object_new(LinphoneContent);
belle_sip_object_ref(content);
content->owned_fields = TRUE;
content->lcp.cryptoContext = NULL; /* this field is managed externally by encryption/decryption functions so be careful to initialise it to NULL */
return content;
}

View file

@ -51,6 +51,9 @@ struct _LinphoneContentPrivate{
When provided by callback #LinphoneCoreFileTransferSendCb or #LinphoneCoreFileTransferRecvCb, it states the total number of bytes of the transfered file*/
char *encoding; /**<The encoding of the data buffer, for example "gzip"*/
char *name; /**< used by RCS File transfer messages to store the original filename of the file to be downloaded from server */
char *key; /**< used by RCS File transfer messages to store the key to encrypt file if needed */
size_t keyLength; /**< Length of key in bytes */
void *cryptoContext; /**< crypto context used to encrypt file for RCS file transfer */
};
/**

View file

@ -33,6 +33,7 @@ struct _LinphoneInfoMessage{
SalCustomHeader *headers;
};
/**
* Destroy a LinphoneInfoMessage
**/

790
coreapi/lime.c Normal file
View file

@ -0,0 +1,790 @@
#include "lime.h"
#include "linphonecore.h"
#include "ortp/b64.h"
#include "polarssl/gcm.h"
/* check polarssl version */
#include <polarssl/version.h>
#if POLARSSL_VERSION_NUMBER >= 0x01030000 /* for Polarssl version 1.3 */
#include "polarssl/sha256.h"
#else /* for Polarssl version 1.2 */
#include "polarssl/sha2.h"
#endif
/**
* @brief convert an hexa char [0-9a-fA-F] into the corresponding unsigned integer value
* Any invalid char will be converted to zero without any warning
*
* @param[in] inputChar a char which shall be in range [0-9a-fA-F]
*
* @return the unsigned integer value in range [0-15]
*/
uint8_t lime_charToByte(uint8_t inputChar) {
/* 0-9 */
if (inputChar>0x29 && inputChar<0x3A) {
return inputChar - 0x30;
}
/* a-f */
if (inputChar>0x60 && inputChar<0x67) {
return inputChar - 0x57; /* 0x57 = 0x61(a) + 0x0A*/
}
/* A-F */
if (inputChar>0x40 && inputChar<0x47) {
return inputChar - 0x37; /* 0x37 = 0x41(a) + 0x0A*/
}
/* shall never arrive here, string is not Hex*/
return 0;
}
/**
* @brief convert a byte which value is in range [0-15] into an hexa char [0-9a-fA-F]
*
* @param[in] inputByte an integer which shall be in range [0-15]
*
* @return the hexa char [0-9a-f] corresponding to the input
*/
uint8_t lime_byteToChar(uint8_t inputByte) {
inputByte &=0x0F; /* restrict the input value to range [0-15] */
/* 0-9 */
if(inputByte<0x0A) {
return inputByte+0x30;
}
/* a-f */
return inputByte + 0x57;
}
/**
* @brief Convert an hexadecimal string into the corresponding byte buffer
*
* @param[out] outputBytes The output bytes buffer, must have a length of half the input string buffer
* @param[in] inputString The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0)
* @param[in] inputStringLength The lenght in chars of the string buffer, output is half this length
*/
void lime_strToUint8(uint8_t *outputBytes, uint8_t *inputString, uint16_t inputStringLength) {
int i;
for (i=0; i<inputStringLength/2; i++) {
outputBytes[i] = (lime_charToByte(inputString[2*i]))<<4 | lime_charToByte(inputString[2*i+1]);
}
}
/**
* @brief Convert a byte buffer into the corresponding hexadecimal string
*
* @param[out] outputString The output string buffer, must have a length of twice the input bytes buffer
* @param[in] inputBytes The input bytes buffer
* @param[in] inputBytesLength The lenght in bytes buffer, output is twice this length
*/
void lime_int8ToStr(uint8_t *outputString, uint8_t *inputBytes, uint16_t inputBytesLength) {
int i;
for (i=0; i<inputBytesLength; i++) {
outputString[2*i] = lime_byteToChar((inputBytes[i]>>4)&0x0F);
outputString[2*i+1] = lime_byteToChar(inputBytes[i]&0x0F);
}
}
int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]) {
xmlNodePtr cur;
xmlChar *selfZidHex;
if (cacheBuffer == NULL ) {
return LIME_INVALID_CACHE;
}
cur = xmlDocGetRootElement(cacheBuffer);
/* if we found a root element, parse its children node */
if (cur!=NULL)
{
cur = cur->xmlChildrenNode;
}
selfZidHex = NULL;
while (cur!=NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */
selfZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode, 1);
/* copy it to the output buffer and add the null termination */
memcpy(selfZid, selfZidHex, 24);
selfZid[24]='\0';
break;
}
cur = cur->next;
}
/* did we found a ZID? */
if (selfZidHex == NULL) {
return LIME_INVALID_CACHE;
}
xmlFree(selfZidHex);
return 0;
}
int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys) {
xmlNodePtr cur;
/* parse the file to get all peer matching the sipURI given in associatedKeys*/
if (cacheBuffer == NULL ) { /* there is no cache return error */
return LIME_INVALID_CACHE;
}
/* reset number of associated keys and their buffer */
associatedKeys->associatedZIDNumber = 0;
associatedKeys->peerKeys = NULL;
cur = xmlDocGetRootElement(cacheBuffer);
/* if we found a root element, parse its children node */
if (cur!=NULL)
{
cur = cur->xmlChildrenNode;
}
while (cur!=NULL) { /* loop on all peer nodes */
uint8_t matchingURIFlag = 0; /* this flag is set to one if we found the requested sipURI in the current peer node */
if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))) { /* found a peer node, check if there is a matching sipURI node in it */
xmlNodePtr peerNodeChildren = cur->xmlChildrenNode;
matchingURIFlag = 0;
/* loop on children nodes until the end or we found the matching sipURI */
while (peerNodeChildren!=NULL && matchingURIFlag==0) {
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"uri")) { /* found a peer an URI node, check the content */
xmlChar *uriNodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
if (!xmlStrcmp(uriNodeContent, (const xmlChar *)associatedKeys->peerURI)) { /* found a match with requested URI */
matchingURIFlag=1;
}
xmlFree(uriNodeContent);
}
peerNodeChildren = peerNodeChildren->next;
}
if (matchingURIFlag == 1) { /* we found a match for the URI in this peer node, extract the keys, session Id and index values */
/* allocate a new limeKey_t structure to hold the retreived keys */
limeKey_t *currentPeerKeys = (limeKey_t *)malloc(sizeof(limeKey_t));
uint8_t itemFound = 0; /* count the item found, we must get all of the requested infos: 5 nodes*/
uint8_t pvs = 0;
peerNodeChildren = cur->xmlChildrenNode; /* reset peerNodeChildren to the first child of node */
while (peerNodeChildren!=NULL && itemFound<5) {
xmlChar *nodeContent = NULL;
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(currentPeerKeys->peerZID, nodeContent, 24);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(currentPeerKeys->key, nodeContent, 64);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(currentPeerKeys->sessionId, nodeContent, 64);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(sessionIndexBuffer, nodeContent, 8);
/* convert it back to a uint32_t (MSByte first)*/
currentPeerKeys->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
itemFound++;
}
xmlFree(nodeContent);
peerNodeChildren = peerNodeChildren->next;
}
/* check if we have all the requested information and the PVS flag is set to 1 */
if (itemFound == 5 && pvs == 1) {
associatedKeys->associatedZIDNumber +=1;
/* extend array of pointer to limeKey_t structures to add the one we found */
associatedKeys->peerKeys = (limeKey_t **)realloc(associatedKeys->peerKeys, (associatedKeys->associatedZIDNumber)*sizeof(limeKey_t *));
/* add the new entry at the end */
associatedKeys->peerKeys[associatedKeys->associatedZIDNumber-1] = currentPeerKeys;
} else {
free(currentPeerKeys);
}
}
}
cur = cur->next;
}
return 0;
}
int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) {
uint8_t peerZidHex[25];
/* to check we collect all the information needed from the cache and that pvs(boolean for previously verified Sas) is set in cache */
uint8_t itemFound = 0;
uint8_t pvs = 0;
xmlNodePtr cur;
if (cacheBuffer == NULL ) { /* there is no cache return error */
return LIME_INVALID_CACHE;
}
/* get the given ZID into hex format */
lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
peerZidHex[24]='\0'; /* must be a null terminated string */
cur = xmlDocGetRootElement(cacheBuffer);
/* if we found a root element, parse its children node */
if (cur!=NULL)
{
cur = cur->xmlChildrenNode;
}
while (cur!=NULL) { /* loop on all peer nodes */
if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
while (peerNodeChildren != NULL && itemFound<4) { /* look for the tag we want to read : rcvKey, rcvSId, rcvIndex and pvs*/
xmlChar *nodeContent = NULL;
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(associatedKey->key, nodeContent, 64);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(associatedKey->sessionId, nodeContent, 64);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(sessionIndexBuffer, nodeContent, 8);
/* convert it back to a uint32_t (MSByte first)*/
associatedKey->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
itemFound++;
}
xmlFree(nodeContent);
peerNodeChildren = peerNodeChildren->next;
}
xmlFree(currentZidHex);
break; /* we parsed the peer node we were looking for, get out of the main while */
}
xmlFree(currentZidHex);
}
cur = cur->next;
}
/* if we manage to find the correct key information and that pvs is set to 1, return 0 (success) */
if ((pvs == 1) && (itemFound == 4)) {
return 0;
}
/* otherwise, key wasn't found or is invalid */
return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
}
int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) {
xmlNodePtr cur;
uint8_t peerZidHex[25];
uint8_t keyHex[65]; /* key is 32 bytes long -> 64 bytes string + null termination */
uint8_t sessionIdHex[65]; /* sessionId is 32 bytes long -> 64 bytes string + null termination */
uint8_t sessionIndexHex[9]; /* sessionInedx is an uint32_t : 4 bytes long -> 8 bytes string + null termination */
uint8_t itemFound = 0;
if (cacheBuffer == NULL ) { /* there is no cache return error */
return LIME_INVALID_CACHE;
}
/* get the given ZID into hex format */
lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
peerZidHex[24]='\0'; /* must be a null terminated string */
cur = xmlDocGetRootElement(cacheBuffer);
/* if we found a root element, parse its children node */
if (cur!=NULL)
{
cur = cur->xmlChildrenNode;
}
/* convert the given tag content to null terminated Hexadecimal strings */
lime_int8ToStr(keyHex, associatedKey->key, 32);
keyHex[64] = '\0';
lime_int8ToStr(sessionIdHex, associatedKey->sessionId, 32);
sessionIdHex[64] = '\0';
sessionIndexHex[0] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>28)&0x0F));
sessionIndexHex[1] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>24)&0x0F));
sessionIndexHex[2] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>20)&0x0F));
sessionIndexHex[3] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>16)&0x0F));
sessionIndexHex[4] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>12)&0x0F));
sessionIndexHex[5] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>8)&0x0F));
sessionIndexHex[6] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>4)&0x0F));
sessionIndexHex[7] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex)&0x0F));
sessionIndexHex[8] = '\0';
while (cur!=NULL && itemFound<3) { /* loop on all peer nodes */
if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
while (peerNodeChildren != NULL && itemFound<3) { /* look for the tag we want to write */
if (role == LIME_RECEIVER) { /* writing receiver key */
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
itemFound++;
}
} else { /* writing sender key */
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
itemFound++;
}
if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
itemFound++;
}
}
peerNodeChildren = peerNodeChildren->next;
}
}
xmlFree(currentZidHex);
}
cur = cur->next;
}
return 0;
}
int lime_deriveKey(limeKey_t *key) {
uint8_t inputData[55];
uint8_t derivedKey[32];
if (key == NULL) {
return LIME_UNABLE_TO_DERIVE_KEY;
}
if ((key->key == NULL) || (key->sessionId == NULL)) {
return LIME_UNABLE_TO_DERIVE_KEY;
}
/* Derivation is made derived Key = HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||0x00000100)*/
/* total data to be hashed is 55 bytes : 4 + 10 + 1 + 32 + 4 + 4 */
inputData[0] = 0x00;
inputData[1] = 0x00;
inputData[2] = 0x00;
inputData[3] = 0x01;
memcpy(inputData+4, "MessageKey", 10);
inputData[14] = 0x00;
memcpy(inputData+15, key->sessionId, 32);
inputData[47] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
inputData[48] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
inputData[49] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
inputData[50] = (uint8_t)(key->sessionIndex&0x000000FF);
inputData[51] = 0x00;
inputData[52] = 0x00;
inputData[53] = 0x01;
inputData[54] = 0x00;
/* derive the key in a temp buffer */
#if POLARSSL_VERSION_NUMBER >= 0x01030000 /* for Polarssl version 1.3 */
sha256_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
#else /* for Polarssl version 1.2 */
sha2_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
#endif /* POLARSSL_VERSION_NUMBER */
/* overwrite the old key with the derived one */
memcpy(key->key, derivedKey, 32);
/* increment the session Index */
key->sessionIndex += 1;
return 0;
}
void lime_freeKeys(limeURIKeys_t associatedKeys) {
int i;
/* free all associated keys */
for (i=0; i< associatedKeys.associatedZIDNumber; i++) {
if (associatedKeys.peerKeys[i] != NULL) {
free(associatedKeys.peerKeys[i]);
associatedKeys.peerKeys[i] = NULL;
}
}
free(associatedKeys.peerKeys);
/* free sipURI string */
free(associatedKeys.peerURI);
}
int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
uint8_t authenticatedData[28];
gcm_context gcmContext;
/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
memcpy(authenticatedData, selfZID, 12);
memcpy(authenticatedData+12, key->peerZID, 12);
authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);
/* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
/* tag is 16 bytes long and is set in the 16 first bytes of the encrypted message */
gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
gcm_crypt_and_tag(&gcmContext, GCM_ENCRYPT, messageLength, key->key+24, 8, authenticatedData, 28, plainMessage, encryptedMessage+16, 16, encryptedMessage);
gcm_free(&gcmContext);
return 0;
}
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
gcm_context *gcmContext;
if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
*cryptoContext = (void *)gcmContext;
gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
gcm_starts(gcmContext, GCM_ENCRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
} else { /* this is not the first call, get the context */
gcmContext = (gcm_context *)*cryptoContext;
}
if (length != 0) {
gcm_update(gcmContext, length, (const unsigned char *)plain, (unsigned char *)cipher);
} else { /* lenght is 0, finish the stream */
gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
gcm_free(gcmContext);
free(*cryptoContext);
*cryptoContext = NULL;
}
return 0;
}
int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
gcm_context *gcmContext;
if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
*cryptoContext = (void *)gcmContext;
gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
gcm_starts(gcmContext, GCM_DECRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
} else { /* this is not the first call, get the context */
gcmContext = (gcm_context *)*cryptoContext;
}
if (length != 0) {
gcm_update(gcmContext, length, (const unsigned char *)cipher, (unsigned char *)plain);
} else { /* lenght is 0, finish the stream */
gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
gcm_free(gcmContext);
free(*cryptoContext);
*cryptoContext = NULL;
}
return 0;
}
int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) {
uint8_t authenticatedData[28];
gcm_context gcmContext;
int retval;
/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
memcpy(authenticatedData, key->peerZID, 12);
memcpy(authenticatedData+12, selfZID, 12);
authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);
/* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
/* tag is 16 bytes long and is the 16 first bytes of the encrypted message */
gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
/* messageLength-16 is the length of encrypted data, messageLength include the 16 bytes tag included at the begining of encryptedMessage */
retval = gcm_auth_decrypt(&gcmContext, messageLength-16, key->key+24, 8, authenticatedData, 28, encryptedMessage, 16, encryptedMessage+16, plainMessage);
gcm_free(&gcmContext);
/* add the null termination char */
plainMessage[messageLength-16] = '\0';
return retval;
}
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
uint8_t selfZidHex[25];
uint8_t selfZid[12]; /* same data but in byte buffer */
uint32_t encryptedMessageLength;
limeURIKeys_t associatedKeys;
xmlDocPtr xmlOutputMessage;
xmlNodePtr rootNode;
int i;
int xmlStringLength;
/* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
}
lime_strToUint8(selfZid, selfZidHex, 24);
/* encrypted message length is plaintext + 16 for tag */
encryptedMessageLength = strlen((char *)message) + 16;
/* retrieve keys associated to the peer URI */
associatedKeys.peerURI = (uint8_t *)malloc(strlen((char *)peerURI)+1);
strcpy((char *)(associatedKeys.peerURI), (char *)peerURI);
associatedKeys.associatedZIDNumber = 0;
associatedKeys.peerKeys = NULL;
if (lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys) != 0) {
lime_freeKeys(associatedKeys);
return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
}
if (associatedKeys.associatedZIDNumber == 0) {
lime_freeKeys(associatedKeys);
return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
}
/* create an xml doc to hold the multipart message */
xmlOutputMessage = xmlNewDoc((const xmlChar *)"1.0");
/* root tag is "doc" */
rootNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"doc", NULL);
xmlDocSetRootElement(xmlOutputMessage, rootNode);
/* add the self ZID child */
xmlNewTextChild(rootNode, NULL, (const xmlChar *)"ZID", selfZidHex);
/* loop on all keys found */
for (i=0; i<associatedKeys.associatedZIDNumber; i++) {
uint8_t peerZidHex[25];
uint8_t sessionIndexHex[9];
xmlNodePtr msgNode;
int b64Size;
char *encryptedMessageb64;
/* encrypt message with current key */
limeKey_t *currentKey = associatedKeys.peerKeys[i];
/* encrypted message include a 16 bytes tag */
uint8_t *encryptedMessage = (uint8_t *)malloc(encryptedMessageLength);
lime_encryptMessage(currentKey, message, strlen((char *)message), selfZid, encryptedMessage);
/* add a "msg" node the the output message, doc node is :
* <msg>
* <pzid>peerZID</pzid>
* <index>session index</index>
* <text>ciphertext</text>
* </msg> */
msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
lime_int8ToStr(peerZidHex, currentKey->peerZID, 12);
peerZidHex[24] = '\0';
sessionIndexHex[0] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>28)&0x0F));
sessionIndexHex[1] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>24)&0x0F));
sessionIndexHex[2] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>20)&0x0F));
sessionIndexHex[3] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>16)&0x0F));
sessionIndexHex[4] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>12)&0x0F));
sessionIndexHex[5] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>8)&0x0F));
sessionIndexHex[6] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>4)&0x0F));
sessionIndexHex[7] = lime_byteToChar((uint8_t)((currentKey->sessionIndex)&0x0F));
sessionIndexHex[8] = '\0';
xmlNewTextChild(msgNode, NULL, (const xmlChar *)"pzid", peerZidHex);
xmlNewTextChild(msgNode, NULL, (const xmlChar *)"index", sessionIndexHex);
/* convert the cipherText to base 64 */
b64Size = b64_encode(NULL, encryptedMessageLength, NULL, 0);
encryptedMessageb64 = (char *)malloc(b64Size+1);
b64Size = b64_encode(encryptedMessage, encryptedMessageLength, encryptedMessageb64, b64Size);
encryptedMessageb64[b64Size] = '\0'; /* libxml need a null terminated string */
xmlNewTextChild(msgNode, NULL, (const xmlChar *)"text", (const xmlChar *)encryptedMessageb64);
free(encryptedMessage);
free(encryptedMessageb64);
/* add the message Node into the doc */
xmlAddChild(rootNode, msgNode);
/* update the key used */
lime_deriveKey(currentKey);
lime_setCachedKey(cacheBuffer, currentKey, LIME_SENDER);
}
/* dump the whole message doc into the output */
xmlDocDumpFormatMemoryEnc(xmlOutputMessage, output, &xmlStringLength, "UTF-8", 0);
xmlFreeDoc(xmlOutputMessage);
lime_freeKeys(associatedKeys);
return 0;
}
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) {
int retval;
uint8_t selfZidHex[25];
uint8_t selfZid[12]; /* same data but in byte buffer */
limeKey_t associatedKey;
xmlChar *peerZidHex = NULL;
xmlNodePtr cur;
uint8_t *encryptedMessage = NULL;
uint32_t encryptedMessageLength = 0;
uint32_t usedSessionIndex = 0;
xmlDocPtr xmlEncryptedMessage;
if (cacheBuffer == NULL) {
return LIME_INVALID_CACHE;
}
/* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
lime_strToUint8(selfZid, selfZidHex, 24);
/* parse the message into an xml doc */
/* make sure we have a valid xml message before trying to parse it */
if (memcmp(message, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", 38) != 0 ) {
return LIME_INVALID_ENCRYPTED_MESSAGE;
}
xmlEncryptedMessage = xmlParseDoc((const xmlChar *)message);
if (xmlEncryptedMessage == NULL) {
return LIME_INVALID_ENCRYPTED_MESSAGE;
}
/* retrieve the sender ZID which is the first child of root */
cur = xmlDocGetRootElement(xmlEncryptedMessage);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
if ((!xmlStrcmp(cur->name, (const xmlChar *)"ZID"))){ /* sender ZID found, extract it */
peerZidHex = xmlNodeListGetString(xmlEncryptedMessage, cur->xmlChildrenNode, 1);
/* convert it from hexa string to bytes string and set the result in the associatedKey structure */
lime_strToUint8(associatedKey.peerZID, peerZidHex, strlen((char *)peerZidHex));
cur = cur->next;
}
}
if (peerZidHex != NULL) {
/* get from cache the matching key */
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
if (retval != 0) {
xmlFree(peerZidHex);
xmlFreeDoc(xmlEncryptedMessage);
return retval;
}
/* retrieve the portion of message which is encrypted with our key */
while (cur != NULL) { /* loop on all "msg" node in the message */
xmlNodePtr msgChildrenNode = cur->xmlChildrenNode;
xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1); /* pZID is the first element of msg */
if (!xmlStrcmp(currentZidHex, (const xmlChar *)selfZidHex)) { /* we found the msg node we are looking for */
/* get the index (second node in the msg one) */
xmlChar *sessionIndexHex;
xmlChar *encryptedMessageb64;
msgChildrenNode = msgChildrenNode->next;
sessionIndexHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
| (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
| (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
| (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
| (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
| (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
| (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
| (((uint32_t)lime_charToByte(sessionIndexHex[7])));
xmlFree(sessionIndexHex);
/* get the encrypted message */
msgChildrenNode = msgChildrenNode->next;
/* convert the cipherText from base 64 */
encryptedMessageb64 = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
encryptedMessageLength = b64_decode((char *)encryptedMessageb64, strlen((char *)encryptedMessageb64), NULL, 0);
encryptedMessage = (uint8_t *)malloc(encryptedMessageLength);
encryptedMessageLength = b64_decode((char *)encryptedMessageb64, strlen((char *)encryptedMessageb64), encryptedMessage, encryptedMessageLength);
xmlFree(encryptedMessageb64);
}
cur = cur->next;
xmlFree(currentZidHex);
}
}
xmlFree(peerZidHex);
xmlFreeDoc(xmlEncryptedMessage);
/* do we have retrieved correctly all the needed data */
if (encryptedMessage == NULL) {
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
/* shall we derive our key before going for decryption */
if (usedSessionIndex < associatedKey.sessionIndex) {
/* something wen't wrong with the cache, this shall never happend */
free(encryptedMessage);
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
if ((usedSessionIndex - associatedKey.sessionIndex > MAX_DERIVATION_NUMBER) ) {
/* we missed to many messages, ask for a cache reset via a ZRTP call */
free(encryptedMessage);
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
while (usedSessionIndex>associatedKey.sessionIndex) {
lime_deriveKey(&associatedKey);
}
/* decrypt the message */
*output = (uint8_t *)malloc(encryptedMessageLength - 16 +1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
retval = lime_decryptMessage(&associatedKey, encryptedMessage, encryptedMessageLength, selfZid, *output);
free(encryptedMessage);
if (retval!=0 ) {
free(*output);
*output = NULL;
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
/* update used key */
lime_deriveKey(&associatedKey);
lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);
return 0;
}
char *lime_error_code_to_string(int errorCode) {
switch (errorCode) {
case LIME_INVALID_CACHE: return "Invalid ZRTP cache";
case LIME_UNABLE_TO_DERIVE_KEY: return "Unable to derive Key";
case LIME_UNABLE_TO_ENCRYPT_MESSAGE: return "Unable to encrypt message";
case LIME_UNABLE_TO_DECRYPT_MESSAGE: return "Unable to decrypt message";
case LIME_NO_VALID_KEY_FOUND_FOR_PEER: return "No valid key found";
case LIME_INVALID_ENCRYPTED_MESSAGE: return "Invalid encrypted message";
}
return "Unknow error";
}

192
coreapi/lime.h Normal file
View file

@ -0,0 +1,192 @@
#ifndef LIME_H
#define LIME_H
#define LIME_INVALID_CACHE 0x1001
#define LIME_UNABLE_TO_DERIVE_KEY 0x1002
#define LIME_UNABLE_TO_ENCRYPT_MESSAGE 0x1004
#define LIME_UNABLE_TO_DECRYPT_MESSAGE 0x1008
#define LIME_NO_VALID_KEY_FOUND_FOR_PEER 0x1010
#define LIME_INVALID_ENCRYPTED_MESSAGE 0x1020
/* this define the maximum key derivation number allowed to get the caches back in sync in case of missed messages */
#define MAX_DERIVATION_NUMBER 100
#define LIME_SENDER 0x01
#define LIME_RECEIVER 0x02
#include <stdint.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlwriter.h>
/**
* @brief Structure holding all needed material to encrypt/decrypt Messages */
typedef struct limeKey_struct {
uint8_t key[32]; /**< a 256 bit key used to encrypt/decrypt message */
uint8_t sessionId[32]; /**< a session id used to derive key */
uint32_t sessionIndex; /**< an index to count number of derivation */
uint8_t peerZID[12]; /**< the ZID associated to this key */
} limeKey_t;
/**
* @brief Store the differents keys associated to a sipURI */
typedef struct limeURIKeys_struct {
limeKey_t **peerKeys; /**< an array of all the key material associated to each ZID matching the specified URI */
uint16_t associatedZIDNumber; /**< previous array length */
uint8_t *peerURI; /**< the sip URI associated to all the keys, must be a null terminated string */
} limeURIKeys_t;
/**
* @brief Retrieve selfZID from cache
*
* @param[in] cacheBuffer The xmlDoc containing current cache
* @param[out] selfZid The ZID found as a 24 hexa char string null terminated
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]);
/**
* @brief Get from cache all the senders keys associated to the given URI
* peerKeys field from associatedKeys param must be NULL when calling this function.
* Structure content must then be freed using lime_freeKeys function
*
* @param[in] cacheBuffer The xmlDoc containing current cache
* @param[in/out] associatedKeys Structure containing the peerURI. After this call contains all key material associated to the given URI. Must be then freed through lime_freeKeys function
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys);
/**
* @brief Get the receiver key associated to the ZID given in the associatedKey parameter
*
* @param[in] cacheBuffer The xmlDoc containing current cache
* @param[in/out] associatedKey Structure containing the peerZID and will store the retrieved key
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey);
/**
* @brief Set in cache the given key material, association is made by ZID contained in the associatedKey parameter
*
* @param[out] cacheBuffer The xmlDoc containing current cache to be updated
* @param[in/out] associatedKey Structure containing the key and ZID to identify the peer node to be updated
* @param[in] role Can be LIME_SENDER or LIME_RECEIVER, specify which key we want to update
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role);
/**
* @brief Free all allocated data in the associated keys structure
* Note, this will also free the peerURI string which then must have been allocated
*
* @param[in/out] associatedKeys The structure to be cleaned
*
*/
__attribute__ ((visibility ("default"))) void lime_freeKeys(limeURIKeys_t associatedKeys);
/**
* @brief Derive in place the key given in parameter and increment session index
* Derivation is made derived Key = HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||256)
*
* @param[in/out] key The structure containing the original key which will be overwritten, the sessionId and SessionIndex
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_deriveKey(limeKey_t *key);
/**
* @brief encrypt a message with the given key
*
* @param[in] key Key to use: first 192 bits are used as key, last 64 bits as init vector
* @param[in] message The string to be encrypted
* @param[in] messageLength The length in bytes of the message to be encrypted
* @param[in] selfZID The self ZID is use in authentication tag computation
* @param[out] encryptedMessage A buffer to hold the output, ouput length is input's one + 16 for the authentication tag
* Authentication tag is set at the begining of the encrypted Message
*
* @return 0 on success, error code otherwise
*
*/
__attribute__ ((visibility ("default"))) int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
/**
* @brief Encrypt a file before transfering it to the server, encryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
*
* @param[in/out] cryptoContext The context used to encrypt the file using AES-GCM. Is created at first call(if null)
* @param[in] key 256 bits : 192 bits of key || 64 bits of Initial Vector
* @param[in] length Length of data to be encrypted, if 0 it will conclude the encryption
* @param[in] plain Plain data to be encrypted (length bytes)
* @param[out] cipher Output to a buffer allocated by caller, at least length bytes available
*
* @return 0 on success, error code otherwise
*
*/
__attribute__ ((visibility ("default"))) int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher);
/**
* @brief Decrypt a file retrieved from server, decryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
*
* @param[in/out] cryptoContext The context used to decrypt the file using AES-GCM. Is created at first call(if null)
* @param[in] key 256 bits : 192 bits of key || 64 bits of Initial Vector
* @param[in] length Length of data to be decrypted, if 0 it will conclude the decryption
* @param[out] plain Output to a buffer allocated by caller, at least length bytes available
* @param[in] cipher Cipher text to be decrypted(length bytes)
*
* @return 0 on success, error code otherwise
*
*/
__attribute__ ((visibility ("default"))) int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher);
/**
* @brief decrypt and authentify a message with the given key
*
* @param[in] key Key to use: first 192 bits are used as key, last 64 bits as init vector
* @param[in] message The string to be decrypted
* @param[in] messageLength The length in bytes of the message to be decrypted (this include the 16 bytes tag at the begining of the message)
* @param[in] selfZID The self ZID is use in authentication tag computation
* @param[out] plainMessage A buffer to hold the output, ouput length is input's one - 16 for the authentication tag + 1 for null termination char
* Authentication tag is retrieved at the begining of the encrypted Message
*
* @return 0 on success, error code otherwise
*
*/
__attribute__ ((visibility ("default"))) int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage);
/**
* @brief create the encrypted multipart xml message from plain text and destination URI
* Retrieve in cache the needed keys which are then updated. Output buffer is allocated and must be freed by caller
*
* @param[in/out] cacheBuffer The xmlDoc containing current cache, get the keys and selfZID from it, updated by this function with derivated keys
* @param[in] message The plain text message to be encrypted
* @param[in] peerURI The destination URI, associated keys will be found in cache
* @param[out] output The output buffer, allocated and set with the encrypted message xml body(null terminated string). Must be freed by caller
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output);
/**
* @brief decrypt a multipart xml message
* Retrieve in cache the needed key which is then updated. Output buffer is allocated and must be freed by caller
*
* @param[in/out] cacheBuffer The xmlDoc containing current cache, get the key and selfZID from it, updated by this function with derivated keys
* @param[in] message The multipart message, contain one or several part identified by destination ZID, one shall match the self ZID retrieved from cache
* @param[out] output The output buffer, allocated and set with the decrypted message(null terminated string). Must be freed by caller
*
* @return 0 on success, error code otherwise
*/
__attribute__ ((visibility ("default"))) int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output);
/**
* @brief given a readable version of error code generated by Lime functions
* @param[in] errorCode The error code
* @return a string containing the error description
*/
char *lime_error_code_to_string(int errorCode);
#endif /* LIME_H */

View file

@ -2752,6 +2752,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut
memset(&params,0,sizeof(MSZrtpParams));
/*call->current_params.media_encryption will be set later when zrtp is activated*/
params.zid_file=lc->zrtp_secrets_cache;
params.uri= linphone_address_as_string_uri_only((call->dir==LinphoneCallIncoming) ? call->log->from : call->log->to);
setZrtpCryptoTypesParameters(&params,call->core);
audio_stream_enable_zrtp(call->audiostream,&params);
#if VIDEO_ENABLED

View file

@ -891,6 +891,8 @@ static void sip_config_read(LinphoneCore *lc)
tmp=lp_config_get_int(lc->config,"sip","guess_hostname",1);
linphone_core_set_guess_hostname(lc,tmp);
tmp=lp_config_get_int(lc->config,"sip","lime",0);
linphone_core_set_lime(lc,tmp);
tmp=lp_config_get_int(lc->config,"sip","inc_timeout",30);
linphone_core_set_inc_timeout(lc,tmp);
@ -1814,6 +1816,15 @@ bool_t linphone_core_get_guess_hostname(LinphoneCore *lc){
return lc->sip_conf.guess_hostname;
}
/**
* Tells to LinphoneCore to use Linphone Instant Messaging encryption
*
*/
void linphone_core_set_lime(LinphoneCore *lc, bool_t val){
lc->lime=val;
lp_config_set_int(lc->config,"sip","lime",val);
}
/**
* Same as linphone_core_get_primary_contact() but the result is a LinphoneAddress object
* instead of const char*

View file

@ -2249,6 +2249,8 @@ LINPHONE_PUBLIC const char * linphone_core_get_identity(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_set_guess_hostname(LinphoneCore *lc, bool_t val);
LINPHONE_PUBLIC bool_t linphone_core_get_guess_hostname(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_set_lime(LinphoneCore *lc, bool_t val);
LINPHONE_PUBLIC bool_t linphone_core_ipv6_enabled(LinphoneCore *lc);
LINPHONE_PUBLIC void linphone_core_enable_ipv6(LinphoneCore *lc, bool_t val);

View file

@ -801,6 +801,8 @@ struct _LinphoneCore
belle_tls_verify_policy_t *http_verify_policy;
MSList *tones;
LinphoneReason chat_deny_code;
bool_t lime;
char *file_transfer_server;
const char **supported_formats;
LinphoneContent *log_collection_upload_information;
LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable
@ -1114,6 +1116,37 @@ void set_playback_gain_db(AudioStream *st, float gain);
LinphoneMediaDirection media_direction_from_sal_stream_dir(SalStreamDir dir);
SalStreamDir sal_dir_from_call_params_dir(LinphoneMediaDirection cpdir);
/*****************************************************************************
* LINPHONE CONTENT PRIVATE ACCESSORS *
****************************************************************************/
/**
* Get the key associated with a RCS file transfer message if encrypted
* @param[in] content LinphoneContent object.
* @return The key to encrypt/decrypt the file associated to this content.
*/
const char *linphone_content_get_key(const LinphoneContent *content);
/**
* Get the size of key associated with a RCS file transfer message if encrypted
* @param[in] content LinphoneContent object.
* @return The key size in bytes
*/
size_t linphone_content_get_key_size(const LinphoneContent *content);
/**
* Set the key associated with a RCS file transfer message if encrypted
* @param[in] content LinphoneContent object.
* @param[in] key The key to be used to encrypt/decrypt file associated to this content.
*/
void linphone_content_set_key(LinphoneContent *content, const char *key, const size_t keyLength);
/**
* Get the address of the crypto context associated with a RCS file transfer message if encrypted
* @param[in] content LinphoneContent object.
* @return The address of the pointer to the crypto context. Crypto context is managed(alloc/free)
* by the encryption/decryption functions, so we give the address to store/retrieve the pointer
*/
void ** linphone_content_get_cryptoContext_address(LinphoneContent *content);
#ifdef ANDROID
void linphone_core_wifi_lock_acquire(LinphoneCore *lc);
void linphone_core_wifi_lock_release(LinphoneCore *lc);

View file

@ -710,7 +710,7 @@ int sal_unregister(SalOp *h);
/*Messaging */
int sal_text_send(SalOp *op, const char *from, const char *to, const char *text);
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg);
int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg, const char *peer_uri);
int sal_message_reply(SalOp *op, SalReason reason);
/*presence Subscribe/notify*/

@ -1 +1 @@
Subproject commit 531a8a80e4cba81468ba4a79905f1a7a054649d0
Subproject commit 82f9cf317da9199454f722f37a493c3a228876cc

2
tester/ZIDCache.xml Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<cache><selfZID>ef7692d0792a67491ae2d44e</selfZID><peer><ZID>005dbe0399643d953a2202dd</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>pipo1@pipo.com</uri><sndKey>963c57bb28e62068d2df23e8f9b771932d3c57bb28e62068d2df23e8f9b77193</sndKey><rcvKey>ffd9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>fcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000069</sndIndex><rcvIndex>000001e2</rcvIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>pipo1@pipo.com</uri><sndKey>123456789012345678901234567890123456765431262068d2df23e8f9b77193</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000001</sndIndex><rcvIndex>00000000</rcvIndex><pvs>01</pvs></peer></cache>

2
tester/ZIDCacheAlice.xml Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<cache><selfZID>ef7692d0792a67491ae2d44e</selfZID><peer><ZID>005dbe0399643d953a2202dd</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>sip:pauline@sip.example.org</uri><sndKey>eeee4fe260a61bb2ab85740cce180f66096c29167b4b10806ff42c964b1884a2</sndKey><rcvKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000007a</sndIndex><rcvIndex>000001cf</rcvIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>sip:pauline@sip.example.org</uri><sndKey>72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a9176</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000000f</sndIndex><rcvIndex>00000000</rcvIndex></peer></cache>

4
tester/ZIDCacheBob.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<cache><selfZID>005dbe0399643d953a2202dd</selfZID>
<peer><ZID>ef7692d0792a67491ae2d44e</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>sip:marie@sip.example.org</uri><rcvKey>eeee4fe260a61bb2ab85740cce180f66096c29167b4b10806ff42c964b1884a2</rcvKey><sndKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</sndKey><rcvSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvIndex>0000007a</rcvIndex><sndIndex>000001cf</sndIndex><pvs>01</pvs></peer>
<peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>sip:marie@sip.example.org</uri><sndKey>81e6e6362c34dc974263d1f77cbb9a8d6d6a718330994379099a8fa19fb12faa</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000002e</sndIndex><rcvIndex>00000000</rcvIndex><pvs>01</pvs></peer></cache>

View file

@ -243,6 +243,9 @@ typedef struct _stats {
int number_of_video_windows_created;
int number_of_LinphoneFileTransferDownloadSuccessful;
int number_of_LinphoneCoreLogCollectionUploadStateDelivered;
int number_of_LinphoneCoreLogCollectionUploadStateNotDelivered;
int number_of_LinphoneCoreLogCollectionUploadStateInProgress;
}stats;

View file

@ -244,12 +244,54 @@ static void collect_files_changing_size() {
collect_cleanup(marie);
}
static void logCollectionUploadStateChangedCb(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info) {
stats* counters = get_stats(lc);
switch(state) {
case LinphoneCoreLogCollectionUploadStateInProgress:
counters->number_of_LinphoneCoreLogCollectionUploadStateInProgress++;
break;
case LinphoneCoreLogCollectionUploadStateDelivered:
counters->number_of_LinphoneCoreLogCollectionUploadStateDelivered++;
CU_ASSERT_TRUE(strlen(info)>0)
break;
case LinphoneCoreLogCollectionUploadStateNotDelivered:
counters->number_of_LinphoneCoreLogCollectionUploadStateNotDelivered++;
break;
}
}
static void upload_collected_traces() {
LinphoneCoreManager* marie = setup(TRUE);
int waiting = 100;
LinphoneCoreVTable *v_table = linphone_core_v_table_new();
v_table->log_collection_upload_state_changed = logCollectionUploadStateChangedCb;
linphone_core_add_listener(marie->lc, v_table);
linphone_core_set_log_collection_max_file_size(5000);
linphone_core_set_log_collection_upload_server_url(marie->lc,"https://www.linphone.org:444/lft.php");
// Generate some logs
while (--waiting) ms_error("(test error)Waiting %d...", waiting);
linphone_core_compress_log_collection(marie->lc);
linphone_core_upload_log_collection(marie->lc);
CU_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphoneCoreLogCollectionUploadStateDelivered,1));
/*try 2 times*/
waiting=100;
linphone_core_reset_log_collection(marie->lc);
while (--waiting) ms_error("(test error)Waiting %d...", waiting);
linphone_core_compress_log_collection(marie->lc);
linphone_core_upload_log_collection(marie->lc);
CU_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphoneCoreLogCollectionUploadStateDelivered,2));
collect_cleanup(marie);
}
test_t log_collection_tests[] = {
{ "No file when disabled", collect_files_disabled},
{ "Collect files filled when enabled", collect_files_filled},
{ "Logs collected into small file", collect_files_small_size},
{ "Logs collected when decreasing max size", collect_files_changing_size},
{ "Upload collected traces", upload_collected_traces}
};
test_suite_t log_collection_test_suite = {

View file

@ -22,6 +22,7 @@
#include "linphonecore.h"
#include "private.h"
#include "liblinphone_tester.h"
#include "lime.h"
#ifdef MSG_STORAGE_ENABLED
#include <sqlite3.h>
@ -87,7 +88,7 @@ void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent*
}
}
static char big_file [128000]; /* a buffer to simulate a big file for the file transfer message test */
char big_file[128000]; /* a buffer to simulate a big file for the file transfer message test */
/*
* function called when the file transfer is initiated. file content should be feed into object LinphoneContent
@ -179,9 +180,11 @@ void liblinphone_tester_chat_message_msg_state_changed(LinphoneChatMessage *msg,
static void text_message(void) {
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to;
LinphoneChatRoom* chat_room;
char* to = linphone_address_as_string(marie->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc,to);
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc,to);
ms_free(to);
{
int dummy=0;
@ -269,11 +272,15 @@ static void text_message_with_credential_from_auth_cb(void) {
}
static void text_message_with_privacy(void) {
char *to;
LinphoneChatRoom* chat_room;
LinphoneProxyConfig* pauline_proxy;
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneProxyConfig* pauline_proxy;
char* to = linphone_address_as_string(marie->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc,to);
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc,to);
ms_free(to);
/*test proxy config privacy*/
@ -343,12 +350,16 @@ static void text_message_compatibility_mode(void) {
static void text_message_with_ack(void) {
int leaked_objects;
int begin;
LinphoneCoreManager* marie;
LinphoneCoreManager* pauline;
belle_sip_object_enable_leak_detector(TRUE);
begin=belle_sip_object_get_object_count();
marie = linphone_core_manager_new( "marie_rc");
pauline = linphone_core_manager_new( "pauline_rc");
{
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to = linphone_address_as_string(marie->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc,to);
LinphoneChatMessage* message = linphone_chat_room_create_message(chat_room,"Bli bli bli \n blu");
@ -376,11 +387,13 @@ static void text_message_with_ack(void) {
static void text_message_with_external_body(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to = linphone_address_as_string(marie->identity);
char *to = linphone_address_as_string(marie->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc,to);
LinphoneChatMessage* message = linphone_chat_room_create_message(chat_room,"Bli bli bli \n blu");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(message);
linphone_chat_message_set_external_body_url(message,message_external_body_url="http://www.linphone.org");
{
int dummy=0;
wait_for_until(marie->lc,pauline->lc,&dummy,1,100); /*just to have time to purge message stored in the server*/
@ -453,6 +466,7 @@ static void file_transfer_message(void) {
char *receive_filepath = ms_strdup_printf("%s/receive_file.dump", liblinphone_tester_writable_dir_prefix);
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -571,6 +585,88 @@ static void small_file_transfer_message(void) {
linphone_core_manager_destroy(pauline);
}
static void lime_file_transfer_message(void) {
int i;
char *to;
FILE *ZIDCacheMarieFD, *ZIDCachePaulineFD;
LinphoneCoreManager *marie, *pauline;
LinphoneChatRoom *chat_room;
LinphoneContent *content;
LinphoneChatMessage *message;
LinphoneChatMessageCbs *cbs;
/* setting dummy file content to something */
const char* big_file_content="big file";
for (i=0;i<sizeof(big_file);i+=strlen(big_file_content))
memcpy(big_file+i, big_file_content, strlen(big_file_content));
big_file[0]=*"S";
big_file[sizeof(big_file)-1]=*"E";
marie = linphone_core_manager_new( "marie_rc");
pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
/* make sure lime is enabled */
linphone_core_set_lime(marie->lc, 1);
linphone_core_set_lime(pauline->lc, 1);
/* set the zid caches files : create two ZID cache from this valid one inserting the auto-generated sip URI for the peer account as keys in ZID cache are indexed by peer sip uri */
ZIDCacheMarieFD = fopen("tmpZIDCacheMarie.xml", "w");
ZIDCachePaulineFD = fopen("tmpZIDCachePauline.xml", "w");
fprintf(ZIDCacheMarieFD, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cache><selfZID>ef7692d0792a67491ae2d44e</selfZID><peer><ZID>005dbe0399643d953a2202dd</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>08df5907d30959b8cb70f6fff2d8febd88fb41b0c8afc39e4b972f86dd5cfe2d</sndKey><rcvKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000078</sndIndex><rcvIndex>000001cf</rcvIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a9176</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000000f</sndIndex><rcvIndex>00000000</rcvIndex></peer></cache>", linphone_address_as_string_uri_only(pauline->identity), linphone_address_as_string_uri_only(pauline->identity));
fprintf(ZIDCachePaulineFD, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cache><selfZID>005dbe0399643d953a2202dd</selfZID><peer><ZID>ef7692d0792a67491ae2d44e</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><rcvKey>08df5907d30959b8cb70f6fff2d8febd88fb41b0c8afc39e4b972f86dd5cfe2d</rcvKey><sndKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</sndKey><rcvSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvIndex>00000078</rcvIndex><sndIndex>000001cf</sndIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>81e6e6362c34dc974263d1f77cbb9a8d6d6a718330994379099a8fa19fb12faa</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000002e</sndIndex><rcvIndex>00000000</rcvIndex><pvs>01</pvs></peer></cache>", linphone_address_as_string_uri_only(marie->identity), linphone_address_as_string_uri_only(marie->identity));
fclose(ZIDCacheMarieFD);
fclose(ZIDCachePaulineFD);
linphone_core_set_zrtp_secrets_file(marie->lc, "tmpZIDCacheMarie.xml");
linphone_core_set_zrtp_secrets_file(pauline->lc, "tmpZIDCachePauline.xml");
/* Globally configure an http file transfer server. */
linphone_core_set_file_transfer_server(pauline->lc,"https://www.linphone.org:444/lft.php");
/* create a chatroom on pauline's side */
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc,to);
ms_free(to);
/* create a file transfer message */
content = linphone_core_create_content(pauline->lc);
linphone_content_set_type(content,"text");
linphone_content_set_subtype(content,"plain");
linphone_content_set_size(content,sizeof(big_file)); /*total size to be transfered*/
linphone_content_set_name(content,"big_file.txt");
message = linphone_chat_room_create_file_transfer_message(chat_room, content);
{
int dummy=0;
wait_for_until(marie->lc,pauline->lc,&dummy,1,100); /*just to have time to purge message stored in the server*/
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
}
cbs = linphone_chat_message_get_callbacks(message);
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
linphone_chat_message_cbs_set_file_transfer_send(cbs, memory_file_transfer_send);
linphone_chat_room_send_chat_message(chat_room,message);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1));
if (marie->stat.last_received_chat_message ) {
cbs = linphone_chat_message_get_callbacks(marie->stat.last_received_chat_message);
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
linphone_chat_message_cbs_set_file_transfer_recv(cbs, file_transfer_received);
linphone_chat_message_download_file(marie->stat.last_received_chat_message);
}
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageExtBodyReceived,1));
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageInProgress,1);
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageDelivered,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageExtBodyReceived,1);
linphone_content_unref(content);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void file_transfer_message_io_error_upload(void) {
int i;
char* to;
@ -581,6 +677,7 @@ static void file_transfer_message_io_error_upload(void) {
const char* big_file_content="big file"; /* setting dummy file content to something */
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -647,6 +744,7 @@ static void file_transfer_message_io_error_download(void) {
const char* big_file_content="big file"; /* setting dummy file content to something */
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -712,6 +810,7 @@ static void file_transfer_message_upload_cancelled(void) {
const char* big_file_content="big file"; /* setting dummy file content to something */
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -772,6 +871,7 @@ static void file_transfer_message_download_cancelled(void) {
const char* big_file_content="big file"; /* setting dummy file content to something */
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -837,6 +937,10 @@ static void file_transfer_using_external_body_url(void) {
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
/* make sure lime is disabled */
linphone_core_set_lime(marie->lc, 0);
linphone_core_set_lime(pauline->lc, 0);
/* create a chatroom on pauline's side */
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc,to);
@ -865,7 +969,8 @@ static void file_transfer_using_external_body_url(void) {
static void text_message_with_send_error(void) {
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to = linphone_address_as_string(pauline->identity);
char *to = linphone_address_as_string(pauline->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(marie->lc,to);
LinphoneChatMessage* message = linphone_chat_room_create_message(chat_room,"Bli bli bli \n blu");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(message);
@ -903,7 +1008,8 @@ static void text_message_with_send_error(void) {
static void text_message_denied(void) {
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to = linphone_address_as_string(pauline->identity);
char *to = linphone_address_as_string(pauline->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(marie->lc,to);
LinphoneChatMessage* message = linphone_chat_room_create_message(chat_room,"Bli bli bli \n blu");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(message);
@ -941,12 +1047,13 @@ void info_message_received(LinphoneCore *lc, LinphoneCall* call, const LinphoneI
static void info_message_with_args(bool_t with_content) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneInfoMessage *info;
const LinphoneContent *content;
const char *hvalue;
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
CU_ASSERT_TRUE(call(pauline,marie));
info=linphone_core_create_info_message(marie->lc);
@ -1003,11 +1110,15 @@ static void info_message_with_body(){
}
static void is_composing_notification(void) {
char* to;
LinphoneChatRoom* chat_room;
int dummy = 0;
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
char* to = linphone_address_as_string(marie->identity);
LinphoneChatRoom* chat_room = linphone_core_create_chat_room(pauline->lc, to);
int dummy = 0;
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc, to);
ms_free(to);
{
@ -1026,6 +1137,211 @@ static void is_composing_notification(void) {
linphone_core_manager_destroy(pauline);
}
void printHex(char *title, uint8_t *data, uint32_t length) {
int i;
printf ("%s : ", title);
for (i=0; i<length; i++) {
printf ("0x%02x, ", data[i]);
}
printf ("\n");
}
static void lime_unit(void) {
int retval;
int size;
uint8_t *cacheBufferString;
xmlDocPtr cacheBufferAlice;
xmlDocPtr cacheBufferBob;
uint8_t *multipartMessage = NULL;
uint8_t *decryptedMessage = NULL;
xmlChar *xmlStringOutput;
int xmlStringLength;
limeURIKeys_t associatedKeys;
int i;
limeKey_t associatedKey;
uint8_t targetZID[12] = {0x00, 0x5d, 0xbe, 0x03, 0x99, 0x64, 0x3d, 0x95, 0x3a, 0x22, 0x02, 0xdd};
uint8_t senderZID[12] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0};
uint8_t encryptedMessage[48];
uint8_t plainMessage[48];
uint8_t receiverZID[12];
xmlDocPtr cacheBuffer;
/* Load Alice cache file */
FILE *CACHE = fopen("ZIDCacheAlice.xml", "r+");
fseek(CACHE, 0L, SEEK_END); /* Position to end of file */
size = ftell(CACHE); /* Get file length */
rewind(CACHE); /* Back to start of file */
cacheBufferString = (uint8_t *)malloc(size*sizeof(uint8_t)+1);
fread(cacheBufferString, 1, size, CACHE);
*(cacheBufferString+size) = '\0';
fclose(CACHE);
/* parse it to an xmlDoc */
cacheBufferAlice = xmlParseDoc(cacheBufferString);
free(cacheBufferString);
/* Load Bob cache file */
CACHE = fopen("ZIDCacheBob.xml", "r+");
fseek(CACHE, 0L, SEEK_END); /* Position to end of file */
size = ftell(CACHE); /* Get file length */
rewind(CACHE); /* Back to start of file */
cacheBufferString = (uint8_t *)malloc(size*sizeof(uint8_t)+1);
fread(cacheBufferString, 1, size, CACHE);
*(cacheBufferString+size) = '\0';
fclose(CACHE);
/* parse it to an xmlDoc */
cacheBufferBob = xmlParseDoc(cacheBufferString);
free(cacheBufferString);
/* encrypt a message */
retval = lime_createMultipartMessage(cacheBufferAlice, (uint8_t *)"Bonjour les petits lapins,ca va? éh oui oui", (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage);
printf("create message return %d\n", retval);
if (retval == 0) {
printf("message is %s\n", multipartMessage);
}
/* decrypt the multipart message */
retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage);
printf("decrypt message return %d\n", retval);
if (retval == 0) {
printf("message is %s##END\n", decryptedMessage);
}
free(multipartMessage);
free(decryptedMessage);
/* update ZID files */
/* dump the xml document into a string */
xmlDocDumpFormatMemoryEnc(cacheBufferAlice, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the file */
CACHE = fopen("ZIDCacheAlice.xml", "w+");
fwrite(xmlStringOutput, 1, xmlStringLength, CACHE);
xmlFree(xmlStringOutput);
fclose(CACHE);
xmlDocDumpFormatMemoryEnc(cacheBufferBob, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the file */
CACHE = fopen("ZIDCacheBob.xml", "w+");
fwrite(xmlStringOutput, 1, xmlStringLength, CACHE);
xmlFree(xmlStringOutput);
fclose(CACHE);
xmlFreeDoc(cacheBufferAlice);
xmlFreeDoc(cacheBufferBob);
/* Load cache file */
CACHE = fopen("ZIDCache.xml", "r+");
fseek(CACHE, 0L, SEEK_END); /* Position to end of file */
size = ftell(CACHE); /* Get file length */
rewind(CACHE); /* Back to start of file */
cacheBufferString = (uint8_t *)malloc(size*sizeof(uint8_t)+1);
fread(cacheBufferString, 1, size, CACHE);
*(cacheBufferString+size) = '\0';
fclose(CACHE);
/* parse it to an xmlDoc */
cacheBuffer = xmlParseDoc(cacheBufferString);
free(cacheBufferString);
/* get data from cache : sender */
associatedKeys.peerURI = (uint8_t *)malloc(15);
memcpy(associatedKeys.peerURI, "pipo1@pipo.com", 15);
associatedKeys.associatedZIDNumber = 0;
retval = lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys);
printf("getCachedKeys returns %d, number of key found %d\n", retval, associatedKeys.associatedZIDNumber);
for (i=0; i<associatedKeys.associatedZIDNumber; i++) {
printHex("ZID", associatedKeys.peerKeys[i]->peerZID, 12);
printHex("key", associatedKeys.peerKeys[i]->key, 32);
printHex("sessionID", associatedKeys.peerKeys[i]->sessionId, 32);
printf("session index %d\n", associatedKeys.peerKeys[i]->sessionIndex);
}
/* get data from cache : receiver */
memcpy(associatedKey.peerZID, targetZID, 12);
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
printf("getCachedKey by ZID return %d\n", retval);
printHex("Key", associatedKey.key, 32);
printHex("sessionID", associatedKey.sessionId, 32);
printf("session index %d\n", associatedKey.sessionIndex);
/* encrypt/decrypt a message */
lime_encryptMessage(associatedKeys.peerKeys[0], (uint8_t *)"bla Bla bla b! Pipo", 20, senderZID, encryptedMessage);
printHex("Ciphered", encryptedMessage, 32);
/* invert sender and receiverZID to decrypt/authenticate */
memcpy(receiverZID, associatedKeys.peerKeys[0]->peerZID, 12);
memcpy(associatedKeys.peerKeys[0]->peerZID, senderZID, 12);
retval = lime_decryptMessage(associatedKeys.peerKeys[0], encryptedMessage, 36, receiverZID, plainMessage);
printf("Decrypt and auth returned %d\nPlain: %s\n", retval, plainMessage);
/* update receiver data */
associatedKey.sessionIndex++;
associatedKey.key[0]++;
associatedKey.sessionId[0]++;
retval = lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);
printf("setCachedKey return %d\n", retval);
/* update sender data */
associatedKeys.peerKeys[0]->sessionIndex++;
associatedKeys.peerKeys[0]->key[0]++;
associatedKeys.peerKeys[0]->sessionId[0]++;
retval = lime_setCachedKey(cacheBuffer, associatedKeys.peerKeys[0], LIME_SENDER);
printf("setCachedKey return %d\n", retval);
/* free memory */
lime_freeKeys(associatedKeys);
/* write the file */
/* dump the xml document into a string */
xmlDocDumpFormatMemoryEnc(cacheBuffer, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the file */
CACHE = fopen("ZIDCache.xml", "w+");
fwrite(xmlStringOutput, 1, xmlStringLength, CACHE);
xmlFree(xmlStringOutput);
fclose(CACHE);
xmlFreeDoc(cacheBuffer);
}
static void lime_text_message(void) {
char* to;
FILE *ZIDCacheMarieFD, *ZIDCachePaulineFD;
LinphoneChatRoom* chat_room;
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
/* make sure lime is enabled */
linphone_core_set_lime(marie->lc, 1);
linphone_core_set_lime(pauline->lc, 1);
/* set the zid caches files : create two ZID cache from this valid one inserting the auto-generated sip URI for the peer account as keys in ZID cache are indexed by peer sip uri */
ZIDCacheMarieFD = fopen("tmpZIDCacheMarie.xml", "w");
ZIDCachePaulineFD = fopen("tmpZIDCachePauline.xml", "w");
fprintf(ZIDCacheMarieFD, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cache><selfZID>ef7692d0792a67491ae2d44e</selfZID><peer><ZID>005dbe0399643d953a2202dd</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>08df5907d30959b8cb70f6fff2d8febd88fb41b0c8afc39e4b972f86dd5cfe2d</sndKey><rcvKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000078</sndIndex><rcvIndex>000001cf</rcvIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a9176</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000000f</sndIndex><rcvIndex>00000000</rcvIndex></peer></cache>", linphone_address_as_string_uri_only(pauline->identity), linphone_address_as_string_uri_only(pauline->identity));
fprintf(ZIDCachePaulineFD, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<cache><selfZID>005dbe0399643d953a2202dd</selfZID><peer><ZID>ef7692d0792a67491ae2d44e</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><rcvKey>08df5907d30959b8cb70f6fff2d8febd88fb41b0c8afc39e4b972f86dd5cfe2d</rcvKey><sndKey>60f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a</sndKey><rcvSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvIndex>00000078</rcvIndex><sndIndex>000001cf</sndIndex><pvs>01</pvs></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>%s</uri><sndKey>81e6e6362c34dc974263d1f77cbb9a8d6d6a718330994379099a8fa19fb12faa</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000002e</sndIndex><rcvIndex>00000000</rcvIndex><pvs>01</pvs></peer></cache>", linphone_address_as_string_uri_only(marie->identity), linphone_address_as_string_uri_only(marie->identity));
fclose(ZIDCacheMarieFD);
fclose(ZIDCachePaulineFD);
linphone_core_set_zrtp_secrets_file(marie->lc, "tmpZIDCacheMarie.xml");
linphone_core_set_zrtp_secrets_file(pauline->lc, "tmpZIDCachePauline.xml");
to = linphone_address_as_string(marie->identity);
chat_room = linphone_core_create_chat_room(pauline->lc,to);
ms_free(to);
linphone_chat_room_send_message(chat_room,"Bla bla bla bla");
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageReceivedLegacy,1);
CU_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity));
/* TODO : check the message arrived correctly deciphered */
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
#ifdef MSG_STORAGE_ENABLED
/*
@ -1224,6 +1540,7 @@ static void history_messages_count() {
#endif
test_t message_tests[] = {
{ "Lime Text Message", lime_text_message },
{ "Text message", text_message },
{ "Text message within call's dialog", text_message_within_dialog},
{ "Text message with credentials from auth info cb", text_message_with_credential_from_auth_cb},
@ -1238,11 +1555,13 @@ test_t message_tests[] = {
/* { "File transfer message with io error at download", file_transfer_message_io_error_download },*/
{ "File transfer message upload cancelled", file_transfer_message_upload_cancelled },
{ "File transfer message download cancelled", file_transfer_message_download_cancelled },
{ "Lime File transfer message", lime_file_transfer_message },
{ "File transfer message using external body url", file_transfer_using_external_body_url },
{ "Text message denied", text_message_denied },
{ "Info message", info_message },
{ "Info message with body", info_message_with_body },
{ "IsComposing notification", is_composing_notification }
{ "IsComposing notification", is_composing_notification },
{ "Lime Unitary", lime_unit }
#ifdef MSG_STORAGE_ENABLED
,{ "Database migration", message_storage_migration }
,{ "History count", history_messages_count }