lime.c 32.1 KB
Newer Older
1
#include "lime.h"
2 3 4 5 6 7
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_LIME

8 9 10 11 12 13 14 15 16 17 18 19
#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
20 21 22 23 24 25 26 27

/**
 * @brief check at runtime if LIME is available
 *
 * @return TRUE when Lime was fully compiled, FALSE when it wasn't
 */
bool_t lime_is_available() { return TRUE; }

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/**
 * @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);
	}
}



106 107 108 109 110 111 112 113 114
/**
 * @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
 */
static int lime_getSelfZid(xmlDocPtr cacheBuffer, uint8_t selfZid[25]) {
johan's avatar
johan committed
115 116 117
	xmlNodePtr cur;
	xmlChar *selfZidHex;

118 119
	if (cacheBuffer == NULL ) {
		return LIME_INVALID_CACHE;
120 121
	}

johan's avatar
johan committed
122
	cur = xmlDocGetRootElement(cacheBuffer);
123
	/* if we found a root element, parse its children node */
124
	if (cur!=NULL)
125 126 127
	{
		cur = cur->xmlChildrenNode;
	}
johan's avatar
johan committed
128
	selfZidHex = NULL;
129 130
	while (cur!=NULL) {
		if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */
131
			selfZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode, 1);
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
			/* 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) {
johan's avatar
johan committed
150
	xmlNodePtr cur;
151

152 153 154 155 156 157 158 159 160
	/* 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;

johan's avatar
johan committed
161
	cur = xmlDocGetRootElement(cacheBuffer);
162
	/* if we found a root element, parse its children node */
163
	if (cur!=NULL)
164 165 166 167 168 169 170
	{
		cur = cur->xmlChildrenNode;
	}
	while (cur!=NULL) { /* loop on all peer nodes */
		uint8_t matchingURIFlag = 0; /* this flag is set to one if we found the requested sipURI in the current peer node */
		if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))) { /* found a peer node, check if there is a matching sipURI node in it */
			xmlNodePtr peerNodeChildren = cur->xmlChildrenNode;
johan's avatar
johan committed
171
			matchingURIFlag = 0;
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

			/* 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));
188 189
				uint8_t itemFound = 0; /* count the item found, we must get all of the requested infos: 5 nodes*/
				uint8_t pvs = 0;
190

191
				peerNodeChildren = cur->xmlChildrenNode; /* reset peerNodeChildren to the first child of node */
192
				while (peerNodeChildren!=NULL && itemFound<5) {
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
					xmlChar *nodeContent = NULL;
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(currentPeerKeys->peerZID, nodeContent, 24);
						itemFound++;
					}

					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(currentPeerKeys->key, nodeContent, 64);
						itemFound++;
					}
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(currentPeerKeys->sessionId, nodeContent, 64);
						itemFound++;
					}
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
						uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
johan's avatar
johan committed
212
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
213 214 215 216 217
						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++;
					}
218 219 220 221 222
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
						itemFound++;
					}
223 224 225 226 227

					xmlFree(nodeContent);
					peerNodeChildren = peerNodeChildren->next;
				}

228 229
				/* check if we have all the requested information and the PVS flag is set to 1 */
				if (itemFound == 5 && pvs == 1) {
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
					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) {
johan's avatar
johan committed
248 249 250 251 252 253
	uint8_t peerZidHex[25];
	/* to check we collect all the information needed from the cache and that pvs(boolean for previously verified Sas) is set in cache */
	uint8_t itemFound = 0;
	uint8_t pvs = 0;
	xmlNodePtr cur;

254 255 256 257 258 259 260 261
	if (cacheBuffer == NULL ) { /* there is no cache return error */
		return LIME_INVALID_CACHE;
	}

	/* get the given ZID into hex format */
	lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
	peerZidHex[24]='\0'; /* must be a null terminated string */

johan's avatar
johan committed
262
	cur = xmlDocGetRootElement(cacheBuffer);
263
	/* if we found a root element, parse its children node */
264
	if (cur!=NULL)
265 266 267 268
	{
		cur = cur->xmlChildrenNode;

	}
269 270

	while (cur!=NULL) { /* loop on all peer nodes */
271 272 273 274
		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;
275
				while (peerNodeChildren != NULL && itemFound<4) { /* look for the tag we want to read : rcvKey, rcvSId, rcvIndex and pvs*/
276 277 278 279 280 281 282 283 284 285 286 287 288
					xmlChar *nodeContent = NULL;
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(associatedKey->key, nodeContent, 64);
						itemFound++;
					}
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(associatedKey->sessionId, nodeContent, 64);
						itemFound++;
					}
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
						uint8_t sessionIndexBuffer[4]; /* session index is a uint32_t but we first retrieved it as an hexa string, convert it to a 4 uint8_t buffer */
johan's avatar
johan committed
289
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
290 291 292 293 294
						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++;
					}
295 296 297 298 299
					if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"pvs")) {
						nodeContent = xmlNodeListGetString(cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
						lime_strToUint8(&pvs, nodeContent, 2); /* pvs is retrieved as a 2 characters hexa string, convert it to an int8 */
						itemFound++;
					}
300 301 302
					xmlFree(nodeContent);
					peerNodeChildren = peerNodeChildren->next;
				}
303 304
				xmlFree(currentZidHex);
				break; /* we parsed the peer node we were looking for, get out of the main while */
305 306 307 308 309 310
			}
			xmlFree(currentZidHex);
		}
		cur = cur->next;
	}

311 312 313 314 315 316 317
	/* if we manage to find the correct key information and that pvs is set to 1, return 0 (success) */
	if ((pvs == 1) && (itemFound == 4)) {
		return 0;
	}

	/* otherwise, key wasn't found or is invalid */
	return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
318 319 320
}

int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) {
johan's avatar
johan committed
321 322 323 324 325 326 327
	xmlNodePtr cur;
	uint8_t peerZidHex[25];
	uint8_t keyHex[65]; /* key is 32 bytes long -> 64 bytes string + null termination */
	uint8_t sessionIdHex[65]; /* sessionId is 32 bytes long -> 64 bytes string + null termination */
	uint8_t sessionIndexHex[9]; /*  sessionInedx is an uint32_t : 4 bytes long -> 8 bytes string + null termination */
	uint8_t itemFound = 0;

328 329 330 331 332 333 334 335
	if (cacheBuffer == NULL ) { /* there is no cache return error */
		return LIME_INVALID_CACHE;
	}

	/* get the given ZID into hex format */
	lime_int8ToStr(peerZidHex, associatedKey->peerZID, 12);
	peerZidHex[24]='\0'; /* must be a null terminated string */

johan's avatar
johan committed
336
	cur = xmlDocGetRootElement(cacheBuffer);
337
	/* if we found a root element, parse its children node */
338
	if (cur!=NULL)
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	{
		cur = cur->xmlChildrenNode;

	}

	/* convert the given tag content to null terminated Hexadecimal strings */
	lime_int8ToStr(keyHex, associatedKey->key, 32);
	keyHex[64] = '\0';
	lime_int8ToStr(sessionIdHex, associatedKey->sessionId, 32);
	sessionIdHex[64] = '\0';
	sessionIndexHex[0] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>28)&0x0F));
	sessionIndexHex[1] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>24)&0x0F));
	sessionIndexHex[2] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>20)&0x0F));
	sessionIndexHex[3] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>16)&0x0F));
	sessionIndexHex[4] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>12)&0x0F));
	sessionIndexHex[5] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>8)&0x0F));
	sessionIndexHex[6] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex>>4)&0x0F));
	sessionIndexHex[7] = lime_byteToChar((uint8_t)((associatedKey->sessionIndex)&0x0F));
	sessionIndexHex[8] = '\0';

	while (cur!=NULL && itemFound<3) { /* loop on all peer nodes */
		if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
			xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
			if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
				xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
				while (peerNodeChildren != NULL && itemFound<3) { /* look for the tag we want to write */
					if (role == LIME_RECEIVER) { /* writing receiver key */
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvKey")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
							itemFound++;
						}
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvSId")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
							itemFound++;
						}
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"rcvIndex")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
							itemFound++;
						}
					} else { /* writing sender key */
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndKey")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)keyHex);
							itemFound++;
						}
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndSId")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIdHex);
							itemFound++;
						}
						if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"sndIndex")) {
							xmlNodeSetContent(peerNodeChildren, (const xmlChar *)sessionIndexHex);
							itemFound++;
						}
					}
					peerNodeChildren = peerNodeChildren->next;
				}
			}
			xmlFree(currentZidHex);
		}
		cur = cur->next;
	}

400

401 402 403
	return 0;
}

404 405 406 407 408 409 410 411 412
/**
 * @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
 */
static int lime_deriveKey(limeKey_t *key) {
johan's avatar
johan committed
413 414 415
	uint8_t inputData[55];
	uint8_t derivedKey[32];

416 417 418 419
	if (key == NULL) {
		return LIME_UNABLE_TO_DERIVE_KEY;
	}

420 421
#if 0
	/*not doing anything yet since key and sessionId are array, not pointers*/
422 423 424
	if ((key->key == NULL) || (key->sessionId == NULL)) {
		return LIME_UNABLE_TO_DERIVE_KEY;
	}
425
#endif
426 427 428 429 430 431 432

 	/* Derivation is made derived Key = HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||0x00000100)*/
	/* total data to be hashed is       55 bytes  :           4   +      10     +   1 +     32   +   4         +   4 */
	inputData[0] = 0x00;
	inputData[1] = 0x00;
	inputData[2] = 0x00;
	inputData[3] = 0x01;
433

434 435 436
	memcpy(inputData+4, "MessageKey", 10);

	inputData[14] = 0x00;
437

438
	memcpy(inputData+15, key->sessionId, 32);
439

440 441 442 443
	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);
444

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
	inputData[51] = 0x00;
	inputData[52] = 0x00;
	inputData[53] = 0x01;
	inputData[54] = 0x00;

	/* derive the key in a temp buffer */
#if POLARSSL_VERSION_NUMBER >= 0x01030000 /* for Polarssl version 1.3 */
	sha256_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
#else /* for Polarssl version 1.2 */
	sha2_hmac(key->key, 32, inputData, 55, derivedKey, 0); /* last param to zero to select SHA256 and not SHA224 */
#endif /* POLARSSL_VERSION_NUMBER */

	/* overwrite the old key with the derived one */
	memcpy(key->key, derivedKey, 32);

	/* increment the session Index */
	key->sessionIndex += 1;
	return 0;
}

void lime_freeKeys(limeURIKeys_t associatedKeys) {
	int i;

	/* free all associated keys */
	for (i=0; i< associatedKeys.associatedZIDNumber; i++) {
		if (associatedKeys.peerKeys[i] != NULL) {
			free(associatedKeys.peerKeys[i]);
			associatedKeys.peerKeys[i] = NULL;
		}
	}

	free(associatedKeys.peerKeys);

	/* free sipURI string */
	free(associatedKeys.peerURI);
}

int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
	uint8_t authenticatedData[28];
johan's avatar
johan committed
484 485
	gcm_context gcmContext;
	/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
	memcpy(authenticatedData, selfZID, 12);
	memcpy(authenticatedData+12, key->peerZID, 12);
	authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
	authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
	authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
	authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);

	/* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
	/* tag is 16 bytes long and is set in the 16 first bytes of the encrypted message */
	gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
	gcm_crypt_and_tag(&gcmContext, GCM_ENCRYPT, messageLength, key->key+24, 8, authenticatedData, 28, plainMessage, encryptedMessage+16, 16, encryptedMessage);
	gcm_free(&gcmContext);

	return 0;
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
	gcm_context *gcmContext;

	if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
		gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
		*cryptoContext = (void *)gcmContext;
		gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
		gcm_starts(gcmContext, GCM_ENCRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
	} else { /* this is not the first call, get the context */
		gcmContext = (gcm_context *)*cryptoContext;
	}

	if (length != 0) {
		gcm_update(gcmContext, length, (const unsigned char *)plain, (unsigned char *)cipher);
	} else { /* lenght is 0, finish the stream */
		gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
		gcm_free(gcmContext);
		free(*cryptoContext);
		*cryptoContext = NULL;
	}

	return 0;
}

int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
	gcm_context *gcmContext;

	if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
		gcmContext = (gcm_context *)malloc(sizeof(gcm_context));
		*cryptoContext = (void *)gcmContext;
		gcm_init(gcmContext, POLARSSL_CIPHER_ID_AES, key, 192);
		gcm_starts(gcmContext, GCM_DECRYPT, key+24, 8, NULL, 0); /* key contains 192bits of key || 64 bits of Initialisation Vector */
	} else { /* this is not the first call, get the context */
		gcmContext = (gcm_context *)*cryptoContext;
	}
537

538 539 540 541 542 543 544 545 546 547 548 549 550
	if (length != 0) {
		gcm_update(gcmContext, length, (const unsigned char *)cipher, (unsigned char *)plain);
	} else { /* lenght is 0, finish the stream */
		gcm_finish(gcmContext, NULL, 0); /* do not generate tag */
		gcm_free(gcmContext);
		free(*cryptoContext);
		*cryptoContext = NULL;
	}

	return 0;
}


551 552
int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) {
	uint8_t authenticatedData[28];
johan's avatar
johan committed
553 554 555 556
	gcm_context gcmContext;
	int retval;

	/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
557 558 559 560 561 562 563 564 565 566 567
	memcpy(authenticatedData, key->peerZID, 12);
	memcpy(authenticatedData+12, selfZID, 12);
	authenticatedData[24] = (uint8_t)((key->sessionIndex>>24)&0x000000FF);
	authenticatedData[25] = (uint8_t)((key->sessionIndex>>16)&0x000000FF);
	authenticatedData[26] = (uint8_t)((key->sessionIndex>>8)&0x000000FF);
	authenticatedData[27] = (uint8_t)(key->sessionIndex&0x000000FF);

	/* AES-GCM : key is 192 bits long, Init Vector 64 bits. 256 bits key given is AES key||IV */
	/* tag is 16 bytes long and is the 16 first bytes of the encrypted message */
	gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key->key, 192);
	/* messageLength-16 is the length of encrypted data, messageLength include the 16 bytes tag included at the begining of encryptedMessage */
johan's avatar
johan committed
568
	retval = gcm_auth_decrypt(&gcmContext, messageLength-16, key->key+24, 8, authenticatedData, 28, encryptedMessage, 16, encryptedMessage+16, plainMessage);
569 570 571 572 573 574 575 576 577
	gcm_free(&gcmContext);
	/* add the null termination char */
	plainMessage[messageLength-16] = '\0';

	return retval;
}

int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
	uint8_t selfZidHex[25];
johan's avatar
johan committed
578 579 580 581 582 583 584 585 586
	uint8_t selfZid[12]; /* same data but in byte buffer */
	uint32_t encryptedMessageLength;
	limeURIKeys_t associatedKeys;
	xmlDocPtr xmlOutputMessage;
	xmlNodePtr rootNode;
	int i;
	int xmlStringLength;

	/* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
587 588 589 590 591 592
	if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
		return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
	}
	lime_strToUint8(selfZid, selfZidHex, 24);

	/* encrypted message length is plaintext + 16 for tag */
johan's avatar
johan committed
593
	encryptedMessageLength = strlen((char *)message) + 16;
594 595 596 597 598 599 600 601 602 603 604 605 606 607

	/* retrieve keys associated to the peer URI */
	associatedKeys.peerURI = (uint8_t *)malloc(strlen((char *)peerURI)+1);
	strcpy((char *)(associatedKeys.peerURI), (char *)peerURI);
	associatedKeys.associatedZIDNumber  = 0;
	associatedKeys.peerKeys = NULL;

	if (lime_getCachedSndKeysByURI(cacheBuffer, &associatedKeys) != 0) {
		lime_freeKeys(associatedKeys);
		return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
	}

	if (associatedKeys.associatedZIDNumber == 0) {
		lime_freeKeys(associatedKeys);
608
		return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
609 610 611
	}

	/* create an xml doc to hold the multipart message */
johan's avatar
johan committed
612
	xmlOutputMessage = xmlNewDoc((const xmlChar *)"1.0");
613
	/* root tag is "doc" */
johan's avatar
johan committed
614
	rootNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"doc", NULL);
615 616 617 618 619 620
	xmlDocSetRootElement(xmlOutputMessage, rootNode);
	/* add the self ZID child */
	xmlNewTextChild(rootNode, NULL, (const xmlChar *)"ZID", selfZidHex);

	/* loop on all keys found */
	for (i=0; i<associatedKeys.associatedZIDNumber; i++) {
johan's avatar
johan committed
621 622 623 624 625 626
		uint8_t peerZidHex[25];
		uint8_t sessionIndexHex[9];
		xmlNodePtr msgNode;
		int b64Size;
		char *encryptedMessageb64;

627 628 629 630 631 632 633 634 635 636 637
		/* 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> */
johan's avatar
johan committed
638
		msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
		lime_int8ToStr(peerZidHex, currentKey->peerZID, 12);
		peerZidHex[24] = '\0';
		sessionIndexHex[0] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>28)&0x0F));
		sessionIndexHex[1] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>24)&0x0F));
		sessionIndexHex[2] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>20)&0x0F));
		sessionIndexHex[3] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>16)&0x0F));
		sessionIndexHex[4] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>12)&0x0F));
		sessionIndexHex[5] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>8)&0x0F));
		sessionIndexHex[6] = lime_byteToChar((uint8_t)((currentKey->sessionIndex>>4)&0x0F));
		sessionIndexHex[7] = lime_byteToChar((uint8_t)((currentKey->sessionIndex)&0x0F));
		sessionIndexHex[8] = '\0';

		xmlNewTextChild(msgNode, NULL, (const xmlChar *)"pzid", peerZidHex);
		xmlNewTextChild(msgNode, NULL, (const xmlChar *)"index", sessionIndexHex);

		/* convert the cipherText to base 64 */
johan's avatar
johan committed
655 656
		b64Size =  b64_encode(NULL, encryptedMessageLength, NULL, 0);
		encryptedMessageb64 = (char *)malloc(b64Size+1);
657 658 659 660 661
		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);
662

663 664
		/* add the message Node into the doc */
		xmlAddChild(rootNode, msgNode);
665

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
		/* update the key used */
		lime_deriveKey(currentKey);
		lime_setCachedKey(cacheBuffer, currentKey, LIME_SENDER);
	}

	/* dump the whole message doc into the output */
	xmlDocDumpFormatMemoryEnc(xmlOutputMessage, output, &xmlStringLength, "UTF-8", 0);
	xmlFreeDoc(xmlOutputMessage);

	lime_freeKeys(associatedKeys);

	return 0;
}

int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) {
681
	int retval;
johan's avatar
johan committed
682 683 684 685 686 687 688 689 690
	uint8_t selfZidHex[25];
	uint8_t selfZid[12]; /* same data but in byte buffer */
	limeKey_t associatedKey;
	xmlChar *peerZidHex = NULL;
	xmlNodePtr cur;
	uint8_t *encryptedMessage = NULL;
	uint32_t encryptedMessageLength = 0;
	uint32_t usedSessionIndex = 0;
	xmlDocPtr xmlEncryptedMessage;
691 692 693 694

	if (cacheBuffer == NULL) {
		return LIME_INVALID_CACHE;
	}
695 696 697 698 699 700 701
	/* retrieve selfZIDHex from cache(return a 24 char hexa string + null termination) */
	if (lime_getSelfZid(cacheBuffer, selfZidHex) != 0) {
		return LIME_UNABLE_TO_DECRYPT_MESSAGE;
	}
	lime_strToUint8(selfZid, selfZidHex, 24);

	/* parse the message into an xml doc */
702 703 704 705
	/* make sure we have a valid xml message before trying to parse it */
	if (memcmp(message, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", 38) != 0 ) {
		return LIME_INVALID_ENCRYPTED_MESSAGE;
	}
johan's avatar
johan committed
706
	xmlEncryptedMessage = xmlParseDoc((const xmlChar *)message);
707 708 709
	if (xmlEncryptedMessage == NULL) {
		return LIME_INVALID_ENCRYPTED_MESSAGE;
	}
710 711

	/* retrieve the sender ZID which is the first child of root */
johan's avatar
johan committed
712
	cur = xmlDocGetRootElement(xmlEncryptedMessage);
713 714 715
	if (cur != NULL) {
		cur = cur->xmlChildrenNode;
		if ((!xmlStrcmp(cur->name, (const xmlChar *)"ZID"))){ /* sender ZID found, extract it */
716
			peerZidHex = xmlNodeListGetString(xmlEncryptedMessage, cur->xmlChildrenNode, 1);
717 718 719 720 721 722 723 724
			/* convert it from hexa string to bytes string and set the result in the associatedKey structure */
			lime_strToUint8(associatedKey.peerZID, peerZidHex, strlen((char *)peerZidHex));
			cur = cur->next;
		}
	}

	if (peerZidHex != NULL) {
		/* get from cache the matching key */
725
		retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
726

727 728 729 730 731
		if (retval != 0) {
			xmlFree(peerZidHex);
			xmlFreeDoc(xmlEncryptedMessage);
			return retval;
		}
732

733 734 735 736 737 738
		/* 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) */
johan's avatar
johan committed
739 740 741
				xmlChar *sessionIndexHex;
				xmlChar *encryptedMessageb64;

742
				msgChildrenNode = msgChildrenNode->next;
johan's avatar
johan committed
743
				sessionIndexHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
744 745 746 747 748 749 750 751 752 753 754 755
				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 */
johan's avatar
johan committed
756
				encryptedMessageb64 = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
757 758 759 760 761 762 763 764
				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);
765 766 767 768 769 770 771 772 773 774 775 776
			}
	}

	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 */
777 778 779 780 781 782 783 784 785 786 787 788 789 790
	if (usedSessionIndex < associatedKey.sessionIndex) {
		/* something wen't wrong with the cache, this shall never happend */
		free(encryptedMessage);
		return LIME_UNABLE_TO_DECRYPT_MESSAGE;
	}

	if ((usedSessionIndex - associatedKey.sessionIndex > MAX_DERIVATION_NUMBER) ) {
		/* we missed to many messages, ask for a cache reset via a ZRTP call */
		free(encryptedMessage);
		return LIME_UNABLE_TO_DECRYPT_MESSAGE;
	}

	while (usedSessionIndex>associatedKey.sessionIndex) {
		lime_deriveKey(&associatedKey);
791 792 793 794
	}

	/* 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 */
795 796
	retval = lime_decryptMessage(&associatedKey, encryptedMessage, encryptedMessageLength, selfZid, *output);

797 798
	free(encryptedMessage);

799 800 801 802 803 804
	if (retval!=0 ) {
		free(*output);
		*output = NULL;
		return LIME_UNABLE_TO_DECRYPT_MESSAGE;
	}

805 806 807 808 809 810
	/* update used key */
	lime_deriveKey(&associatedKey);
	lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);

	return 0;
}
811

812 813 814 815 816 817 818 819 820 821 822 823

#else /* HAVE_LIME */

bool_t lime_is_available() { return FALSE; }
int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { return LIME_NOT_ENABLED;}
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) { return LIME_NOT_ENABLED;}
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {return LIME_NOT_ENABLED;}


#endif /* HAVE_LIME */

824 825 826 827 828 829 830 831
char *lime_error_code_to_string(int errorCode) {
	switch (errorCode) {
		case LIME_INVALID_CACHE: return "Invalid ZRTP cache";
		case LIME_UNABLE_TO_DERIVE_KEY: return "Unable to derive Key";
		case LIME_UNABLE_TO_ENCRYPT_MESSAGE: return "Unable to encrypt message";
		case LIME_UNABLE_TO_DECRYPT_MESSAGE: return "Unable to decrypt message";
		case LIME_NO_VALID_KEY_FOUND_FOR_PEER: return "No valid key found";
		case LIME_INVALID_ENCRYPTED_MESSAGE: return "Invalid encrypted message";
832
		case LIME_NOT_ENABLED: return "Lime not enabled at build";
833 834 835 836
	}
	return "Unknow error";

}