From 8a8eb4afe1c0c8e3aa3c80204d58e99cdda6579f Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Fri, 11 Apr 2014 00:02:36 +0200 Subject: [PATCH] Add Lime (Linphone Instant Message Encryption) - not fully functional yet. --- coreapi/Makefile.am | 1 + coreapi/chat.c | 89 +++++- coreapi/lime.c | 650 +++++++++++++++++++++++++++++++++++++++ coreapi/lime.h | 141 +++++++++ coreapi/linphonecall.c | 3 +- mediastreamer2 | 2 +- oRTP | 2 +- tester/ZIDCache.xml | 2 + tester/ZIDCacheAlice.xml | 2 + tester/ZIDCacheBob.xml | 4 + tester/message_tester.c | 166 +++++++++- 11 files changed, 1056 insertions(+), 6 deletions(-) create mode 100644 coreapi/lime.c create mode 100644 coreapi/lime.h create mode 100644 tester/ZIDCache.xml create mode 100644 tester/ZIDCacheAlice.xml create mode 100644 tester/ZIDCacheBob.xml diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 68d78447f..6f313d861 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -56,6 +56,7 @@ liblinphone_la_SOURCES=\ xml2lpc.c \ lpc2xml.c \ remote_provisioning.c \ + lime.c \ $(GITVERSION_FILE) if BUILD_UPNP diff --git a/coreapi/chat.c b/coreapi/chat.c index ef01b8b11..328700123 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -28,6 +28,8 @@ #include +#include "lime.h" + #define COMPOSING_DEFAULT_IDLE_TIMEOUT 15 #define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60 #define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120 @@ -194,7 +196,48 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM sal_message_send(op,identity,cr->peer,content_type, NULL); ms_free(content_type); } else { - sal_text_send(op, identity, cr->peer,msg->message); + uint8_t *multipartEncryptedMessage = NULL; + /* shall we try to encrypt the message?*/ + if (1) { /* TODO : set a flag for message encryption into LinphoneChatRoom structure */ + /* get the zrtp cache and parse it into an xml doc */ + FILE *CACHEFD = fopen(cr->lc->zrtp_secrets_cache, "r+"); + ms_message("Cache file is %s", cr->lc->zrtp_secrets_cache); + if (CACHEFD == NULL) { + ms_warning("Unable to access ZRTP ZID cache to encrypt message"); + } else { + fseek(CACHEFD, 0L, SEEK_END); /* Position to end of file */ + int cacheSize = ftell(CACHEFD); /* Get file length */ + rewind(CACHEFD); /* Back to start of file */ + uint8_t *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); + xmlDocPtr cacheXml = xmlParseDoc(cacheString); + int retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg->message, (uint8_t *)linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr)), &multipartEncryptedMessage); + if (retval != 0) { + ms_warning("Unable to encrypt message to %s error %x", linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr)), retval); + } + /* 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(cr->lc->zrtp_secrets_cache, "w+"); + fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD); + xmlFree(xmlStringOutput); + fclose(CACHEFD); + + + xmlFreeDoc(cacheXml); + } + } + if (multipartEncryptedMessage!=NULL) { + sal_text_send(op, identity, cr->peer,(const char *)multipartEncryptedMessage); + free(multipartEncryptedMessage); + } else { + sal_text_send(op, identity, cr->peer,msg->message); + } } msg->dir=LinphoneChatMessageOutgoing; msg->from=linphone_address_new(identity); @@ -264,7 +307,49 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag /* create a new chat room */ cr=linphone_core_create_chat_room(lc,cleanfrom); } - msg = linphone_chat_room_create_message(cr, sal_msg->text); + + /* shall we try to decrypt the message */ + uint8_t *decryptedMessage = NULL; + if (1) { /* TODO : set a flag for message encryption into LinphoneChatRoom structure */ + /* get the zrtp cache and parse it into an xml doc */ + FILE *CACHEFD = fopen(cr->lc->zrtp_secrets_cache, "r+"); + ms_message("Cache file is %s", lc->zrtp_secrets_cache); + if (CACHEFD == NULL) { + ms_warning("Unable to access ZRTP ZID cache to decrypt message"); + } else { + fseek(CACHEFD, 0L, SEEK_END); /* Position to end of file */ + int cacheSize = ftell(CACHEFD); /* Get file length */ + rewind(CACHEFD); /* Back to start of file */ + uint8_t *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); + xmlDocPtr cacheXml = xmlParseDoc(cacheString); + int retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)(sal_msg->text), &decryptedMessage); + if (retval != 0) { + ms_warning("Unable to decrypt message error %x", retval); + } + /* 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); + } + } + + if (decryptedMessage == NULL) { + msg = linphone_chat_room_create_message(cr, sal_msg->text); + } else { + msg = linphone_chat_room_create_message(cr, (const char *)decryptedMessage); + } + linphone_chat_message_set_from(msg, cr->peer_url); { diff --git a/coreapi/lime.c b/coreapi/lime.c new file mode 100644 index 000000000..7967c3ce6 --- /dev/null +++ b/coreapi/lime.c @@ -0,0 +1,650 @@ +#include "lime.h" +#include "linphonecore.h" +#include "ortp/b64.h" +#include "polarssl/gcm.h" + +/* check polarssl version */ +#include + +#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>4)&0x0F); + outputString[2*i+1] = lime_byteToChar(inputBytes[i]&0x0F); + } +} + + + +int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]) { + if (cacheBuffer == NULL ) { + return LIME_INVALID_CACHE; + } + + xmlNodePtr cur = xmlDocGetRootElement(cacheBuffer); + /* if we found a root element, parse its children node */ + if (cur!=NULL) + { + cur = cur->xmlChildrenNode; + } + xmlChar *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) { + + /* 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; + + xmlNodePtr 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 */ + matchingURIFlag = 0; + xmlNodePtr peerNodeChildren = cur->xmlChildrenNode; + + /* 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: 4 nodes*/ + + peerNodeChildren = cur->xmlChildrenNode; /* reset peerNodeChildren to the first child of node */ + while (peerNodeChildren!=NULL && itemFound<4) { + 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")) { + nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); + 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 */ + 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++; + } + + xmlFree(nodeContent); + peerNodeChildren = peerNodeChildren->next; + } + + /* check if we have all the requested information */ + if (itemFound == 4) { + 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) { + if (cacheBuffer == NULL ) { /* there is no cache return error */ + return LIME_INVALID_CACHE; + } + + /* get the given ZID into hex format */ + uint8_t peerZidHex[25]; + lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12); + peerZidHex[24]='\0'; /* must be a null terminated string */ + + xmlNodePtr cur = xmlDocGetRootElement(cacheBuffer); + /* if we found a root element, parse its children node */ + if (cur!=NULL) + { + cur = cur->xmlChildrenNode; + + } + uint8_t itemFound = 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 read */ + 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")) { + nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); + 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 */ + 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++; + } + xmlFree(nodeContent); + peerNodeChildren = peerNodeChildren->next; + } + } + xmlFree(currentZidHex); + } + cur = cur->next; + } + + + return 0; +} + +int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) { + if (cacheBuffer == NULL ) { /* there is no cache return error */ + return LIME_INVALID_CACHE; + } + + /* get the given ZID into hex format */ + uint8_t peerZidHex[25]; + lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12); + peerZidHex[24]='\0'; /* must be a null terminated string */ + + xmlNodePtr 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 */ + uint8_t keyHex[65]; /* key is 32 bytes long -> 64 bytes string + null termination */ + lime_int8ToStr(keyHex, associatedKey->key, 32); + keyHex[64] = '\0'; + uint8_t sessionIdHex[65]; /* sessionId is 32 bytes long -> 64 bytes string + null termination */ + lime_int8ToStr(sessionIdHex, associatedKey->sessionId, 32); + sessionIdHex[64] = '\0'; + uint8_t sessionIndexHex[9]; /* sessionInedx is an uint32_t : 4 bytes long -> 8 bytes string + null termination */ + 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'; + + uint8_t itemFound = 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) { + 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 */ + uint8_t inputData[55]; + 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 */ + uint8_t derivedKey[32]; +#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) { + /* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */ + uint8_t authenticatedData[28]; + 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_context gcmContext; + 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_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) { + /* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */ + uint8_t authenticatedData[28]; + 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_context gcmContext; + 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 */ + int 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) { + /* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */ + uint8_t selfZidHex[25]; + if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) { + return LIME_UNABLE_TO_ENCRYPT_MESSAGE; + } + uint8_t selfZid[12]; /* same data but in byte buffer */ + lime_strToUint8(selfZid, selfZidHex, 24); + + /* encrypted message length is plaintext + 16 for tag */ + uint32_t encryptedMessageLength = strlen((char *)message) + 16; + + + /* retrieve keys associated to the peer URI */ + limeURIKeys_t associatedKeys; + 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_KEY_FOUND_FOR_PEER; + } + + /* create an xml doc to hold the multipart message */ + xmlDocPtr xmlOutputMessage = xmlNewDoc((const xmlChar *)"1.0"); + /* root tag is "doc" */ + xmlNodePtr 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 */ + int i; + for (i=0; i + * peerZID + * session index + * ciphertext + * */ + xmlNodePtr msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL); + uint8_t peerZidHex[25]; + lime_int8ToStr(peerZidHex, currentKey->peerZID, 12); + peerZidHex[24] = '\0'; + uint8_t sessionIndexHex[9]; + 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 */ + int b64Size = b64_encode(NULL, encryptedMessageLength, NULL, 0); + char *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 */ + int xmlStringLength; + 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) { + /* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */ + uint8_t selfZidHex[25]; + if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) { + return LIME_UNABLE_TO_DECRYPT_MESSAGE; + } + uint8_t selfZid[12]; /* same data but in byte buffer */ + lime_strToUint8(selfZid, selfZidHex, 24); + + /* parse the message into an xml doc */ + xmlDocPtr xmlEncryptedMessage = xmlParseDoc(message); + + /* retrieve the sender ZID which is the first child of root */ + limeKey_t associatedKey; + xmlChar *peerZidHex = NULL; + xmlNodePtr 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; + } + } + + uint8_t *encryptedMessage = NULL; + uint32_t encryptedMessageLength = 0; + uint32_t usedSessionIndex = 0; + + if (peerZidHex != NULL) { + /* get from cache the matching key */ + int retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey); + + if (retval == 0) { + /* 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) */ + msgChildrenNode = msgChildrenNode->next; + xmlChar *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 */ + xmlChar *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) { + /* TODO */ + } + + /* 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 */ + lime_decryptMessage(&associatedKey, encryptedMessage, encryptedMessageLength, selfZid, *output); + free(encryptedMessage); + + /* update used key */ + lime_deriveKey(&associatedKey); + lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER); + + return 0; +} diff --git a/coreapi/lime.h b/coreapi/lime.h new file mode 100644 index 000000000..0dc5c075a --- /dev/null +++ b/coreapi/lime.h @@ -0,0 +1,141 @@ +#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_KEY_FOUND_FOR_PEER 0x1010 + +#define LIME_SENDER 0x01 +#define LIME_RECEIVER 0x02 +#include +#include +#include +#include + +/** + * @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 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); +__attribute__ ((visibility ("default"))) int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output); +#endif /* LIME_H */ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 41a92140f..f12cecf65 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2069,7 +2069,8 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut call->current_params.media_encryption=LinphoneMediaEncryptionNone; params.zid_file=lc->zrtp_secrets_cache; - params.uri= "SetThePeerSipUriHere@nullTerminated.String"; /* TODO: set the sip URI there, MUST be a null terminated string */ + + params.uri= linphone_address_as_string_uri_only(call->log->to); audio_stream_enable_zrtp(call->audiostream,¶ms); }else{ call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ? diff --git a/mediastreamer2 b/mediastreamer2 index 9117185fd..da5d1de60 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 9117185fd8b0a4679cc198cded64dad7d85628e0 +Subproject commit da5d1de606699162f48751f006d9219321069545 diff --git a/oRTP b/oRTP index a0bab264b..f71c3aa0d 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit a0bab264b915bcb5b67c061ed27bbb2b90a1b29a +Subproject commit f71c3aa0dec5267bc78d22d33282661dc8795be5 diff --git a/tester/ZIDCache.xml b/tester/ZIDCache.xml new file mode 100644 index 000000000..1f0fcb1a3 --- /dev/null +++ b/tester/ZIDCache.xml @@ -0,0 +1,2 @@ + +ef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com963c57bb28e62068d2df23e8f9b771932d3c57bb28e62068d2df23e8f9b77193e9d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193e6ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000069000001cc1234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com123456789012345678901234567890123456765431262068d2df23e8f9b7719325d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000100000000 diff --git a/tester/ZIDCacheAlice.xml b/tester/ZIDCacheAlice.xml new file mode 100644 index 000000000..5fe09a289 --- /dev/null +++ b/tester/ZIDCacheAlice.xml @@ -0,0 +1,2 @@ + +ef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.comf6e5c94feabbe348f25a528cc990b7ec0f3390286314eb70cf2a9a852afb2df4bfd9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000074000001a21234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.comb438386ce7d91f0c3341315ff58ce14826a119f36d91650ca26fa7fde2f1601225d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000c00000000 diff --git a/tester/ZIDCacheBob.xml b/tester/ZIDCacheBob.xml new file mode 100644 index 000000000..23116d8da --- /dev/null +++ b/tester/ZIDCacheBob.xml @@ -0,0 +1,4 @@ + +005dbe0399643d953a2202dd + ef7692d0792a67491ae2d44e9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.comf6e5c94feabbe348f25a528cc990b7ec0f3390286314eb70cf2a9a852afb2df4bfd9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000074000001a2 + 1234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com123456789012345678901234567890123456765431262068d2df23e8f9b7719325d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000100000000 diff --git a/tester/message_tester.c b/tester/message_tester.c index 259727715..bd3faadb7 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -22,6 +22,7 @@ #include "linphonecore.h" #include "private.h" #include "liblinphone_tester.h" +#include "lime.h" static char* message_external_body_url; @@ -370,6 +371,168 @@ static void is_composing_notification(void) { linphone_core_manager_destroy(pauline); } +void printHex(char *title, uint8_t *data, uint32_t length) { + printf ("%s : ", title); + int i; + for (i=0; ipeerZID, 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 */ + limeKey_t associatedKey; + uint8_t targetZID[12] = {0x00, 0x5d, 0xbe, 0x03, 0x99, 0x64, 0x3d, 0x95, 0x3a, 0x22, 0x02, 0xdd}; + 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 */ + uint8_t senderZID[12] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0}; + uint8_t encryptedMessage[48]; + uint8_t plainMessage[48]; + 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 */ + uint8_t receiverZID[12]; + 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); +} + test_t message_tests[] = { { "Text message", text_message }, { "Text message within call's dialog", text_message_within_dialog}, @@ -382,7 +545,8 @@ test_t message_tests[] = { { "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", lime } }; test_suite_t message_test_suite = {