diff --git a/coreapi/lime.c b/coreapi/lime.c index c3a7b9e8c..2fb294f5c 100644 --- a/coreapi/lime.c +++ b/coreapi/lime.c @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifdef HAVE_LIME #include "private.h" #include "bctoolbox/crypto.h" +#include "bctoolbox/port.h" #define FILE_TRANSFER_KEY_SIZE 32 @@ -35,84 +36,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ bool_t lime_is_available(void) { return TRUE; } -/** - * @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, const uint8_t *inputString, uint16_t inputStringLength) { - int i; - for (i=0; i>4)&0x0F); - outputString[2*i+1] = lime_byteToChar(inputBytes[i]&0x0F); - } -} - - - /** * @brief Retrieve selfZID from cache * @@ -158,6 +81,7 @@ static int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]) { int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys) { xmlNodePtr cur; + size_t keysFound = 0; /* used to detect the no key found error because of validity expired */ /* parse the file to get all peer matching the sipURI given in associatedKeys*/ if (cacheBuffer == NULL ) { /* there is no cache return error */ @@ -192,58 +116,80 @@ int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedK 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 */ + if (matchingURIFlag == 1) { /* we found a match for the URI in this peer node, extract the keys, session Id, index values and key validity period */ /* allocate a new limeKey_t structure to hold the retreived keys */ limeKey_t *currentPeerKeys = (limeKey_t *)malloc(sizeof(limeKey_t)); uint8_t itemFound = 0; /* count the item found, we must get all of the requested infos: 5 nodes*/ uint8_t pvs = 0; + uint8_t keyValidityFound = 0; /* flag raised when we found a key validity in the cache */ + bctoolboxTimeSpec currentTimeSpec; + bctoolboxTimeSpec validityTimeSpec; /* optionnal(backward compatibility) tag for key validity */ + validityTimeSpec.tv_sec=0; + validityTimeSpec.tv_nsec=0; peerNodeChildren = cur->xmlChildrenNode; /* reset peerNodeChildren to the first child of node */ - while (peerNodeChildren!=NULL && itemFound<5) { + while (peerNodeChildren!=NULL && itemFound<6) { xmlChar *nodeContent = NULL; if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) { nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(currentPeerKeys->peerZID, nodeContent, 24); + bctbx_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); + bctbx_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); + bctbx_strToUint8(currentPeerKeys->sessionId, nodeContent, 64); itemFound++; } if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) { uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(sessionIndexBuffer, nodeContent, 8); + bctbx_strToUint8(sessionIndexBuffer, nodeContent, 8); /* convert it back to a uint32_t (MSByte first)*/ currentPeerKeys->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24); itemFound++; } if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) { nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */ + bctbx_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */ itemFound++; } + if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"valid")) { + nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); + validityTimeSpec.tv_sec = bctbx_strToUint64(nodeContent); /* validity is retrieved as a 16 characters hexa string, convert it to an uint64 */ + itemFound++; + keyValidityFound = 1; + } xmlFree(nodeContent); peerNodeChildren = peerNodeChildren->next; } - /* check if we have all the requested information and the PVS flag is set to 1 */ - if (itemFound == 5 && pvs == 1) { - associatedKeys->associatedZIDNumber +=1; - /* extend array of pointer to limeKey_t structures to add the one we found */ - associatedKeys->peerKeys = (limeKey_t **)realloc(associatedKeys->peerKeys, (associatedKeys->associatedZIDNumber)*sizeof(limeKey_t *)); + /* key validity may not be present in cache(transition from older versions), so check this, if not found-> set it to 0 wich means valid for ever */ + if (keyValidityFound == 0) { + itemFound++; + validityTimeSpec.tv_sec = 0; + } - /* add the new entry at the end */ - associatedKeys->peerKeys[associatedKeys->associatedZIDNumber-1] = currentPeerKeys; + /* check if we have all the requested information and the PVS flag is set to 1 and key is still valid*/ + _bctbx_get_cur_time(¤tTimeSpec, TRUE); + if (itemFound == 6 && pvs == 1) { + keysFound++; + if (validityTimeSpec.tv_sec == 0 || bctbx_timespec_compare(¤tTimeSpec, &validityTimeSpec)<0) { + 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); + } } else { free(currentPeerKeys); } @@ -251,6 +197,13 @@ int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedK } cur = cur->next; } + if (associatedKeys->associatedZIDNumber == 0) { + if (keysFound == 0) { + return LIME_NO_VALID_KEY_FOUND_FOR_PEER; + } else { + return LIME_PEER_KEY_HAS_EXPIRED; + } + } return 0; } @@ -266,7 +219,7 @@ int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) { } /* get the given ZID into hex format */ - lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12); + bctbx_int8ToStr(peerZidHex, associatedKey->peerZID, 12); peerZidHex[24]='\0'; /* must be a null terminated string */ cur = xmlDocGetRootElement(cacheBuffer); @@ -286,25 +239,25 @@ int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) { xmlChar *nodeContent = NULL; if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) { nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(associatedKey->key, nodeContent, 64); + bctbx_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); + bctbx_strToUint8(associatedKey->sessionId, nodeContent, 64); itemFound++; } if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) { uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */ nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(sessionIndexBuffer, nodeContent, 8); + bctbx_strToUint8(sessionIndexBuffer, nodeContent, 8); /* convert it back to a uint32_t (MSByte first)*/ associatedKey->sessionIndex = sessionIndexBuffer[3] + (sessionIndexBuffer[2]<<8) + (sessionIndexBuffer[1]<<16) + (sessionIndexBuffer[0]<<24); itemFound++; } if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) { nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1); - lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */ + bctbx_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */ itemFound++; } xmlFree(nodeContent); @@ -327,12 +280,16 @@ int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) { return LIME_NO_VALID_KEY_FOUND_FOR_PEER; } -int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) { +int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role, uint64_t validityTimeSpan) { + uint8_t done=0; xmlNodePtr cur; uint8_t peerZidHex[25]; uint8_t keyHex[65]; /* key is 32 bytes long -> 64 bytes string + null termination */ uint8_t sessionIdHex[65]; /* sessionId is 32 bytes long -> 64 bytes string + null termination */ uint8_t sessionIndexHex[9]; /* sessionInedx is an uint32_t : 4 bytes long -> 8 bytes string + null termination */ + uint8_t validHex[17]; /* validity is a unix period in seconds on 64bits -> 16 bytes string + null termination */ + bctoolboxTimeSpec currentTimeSpec; + uint8_t itemFound = 0; if (cacheBuffer == NULL ) { /* there is no cache return error */ @@ -340,7 +297,7 @@ int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t r } /* get the given ZID into hex format */ - lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12); + bctbx_int8ToStr(peerZidHex, associatedKey->peerZID, 12); peerZidHex[24]='\0'; /* must be a null terminated string */ cur = xmlDocGetRootElement(cacheBuffer); @@ -352,26 +309,27 @@ int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t r } /* convert the given tag content to null terminated Hexadecimal strings */ - lime_int8ToStr(keyHex, associatedKey->key, 32); + bctbx_int8ToStr(keyHex, associatedKey->key, 32); keyHex[64] = '\0'; - lime_int8ToStr(sessionIdHex, associatedKey->sessionId, 32); + bctbx_int8ToStr(sessionIdHex, associatedKey->sessionId, 32); sessionIdHex[64] = '\0'; - sessionIndexHex[0] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>28)&0x0F)); - sessionIndexHex[1] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>24)&0x0F)); - sessionIndexHex[2] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>20)&0x0F)); - sessionIndexHex[3] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>16)&0x0F)); - sessionIndexHex[4] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>12)&0x0F)); - sessionIndexHex[5] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>8)&0x0F)); - sessionIndexHex[6] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>4)&0x0F)); - sessionIndexHex[7] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex)&0x0F)); - sessionIndexHex[8] = '\0'; + bctbx_uint32ToStr(sessionIndexHex, associatedKey->sessionIndex); + if (validityTimeSpan > 0 && role == LIME_RECEIVER) { + _bctbx_get_cur_time(¤tTimeSpec, TRUE); + bctbx_timespec_add(¤tTimeSpec, validityTimeSpan); + bctbx_uint64ToStr(validHex, currentTimeSpec.tv_sec); + } - while (cur!=NULL && itemFound<3) { /* loop on all peer nodes */ + while (cur!=NULL && done==0) { /* 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 */ + uint8_t validFound = 0; xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next; - while (peerNodeChildren != NULL && itemFound<3) { /* look for the tag we want to write */ + if (role==LIME_SENDER) { + itemFound=1; /* we do not look for valid when setting sender key */ + } + while (peerNodeChildren != NULL && itemFound<4) { /* 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); @@ -385,6 +343,13 @@ int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t r xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex); itemFound++; } + if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"valid")) { + if (validityTimeSpan > 0) { + xmlNodeSetContent(peerNodeChildren, (const xmlChar *)validHex); + } + itemFound++; + validFound=1; + } } else { /* writing sender key */ if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) { xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex); @@ -401,13 +366,17 @@ int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t r } peerNodeChildren = peerNodeChildren->next; } + + /* we may want to add the valid node which if it is missing and we've been ask to update it */ + if (role == LIME_RECEIVER && validityTimeSpan>0 && validFound==0) { + xmlNewTextChild(cur, NULL, (const xmlChar *)"valid", validHex); + } + done=1; /* step out of the loop */ } xmlFree(currentZidHex); } cur = cur->next; } - - return 0; } @@ -581,7 +550,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, limeURIKeys_t associatedKeys; xmlDocPtr xmlOutputMessage; xmlNodePtr rootNode; - int i; + int i,ret; int xmlStringLength; xmlChar *local_output = NULL; @@ -589,7 +558,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) { return LIME_UNABLE_TO_ENCRYPT_MESSAGE; } - lime_strToUint8(selfZid, selfZidHex, 24); + bctbx_strToUint8(selfZid, selfZidHex, 24); /* encrypted message length is plaintext + 16 for tag */ encryptedMessageLength = (uint32_t)strlen((char *)message) + 16; @@ -601,14 +570,9 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, associatedKeys.associatedZIDNumber = 0; associatedKeys.peerKeys = NULL; - if (lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys) != 0) { + if ((ret = lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys)) != 0) { lime_freeKeys(&associatedKeys); - return LIME_UNABLE_TO_ENCRYPT_MESSAGE; - } - - if (associatedKeys.associatedZIDNumber == 0) { - lime_freeKeys(&associatedKeys); - return LIME_NO_VALID_KEY_FOUND_FOR_PEER; + return ret; } /* create an xml doc to hold the multipart message */ @@ -643,17 +607,9 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, * ciphertext * */ msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL); - lime_int8ToStr(peerZidHex, currentKey->peerZID, 12); + bctbx_int8ToStr(peerZidHex, currentKey->peerZID, 12); peerZidHex[24] = '\0'; - sessionIndexHex[0] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>28)&0x0F)); - sessionIndexHex[1] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>24)&0x0F)); - sessionIndexHex[2] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>20)&0x0F)); - sessionIndexHex[3] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>16)&0x0F)); - sessionIndexHex[4] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>12)&0x0F)); - sessionIndexHex[5] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>8)&0x0F)); - sessionIndexHex[6] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>4)&0x0F)); - sessionIndexHex[7] = lime_byteToChar((uint8_t)((currentKey->sessionIndex)&0x0F)); - sessionIndexHex[8] = '\0'; + bctbx_uint32ToStr(sessionIndexHex, currentKey->sessionIndex); xmlNewTextChild(msgNode, NULL, (const xmlChar *)"pzid", peerZidHex); xmlNewTextChild(msgNode, NULL, (const xmlChar *)"index", sessionIndexHex); @@ -682,7 +638,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, /* update the key used */ lime_deriveKey(currentKey); - lime_setCachedKey(cacheBuffer, currentKey, LIME_SENDER); + lime_setCachedKey(cacheBuffer, currentKey, LIME_SENDER, 0); /* never update validity when sending a message */ } /* dump the whole message doc into the output */ @@ -699,7 +655,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, return 0; } -int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type) { +int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type, uint64_t validityTimeSpan) { int retval = 0; uint8_t selfZidHex[25]; uint8_t selfZid[12]; /* same data but in byte buffer */ @@ -722,7 +678,7 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_ if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) { return LIME_UNABLE_TO_DECRYPT_MESSAGE; } - lime_strToUint8(selfZid, selfZidHex, 24); + bctbx_strToUint8(selfZid, selfZidHex, 24); xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); @@ -741,7 +697,7 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_ peerZidHex = linphone_get_xml_text_content(xml_ctx, "/doc/ZID"); if (peerZidHex != NULL) { /* Convert it from hexa string to bytes string and set the result in the associatedKey structure */ - lime_strToUint8(associatedKey.peerZID, (const uint8_t *)peerZidHex, (uint16_t)strlen(peerZidHex)); + bctbx_strToUint8(associatedKey.peerZID, (const uint8_t *)peerZidHex, (uint16_t)strlen(peerZidHex)); linphone_free_xml_text_content(peerZidHex); /* Get the matching key from cache */ @@ -765,14 +721,7 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_ snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/index", i); sessionIndexHex = linphone_get_xml_text_content(xml_ctx, xpath_str); if (sessionIndexHex != NULL) { - 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]))); + usedSessionIndex = bctbx_strToUint32((const unsigned char *)sessionIndexHex); linphone_free_xml_text_content(sessionIndexHex); } snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/text", i); @@ -844,7 +793,7 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_ /* update used key */ lime_deriveKey(&associatedKey); - lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER); + lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER, validityTimeSpan); error: linphone_xmlparsing_context_destroy(xml_ctx); @@ -884,7 +833,7 @@ bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) { associatedKeys.associatedZIDNumber = 0; associatedKeys.peerKeys = NULL; - res = (lime_getCachedSndKeysByURI(cacheXml, &associatedKeys) == 0 && associatedKeys.associatedZIDNumber != 0); + res = (lime_getCachedSndKeysByURI(cacheXml, &associatedKeys) == 0); lime_freeKeys(&associatedKeys); xmlFreeDoc(cacheXml); ms_free(peer); @@ -931,7 +880,7 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn fclose(CACHEFD); cacheXml = xmlParseDoc((xmlChar*)cacheString); ms_free(cacheString); - retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body, &decrypted_content_type); + retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body, &decrypted_content_type, lp_config_get_int(lc->config, "sip", "lime_key_validity", 0)); if (retval != 0) { ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval)); if (decrypted_body) ms_free(decrypted_body); @@ -1151,6 +1100,7 @@ char *lime_error_code_to_string(int errorCode) { case LIME_UNABLE_TO_ENCRYPT_MESSAGE: return "Unable to encrypt message"; case LIME_UNABLE_TO_DECRYPT_MESSAGE: return "Unable to decrypt message"; case LIME_NO_VALID_KEY_FOUND_FOR_PEER: return "No valid key found"; + case LIME_PEER_KEY_HAS_EXPIRED: return "Any key matching peer Uri has expired"; case LIME_INVALID_ENCRYPTED_MESSAGE: return "Invalid encrypted message"; case LIME_NOT_ENABLED: return "Lime not enabled at build"; } diff --git a/coreapi/lime.h b/coreapi/lime.h index 239b0f729..80565b64b 100644 --- a/coreapi/lime.h +++ b/coreapi/lime.h @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define LIME_UNABLE_TO_DECRYPT_MESSAGE 0x1008 #define LIME_NO_VALID_KEY_FOUND_FOR_PEER 0x1010 #define LIME_INVALID_ENCRYPTED_MESSAGE 0x1020 +#define LIME_PEER_KEY_HAS_EXPIRED 0x1040 #define LIME_NOT_ENABLED 0x1100 /* this define the maximum key derivation number allowed to get the caches back in sync in case of missed messages */ @@ -66,7 +67,7 @@ typedef struct limeURIKeys_struct { * @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 + * @return 0 on success(at least one valid key found), error code otherwise */ LINPHONE_PUBLIC int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys); @@ -84,13 +85,14 @@ LINPHONE_PUBLIC int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t * * @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,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 + * @param[in] validityTimeSpan If not 0, set the tag to now+validityTimeSpan (in seconds) * * @return 0 on success, error code otherwise */ -LINPHONE_PUBLIC int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role); +LINPHONE_PUBLIC int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, const uint8_t role, const uint64_t validityTimeSpan); /** * @brief Free all allocated data in the associated keys structure @@ -183,10 +185,10 @@ LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const cha * @param[in] message The multipart message, contain one or several part identified by destination ZID, one shall match the self ZID retrieved from cache * @param[out] output The output buffer, allocated and set with the decrypted message(null terminated string). Must be freed by caller * @param[out] content_type The content type of the decrypted message + * @param[in] validityTimeSpan If not 0, update the tag associated to sender to now+validityTimeSpan (in seconds) * @return 0 on success, error code otherwise */ - -LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type); +LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type, const uint64_t validityTimeSpan); /** * @brief given a readable version of error code generated by Lime functions diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 266574ece..c4a492b04 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2531,6 +2531,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ /*call->current_params.media_encryption will be set later when zrtp is activated*/ params.zid_file=lc->zrtp_secrets_cache; params.uri=uri; + params.limeKeyTimeSpan = lp_config_get_int(lc->config, "sip", "lime_key_validity", 0); /* get key lifespan from config file, default is 0:forever valid */ setZrtpCryptoTypesParameters(¶ms,call->core); audio_stream_enable_zrtp(call->audiostream,¶ms); if (uri != NULL) ms_free(uri); diff --git a/tester/message_tester.c b/tester/message_tester.c index 824480a1d..ebedc80a0 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1213,7 +1213,6 @@ static void lime_text_message(void) { } BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity)); - /* TODO : check the msg arrived correctly deciphered */ end: remove("tmpZIDCacheMarie.xml"); remove("tmpZIDCachePauline.xml"); @@ -1442,9 +1441,24 @@ static void printHex(char *title, uint8_t *data, size_t length) { ms_message("%s", debug_string_buffer); } +static void stripnCR(char *dst, const char *src, const size_t srcLength) { + size_t i=0; + for (i=0; *src != '\0' && i\r\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com963c57bb28e62068d2df23e8f9b771932d3c57bb28e62068d2df23e8f9b7719306d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719303ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000069000001e9011234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com123456789012345678901234567890123456765431262068d2df23e8f9b7719325d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193000000010000000001\r\n"; + char cachedStringBufferExpected[2048]; + char cachedStringBufferActual[2048]; + char cachedStringBuffer[2048]; int retval; size_t size; uint8_t *cacheBufferString; @@ -1471,7 +1485,7 @@ static void lime_unit(void) { /* create and load cache file */ CACHE = fopen_from_write_dir("ZIDCache.xml", "wb"); - fprintf (CACHE, "\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com963c57bb28e62068d2df23e8f9b771932d3c57bb28e62068d2df23e8f9b7719305d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719302ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000069000001e8011234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com123456789012345678901234567890123456765431262068d2df23e8f9b7719325d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193000000010000000001"); + fprintf (CACHE, "\r\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com963c57bb28e62068d2df23e8f9b771932d3c57bb28e62068d2df23e8f9b7719305d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b771935f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719302ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000069000001e8011234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899pipo1@pipo.com123456789012345678901234567890123456765431262068d2df23e8f9b7719325d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b77193000000010000000001"); fclose(CACHE); CACHE = fopen_from_write_dir("ZIDCache.xml", "rb+"); cacheBufferString = (uint8_t*) ms_load_file_content(CACHE, &size); @@ -1521,22 +1535,28 @@ static void lime_unit(void) { associatedKey.sessionIndex++; associatedKey.key[0]++; associatedKey.sessionId[0]++; - retval = lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER); + retval = lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER, 0); BC_ASSERT_EQUAL(retval, 0, int, "%d"); /* 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); + retval = lime_setCachedKey(cacheBuffer, associatedKeys.peerKeys[0], LIME_SENDER, 0); BC_ASSERT_EQUAL(retval, 0, int, "%d"); /* free memory */ lime_freeKeys(&associatedKeys); /* write the file */ + /* dump the xml document into a string */ xmlDocDumpFormatMemoryEnc(cacheBuffer, &xmlStringOutput, &xmlStringLength, "UTF-8", 0); + /* must strip buffers of their \n\r or strcmp won't like it */ + stripnCR(cachedStringBufferExpected, pattern_ZIDCache_noValidity, strlen(pattern_ZIDCache_noValidity)); + stripnCR(cachedStringBufferActual, (const char *)xmlStringOutput, strlen((const char *)xmlStringOutput)); + BC_ASSERT_NSTRING_EQUAL(cachedStringBufferExpected, cachedStringBufferActual, strlen(cachedStringBufferExpected)); + /* write it to the file */ CACHE = fopen_from_write_dir("ZIDCache.xml", "w+"); fwrite(xmlStringOutput, 1, xmlStringLength, CACHE); @@ -1544,10 +1564,11 @@ static void lime_unit(void) { fclose(CACHE); xmlFreeDoc(cacheBuffer); + /**** Higher level tests using 2 caches to encrypt/decrypt a msg ****/ /* Create Alice cache file and then load it */ CACHE = fopen_from_write_dir("ZIDCacheAlice.xml", "wb"); - fprintf(CACHE, "\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org9111ebeb52e50edcc6fcb3eea1a2d3ae3c2c75d3668923e83c59d0f47245515060f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000080000001cf011234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a917625d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000f00000000"); + fprintf(CACHE, "\r\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org9111ebeb52e50edcc6fcb3eea1a2d3ae3c2c75d3668923e83c59d0f47245515060f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000080000001cf0100000000386d43801234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a917625d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000f0000000000000000386d4380"); fclose(CACHE); CACHE = fopen_from_write_dir("ZIDCacheAlice.xml", "rb+"); cacheBufferString = (uint8_t *)ms_load_file_content(CACHE, &size); @@ -1570,17 +1591,26 @@ static void lime_unit(void) { ms_free(cacheBufferString); + /*Try to encrypt the message, but it shall fail due to expired key */ /* encrypt a msg */ retval = lime_createMultipartMessage(cacheBufferAlice, "text/plain", (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage); + BC_ASSERT_EQUAL(retval, LIME_PEER_KEY_HAS_EXPIRED, int, "%d"); + /* reload a cache but with valid key (time bomb: validity is set to the 2038 bug epoch even if we are not prone to it) */ + sprintf(cachedStringBuffer, "\r\nef7692d0792a67491ae2d44e005dbe0399643d953a2202dd9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org9111ebeb52e50edcc6fcb3eea1a2d3ae3c2c75d3668923e83c59d0f47245515060f020a3fe11dc2cc0e1e8ed9341b4cd14944db806ca4fc95456bbe45d95c43a5f9aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b77193bcffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b7719300000080000001cf01000000007fffffff1234567889643d953a2202ee9b5c8f06f3b6c2c695f2dfc3c26f31f5fef8661f8c5fe7c95aeb5c5b0435b045f8324dd18ea905171ec2be89f879d01d5994132048d92ea020778cbdf31c605e2fdcef69380937c2cf221f7d11526f286c39f49641452ba9012521c705094899sip:pauline@sip.example.org72d80ab1cad243cf45634980c1d02cfb2df81ce0dd5dfcf1ebeacfc5345a917625d9ac653a83c4559cb0ae7394e7cd3b2d3c57bb28e62068d2df23e8f9b77193f69aa1e5e4c7ec88fa389a9f6b8879b42d3c57bb28e62068d2df23e8f9b7719322ffd51e7316a6c6f53a50fcf01b01bf2d3c57bb28e62068d2df23e8f9b771930000000f00000000000000007fffffff"); + xmlFreeDoc(cacheBufferAlice); + cacheBufferAlice = xmlParseDoc((unsigned char *)cachedStringBuffer); + + /* retry to encrypt a msg */ + retval = lime_createMultipartMessage(cacheBufferAlice, "text/plain", (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage); BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (retval == 0) { ms_message("Encrypted msg created is %s", multipartMessage); } /* decrypt the multipart msg */ - retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage, &decryptedContentType); + retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage, &decryptedContentType, 1000); BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (retval == 0) {