mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-22 21:58:08 +00:00
Add Lime (Linphone Instant Message Encryption)
- not fully functional yet.
This commit is contained in:
parent
bfba263da2
commit
8a8eb4afe1
11 changed files with 1056 additions and 6 deletions
|
|
@ -56,6 +56,7 @@ liblinphone_la_SOURCES=\
|
|||
xml2lpc.c \
|
||||
lpc2xml.c \
|
||||
remote_provisioning.c \
|
||||
lime.c \
|
||||
$(GITVERSION_FILE)
|
||||
|
||||
if BUILD_UPNP
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include <libxml/xmlwriter.h>
|
||||
|
||||
#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);
|
||||
|
||||
{
|
||||
|
|
|
|||
650
coreapi/lime.c
Normal file
650
coreapi/lime.c
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
#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]) {
|
||||
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<associatedKeys.associatedZIDNumber; i++) {
|
||||
/* 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> */
|
||||
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;
|
||||
}
|
||||
141
coreapi/lime.h
Normal file
141
coreapi/lime.h
Normal file
|
|
@ -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 <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 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 */
|
||||
|
|
@ -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) ?
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 9117185fd8b0a4679cc198cded64dad7d85628e0
|
||||
Subproject commit da5d1de606699162f48751f006d9219321069545
|
||||
2
oRTP
2
oRTP
|
|
@ -1 +1 @@
|
|||
Subproject commit a0bab264b915bcb5b67c061ed27bbb2b90a1b29a
|
||||
Subproject commit f71c3aa0dec5267bc78d22d33282661dc8795be5
|
||||
2
tester/ZIDCache.xml
Normal file
2
tester/ZIDCache.xml
Normal 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>e9d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>e6ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000069</sndIndex><rcvIndex>000001cc</rcvIndex></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></peer></cache>
|
||||
2
tester/ZIDCacheAlice.xml
Normal file
2
tester/ZIDCacheAlice.xml
Normal 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>f6e5c94feabbe348f25a528cc990b7ec0f3390286314eb70cf2a9a852afb2df4</sndKey><rcvKey>bfd9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>00000074</sndIndex><rcvIndex>000001a2</rcvIndex></peer><peer><ZID>1234567889643d953a2202ee</ZID><rs1>9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045</rs1><aux>f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e</aux><rs2>2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899</rs2><uri>pipo1@pipo.com</uri><sndKey>b438386ce7d91f0c3341315ff58ce14826a119f36d91650ca26fa7fde2f16012</sndKey><rcvKey>25d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</rcvKey><sndSId>f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvSId>22ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndIndex>0000000c</sndIndex><rcvIndex>00000000</rcvIndex></peer></cache>
|
||||
4
tester/ZIDCacheBob.xml
Normal file
4
tester/ZIDCacheBob.xml
Normal 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>pipo1@pipo.com</uri><rcvKey>f6e5c94feabbe348f25a528cc990b7ec0f3390286314eb70cf2a9a852afb2df4</rcvKey><sndKey>bfd9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193</sndKey><rcvSId>5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193</rcvSId><sndSId>bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193</sndSId><rcvIndex>00000074</rcvIndex><sndIndex>000001a2</sndIndex></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></peer></cache>
|
||||
|
|
@ -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; i<length; i++) {
|
||||
printf ("0x%02x, ", data[i]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
static void lime(void) {
|
||||
int retval;
|
||||
/* Load Alice cache file */
|
||||
FILE *CACHE = fopen("ZIDCacheAlice.xml", "r+");
|
||||
fseek(CACHE, 0L, SEEK_END); /* Position to end of file */
|
||||
int size = ftell(CACHE); /* Get file length */
|
||||
rewind(CACHE); /* Back to start of file */
|
||||
uint8_t *cacheBufferString = (uint8_t *)malloc(size*sizeof(uint8_t)+1);
|
||||
fread(cacheBufferString, 1, size, CACHE);
|
||||
*(cacheBufferString+size) = '\0';
|
||||
fclose(CACHE);
|
||||
/* parse it to an xmlDoc */
|
||||
xmlDocPtr 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 */
|
||||
xmlDocPtr cacheBufferBob = xmlParseDoc(cacheBufferString);
|
||||
free(cacheBufferString);
|
||||
|
||||
|
||||
|
||||
/* encrypt a message */
|
||||
uint8_t *multipartMessage = NULL;
|
||||
retval = lime_createMultipartMessage(cacheBufferAlice, (uint8_t *)"Bonjour les petits lapins,ca va? éh oui oui", (uint8_t *)"pipo1@pipo.com", &multipartMessage);
|
||||
|
||||
printf("create message return %d\n", retval);
|
||||
if (retval == 0) {
|
||||
printf("message is %s\n", multipartMessage);
|
||||
}
|
||||
|
||||
/* decrypt the multipart message */
|
||||
uint8_t *decryptedMessage = NULL;
|
||||
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 */
|
||||
xmlChar *xmlStringOutput;
|
||||
int xmlStringLength;
|
||||
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 */
|
||||
xmlDocPtr cacheBuffer = xmlParseDoc(cacheBufferString);
|
||||
free(cacheBufferString);
|
||||
|
||||
/* get data from cache : sender */
|
||||
limeURIKeys_t associatedKeys;
|
||||
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);
|
||||
|
||||
int i;
|
||||
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 */
|
||||
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 = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue