lime.c 43.4 KB
Newer Older
1 2
/*
linphone
3
Copyright (C) 2017  Belledonne Communications SARL
4 5 6 7 8 9 10 11 12 13 14 15 16

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19
*/

20
#include "lime.h"
21 22 23 24 25
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_LIME
26
#include "private.h"
johan's avatar
johan committed
27
#include "bctoolbox/crypto.h"
johan's avatar
johan committed
28
#include "bctoolbox/port.h"
29
#include "bzrtp/bzrtp.h"
30

31 32
#define FILE_TRANSFER_KEY_SIZE 32

33 34 35 36 37
/**
 * @brief check at runtime if LIME is available
 *
 * @return TRUE when Lime was fully compiled, FALSE when it wasn't
 */
38
bool_t lime_is_available(void) { return TRUE; }
39

40 41
int lime_getCachedSndKeysByURI(void *cachedb, limeURIKeys_t *associatedKeys) {
	sqlite3 *db = (sqlite3 *)cachedb;
johan's avatar
johan committed
42
	size_t keysFound = 0; /* used to detect the no key found error because of validity expired */
43 44 45 46 47
	char *stmt = NULL;
	int ret;
	sqlite3_stmt *sqlStmt = NULL;
	int length =0;
	uint8_t pvsOne[1] = {0x01}; /* used to bind this specific byte value to a blob WHERE constraint in the query */
48

49
	if (cachedb == NULL ) { /* there is no cache return error */
50 51 52 53 54 55 56
		return LIME_INVALID_CACHE;
	}

	/* reset number of associated keys and their buffer */
	associatedKeys->associatedZIDNumber = 0;
	associatedKeys->peerKeys = NULL;

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
	/* query the DB: join ziduri, lime and zrtp tables : retrieve zuid(for easier key update in cache), peerZID, sndKey, sndSId, sndIndex, valid where self and peer ZIDs are matching constraint and pvs is raised */
	/* Note: retrieved potentially expired keys, just to be able to send a different status to caller(no keys found is not expired key found) */
	/* if we do not have self uri in associatedKeys, just retrieve any available key matching peer URI */
	if (associatedKeys->selfURI == NULL) {
		stmt = sqlite3_mprintf("SELECT zu.zuid, zu.zid as peerZID, l.sndkey, l.sndSId, l.sndIndex, l.valid FROM ziduri as zu LEFT JOIN zrtp as z ON z.zuid=zu.zuid LEFT JOIN lime as l ON z.zuid=l.zuid WHERE zu.peeruri=? AND z.pvs=?;");
		ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL);
		sqlite3_free(stmt);
		if (ret != SQLITE_OK) {
			return LIME_INVALID_CACHE;
		}
		sqlite3_bind_text(sqlStmt, 1, associatedKeys->peerURI,-1, SQLITE_TRANSIENT);
		sqlite3_bind_blob(sqlStmt, 2, pvsOne, 1, SQLITE_TRANSIENT);
	} else { /* we have a self URI, so include it in the query */
		stmt = sqlite3_mprintf("SELECT zu.zuid, zu.zid as peerZID, l.sndkey, l.sndSId, l.sndIndex, l.valid FROM ziduri as zu LEFT JOIN zrtp as z ON z.zuid=zu.zuid LEFT JOIN lime as l ON z.zuid=l.zuid WHERE zu.selfuri=? AND zu.peeruri=? AND z.pvs=?;");
		ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL);
		sqlite3_free(stmt);
		if (ret != SQLITE_OK) {
			return LIME_INVALID_CACHE;
		}
		sqlite3_bind_text(sqlStmt, 1, associatedKeys->selfURI,-1, SQLITE_TRANSIENT);
		sqlite3_bind_text(sqlStmt, 2, associatedKeys->peerURI,-1, SQLITE_TRANSIENT);
		sqlite3_bind_blob(sqlStmt, 3, pvsOne, 1, SQLITE_TRANSIENT);
	}

	/* parse all retrieved rows */
	while ((ret = sqlite3_step(sqlStmt)) == SQLITE_ROW) {
		/* allocate a new limeKey_t structure to hold the retreived keys */
		limeKey_t *currentPeerKey = (limeKey_t *)bctbx_malloc0(sizeof(limeKey_t));
		bctoolboxTimeSpec currentTimeSpec;
		bctoolboxTimeSpec validityTimeSpec;
		validityTimeSpec.tv_sec=0;
		validityTimeSpec.tv_nsec=0;

		/* get zuid from column 0 */
		currentPeerKey->zuid = sqlite3_column_int(sqlStmt, 0);

		/* retrieve values : peerZid, sndKey, sndSId, sndIndex, valid from columns 1,2,3,4,5 */
		length = sqlite3_column_bytes(sqlStmt, 1);
		if (length==12) { /* peerZID */
Benjamin REIS's avatar
Benjamin REIS committed
96
			memcpy(currentPeerKey->peerZID, sqlite3_column_blob(sqlStmt, 1), (size_t)length);
97 98 99
		} else { /* something wrong with that one, skip it */
			continue;
		}
100

101 102
		length = sqlite3_column_bytes(sqlStmt, 2);
		if (length==32) { /* sndKey */
Benjamin REIS's avatar
Benjamin REIS committed
103
			memcpy(currentPeerKey->key, sqlite3_column_blob(sqlStmt, 2), (size_t)length);
104 105 106
		} else { /* something wrong with that one, skip it */
			continue;
		}
107

108 109
		length = sqlite3_column_bytes(sqlStmt, 3);
		if (length==32) { /* sndSId */
Benjamin REIS's avatar
Benjamin REIS committed
110
			memcpy(currentPeerKey->sessionId, sqlite3_column_blob(sqlStmt, 3), (size_t)length);
111 112 113
		} else { /* something wrong with that one, skip it */
			continue;
		}
114

115 116 117 118 119 120 121 122 123 124
		length = sqlite3_column_bytes(sqlStmt, 4);
		if (length==4) { /* sndIndex : 4 bytes of a uint32_t, stored as a blob in big endian */
			uint8_t *sessionId = (uint8_t *)sqlite3_column_blob(sqlStmt, 4);
			currentPeerKey->sessionIndex = ((uint32_t)(sessionId[0]))<<24 |
							((uint32_t)(sessionId[1]))<<16 |
							((uint32_t)(sessionId[2]))<<8 |
							((uint32_t)(sessionId[3]));
		} else { /* something wrong with that one, skip it */
			continue;
		}
125

126 127 128
		length = sqlite3_column_bytes(sqlStmt, 5);
		if (length==8) { /* sndIndex : 8 bytes of a int64_t, stored as a blob in big endian */
			uint8_t *validity = (uint8_t *)sqlite3_column_blob(sqlStmt, 5);
Benjamin REIS's avatar
Benjamin REIS committed
129
			validityTimeSpec.tv_sec = (int64_t)(((uint64_t)(validity[0]))<<56 |
130 131 132 133 134 135
							((uint64_t)(validity[1]))<<48 |
							((uint64_t)(validity[2]))<<40 |
							((uint64_t)(validity[3]))<<32 |
							((uint64_t)(validity[4]))<<24 |
							((uint64_t)(validity[5]))<<16 |
							((uint64_t)(validity[6]))<<8 |
Benjamin REIS's avatar
Benjamin REIS committed
136
							((uint64_t)(validity[7])));
137 138 139
		} else { /* something wrong with that one, skip it */
			continue;
		}
140

141 142 143 144 145 146
		/* count is a found even if it may be expired */
		keysFound++;

		/* check validity */
		bctbx_get_utc_cur_time(&currentTimeSpec);
		if (validityTimeSpec.tv_sec == 0 || bctbx_timespec_compare(&currentTimeSpec, &validityTimeSpec)<0) {
147
			associatedKeys->associatedZIDNumber++;
148 149 150 151 152 153 154
			/* extend array of pointer to limeKey_t structures to add the one we found */
			associatedKeys->peerKeys = (limeKey_t **)bctbx_realloc(associatedKeys->peerKeys, (associatedKeys->associatedZIDNumber)*sizeof(limeKey_t *));

			/* add the new entry at the end */
			associatedKeys->peerKeys[associatedKeys->associatedZIDNumber-1] = currentPeerKey;
		} else {
			free(currentPeerKey);
155 156
		}
	}
157 158 159 160 161 162 163 164 165

	sqlite3_finalize(sqlStmt);

	/* something is wrong with the cache? */
	if (ret!=SQLITE_DONE) {
		return LIME_INVALID_CACHE;
	}

	/* we're done, check what we have */
johan's avatar
johan committed
166 167 168 169 170 171 172
	if (associatedKeys->associatedZIDNumber == 0) {
		if (keysFound == 0) {
			return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
		} else {
			return LIME_PEER_KEY_HAS_EXPIRED;
		}
	}
173
	return 0;
174

175 176
}

177 178 179 180 181 182 183
int lime_getCachedRcvKeyByZid(void *cachedb, limeKey_t *associatedKey, const char *selfURI, const char *peerURI) {
	sqlite3 *db = (sqlite3 *)cachedb;
	char *stmt = NULL;
	int ret;
	sqlite3_stmt *sqlStmt = NULL;
	int length =0;
	uint8_t pvsOne[1] = {0x01}; /* used to bind this specific byte value to a blob WHERE constraint in the query */
johan's avatar
johan committed
184

185 186

	if (db == NULL) { /* there is no cache return error */
187 188 189
		return LIME_INVALID_CACHE;
	}

190 191 192 193 194 195 196 197 198 199 200 201 202 203
	/* query the DB: join ziduri, lime and zrtp tables : */
	/* retrieve zuid(for easier key update in cache), rcvKey, rcvSId, rcvIndex where self/peer uris and peer zid are matching constraint(unique row) and pvs is raised */
	/* Note: retrieved potentially expired keys, just to be able to send a different status to caller(no keys found is not expired key found) */
	/* if we do not have self uri in associatedKeys, just retrieve any available key matching peer URI */
	stmt = sqlite3_mprintf("SELECT zu.zuid, l.rcvkey, l.rcvSId, l.rcvIndex FROM ziduri as zu LEFT JOIN zrtp as z ON z.zuid=zu.zuid LEFT JOIN lime as l ON z.zuid=l.zuid WHERE zu.selfuri=? AND zu.peeruri=? AND zu.zid=? AND z.pvs=? LIMIT 1;");
	ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL);
	sqlite3_free(stmt);
	if (ret != SQLITE_OK) {
		return LIME_INVALID_CACHE;
	}
	sqlite3_bind_text(sqlStmt, 1, selfURI,-1, SQLITE_TRANSIENT);
	sqlite3_bind_text(sqlStmt, 2, peerURI,-1, SQLITE_TRANSIENT);
	sqlite3_bind_blob(sqlStmt, 3, associatedKey->peerZID, 12, SQLITE_TRANSIENT);
	sqlite3_bind_blob(sqlStmt, 4, pvsOne, 1, SQLITE_TRANSIENT);
204 205


206 207 208
	if ((ret = sqlite3_step(sqlStmt)) == SQLITE_ROW) { /* we found a row */
		/* get zuid from column 0 */
		associatedKey->zuid = sqlite3_column_int(sqlStmt, 0);
209

210 211 212
		/* retrieve values : rcvKey, rcvSId, rcvIndex from columns 1,2,3 */
		length = sqlite3_column_bytes(sqlStmt, 1);
		if (length==32) { /* rcvKey */
Benjamin REIS's avatar
Benjamin REIS committed
213
			memcpy(associatedKey->key, sqlite3_column_blob(sqlStmt, 1), (size_t)length);
214 215 216 217 218 219 220
		} else { /* something wrong */
			sqlite3_finalize(sqlStmt);
			return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
		}

		length = sqlite3_column_bytes(sqlStmt, 2);
		if (length==32) { /* rcvSId */
Benjamin REIS's avatar
Benjamin REIS committed
221
			memcpy(associatedKey->sessionId, sqlite3_column_blob(sqlStmt, 2), (size_t)length);
222 223 224
		} else { /* something wrong */
			sqlite3_finalize(sqlStmt);
			return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
225 226
		}

227 228 229 230 231 232 233 234 235 236 237 238 239
		length = sqlite3_column_bytes(sqlStmt, 3);
		if (length==4) { /* rcvKey */
			uint8_t *sessionId = (uint8_t *)sqlite3_column_blob(sqlStmt, 3);
			associatedKey->sessionIndex = ((uint32_t)(sessionId[0]))<<24 |
							((uint32_t)(sessionId[1]))<<16 |
							((uint32_t)(sessionId[2]))<<8 |
							((uint32_t)(sessionId[3]));
		} else { /* something wrong */
			sqlite3_finalize(sqlStmt);
			return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
		}

		sqlite3_finalize(sqlStmt);
240 241 242
		return 0;
	}

243 244 245 246 247 248
	/* something is wrong with the cache? */
	if (ret!=SQLITE_DONE) {
		return LIME_INVALID_CACHE;
	}

	/* reach here if the query executed correctly but returned no result */
249
	return LIME_NO_VALID_KEY_FOUND_FOR_PEER;
250 251
}

252 253 254
int lime_setCachedKey(void *cachedb, limeKey_t *associatedKey, uint8_t role, uint64_t validityTimeSpan) {
	bctoolboxTimeSpec currentTime;
	/* columns to be written in cache */
255 256
	const char *colNamesSender[] = {"sndKey", "sndSId", "sndIndex"}; /* Sender never update the validity period */
	const char *colNamesReceiver[] = {"rcvKey", "rcvSId", "rcvIndex", "valid"};
257 258 259
	uint8_t *colValues[4];
	uint8_t sessionIndex[4]; /* buffer to hold the uint32_t buffer index in big endian */
	size_t colLength[] = {32, 32, 4, 8}; /* data length: keys and session ID : 32 bytes, Index: 4 bytes(uint32_t), validity : 8 bytes(UTC time as int64_t) */
260
	uint8_t colNums;
261 262

	if (cachedb == NULL  || associatedKey == NULL) { /* there is no cache return error */
263 264 265
		return LIME_INVALID_CACHE;
	}

266
	/* wrap values to be written */
267
	sessionIndex[0] = (uint8_t)((associatedKey->sessionIndex>>24)&0xFF);
268 269 270 271 272 273
	sessionIndex[1] = (associatedKey->sessionIndex>>16)&0xFF;
	sessionIndex[2] = (associatedKey->sessionIndex>>8)&0xFF;
	sessionIndex[3] = (associatedKey->sessionIndex)&0xFF;
	colValues[0] = associatedKey->key;
	colValues[1] = associatedKey->sessionId;
	colValues[2] = sessionIndex;
274

275
	/* shall we update valid column? Enforce only when receiver, if timeSpan is 0, just ignore */
johan's avatar
johan committed
276
	if (validityTimeSpan > 0 && role == LIME_RECEIVER) {
277
		bctbx_get_utc_cur_time(&currentTime);
Benjamin REIS's avatar
Benjamin REIS committed
278
		bctbx_timespec_add(&currentTime, (int64_t)validityTimeSpan);
279
		/* store the int64_t in big endian in the cache(cache is not typed, all data seen as blob) */
280 281 282 283 284 285 286 287
		colValues[3][0] = (uint8_t)((currentTime.tv_sec>>56)&0xFF);
		colValues[3][1] = (uint8_t)((currentTime.tv_sec>>48)&0xFF);
		colValues[3][2] = (uint8_t)((currentTime.tv_sec>>40)&0xFF);
		colValues[3][3] = (uint8_t)((currentTime.tv_sec>>32)&0xFF);
		colValues[3][4] = (uint8_t)((currentTime.tv_sec>>24)&0xFF);
		colValues[3][5] = (uint8_t)((currentTime.tv_sec>>16)&0xFF);
		colValues[3][6] = (uint8_t)((currentTime.tv_sec>>8)&0xFF);
		colValues[3][7] = (uint8_t)((currentTime.tv_sec)&0xFF);
288 289 290 291 292 293 294

		colNums = 4;
	} else {
		colNums = 3; /* do not write the valid column*/
	}

	/* update cache */
295 296 297 298 299
	return bzrtp_cache_write(
		cachedb, associatedKey->zuid, "lime",
		role == LIME_SENDER ? colNamesSender : colNamesReceiver,
		colValues, colLength, colNums
	);
300 301
}

302 303 304 305 306 307 308 309 310
/**
 * @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
311 312 313
	uint8_t inputData[55];
	uint8_t derivedKey[32];

314 315 316 317 318 319 320 321 322 323
	if (key == NULL) {
		return LIME_UNABLE_TO_DERIVE_KEY;
	}

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

325 326 327
	memcpy(inputData+4, "MessageKey", 10);

	inputData[14] = 0x00;
328

329
	memcpy(inputData+15, key->sessionId, 32);
330

331 332 333 334
	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);
335

336 337 338 339 340 341
	inputData[51] = 0x00;
	inputData[52] = 0x00;
	inputData[53] = 0x01;
	inputData[54] = 0x00;

	/* derive the key in a temp buffer */
jehan's avatar
jehan committed
342
	bctbx_hmacSha256(key->key, 32, inputData, 55, 32, derivedKey);
343 344 345 346 347 348 349 350 351

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

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

352
void lime_freeKeys(limeURIKeys_t *associatedKeys) {
353 354 355
	int i;

	/* free all associated keys */
356 357
	for (i=0; i< associatedKeys->associatedZIDNumber; i++) {
		if (associatedKeys->peerKeys[i] != NULL) {
Simon Morlat's avatar
Simon Morlat committed
358
			/*shouldn't we memset to zero the content of peerKeys[i] in order clear keys?*/
359 360
			free(associatedKeys->peerKeys[i]);
			associatedKeys->peerKeys[i] = NULL;
361 362 363
		}
	}

364
	bctbx_free(associatedKeys->peerKeys);
Simon Morlat's avatar
Simon Morlat committed
365
	associatedKeys->peerKeys = NULL;
366

367 368 369 370
	/* free sipURI strings */
	bctbx_free(associatedKeys->selfURI);
	associatedKeys->selfURI = NULL;
	bctbx_free(associatedKeys->peerURI);
Simon Morlat's avatar
Simon Morlat committed
371
	associatedKeys->peerURI = NULL;
372 373
}

Ghislain MARY's avatar
Ghislain MARY committed
374
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
375
	uint8_t authenticatedData[28];
johan's avatar
johan committed
376
	/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
377 378 379 380 381 382 383 384 385
	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 */
jehan's avatar
jehan committed
386
	return bctbx_aes_gcm_encrypt_and_tag(key->key, 24,
johan's avatar
johan committed
387 388 389 390 391
			plainMessage, messageLength,
			authenticatedData, 28,
			key->key+24, 8, /* IV is at the end(last 64 bits) of the given key buffer */
			encryptedMessage, 16, /* the first 16 bytes of output are the authentication tag */
			encryptedMessage+16); /* actual encrypted message starts after 16 bytes of authentication tag */
392 393 394 395

	return 0;
}

396
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
jehan's avatar
jehan committed
397
	bctbx_aes_gcm_context_t *gcmContext;
398

399 400
	if (key == NULL) return -1;

401
	if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
johan's avatar
johan committed
402
		/* key contains 192bits of key || 64 bits of Initialisation Vector, no additional data */
jehan's avatar
jehan committed
403
		gcmContext = bctbx_aes_gcm_context_new(key, 24, NULL, 0, key+24, 8, BCTBX_GCM_ENCRYPT);
johan's avatar
johan committed
404
		*cryptoContext = gcmContext;
405
	} else { /* this is not the first call, get the context */
jehan's avatar
jehan committed
406
		gcmContext = (bctbx_aes_gcm_context_t *)*cryptoContext;
407 408 409
	}

	if (length != 0) {
jehan's avatar
jehan committed
410
		bctbx_aes_gcm_process_chunk(gcmContext, (const uint8_t *)plain, length, (uint8_t *)cipher);
johan's avatar
johan committed
411
	} else { /* lenght is 0, finish the stream, no tag to be generated */
jehan's avatar
jehan committed
412
		bctbx_aes_gcm_finish(gcmContext, NULL, 0);
413 414 415 416 417 418 419
		*cryptoContext = NULL;
	}

	return 0;
}

int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {
jehan's avatar
jehan committed
420
	bctbx_aes_gcm_context_t *gcmContext;
421 422

	if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */
johan's avatar
johan committed
423
		/* key contains 192bits of key || 64 bits of Initialisation Vector, no additional data */
jehan's avatar
jehan committed
424
		gcmContext = bctbx_aes_gcm_context_new(key, 24, NULL, 0, key+24, 8, BCTBX_GCM_DECRYPT);
johan's avatar
johan committed
425
		*cryptoContext = gcmContext;
426
	} else { /* this is not the first call, get the context */
jehan's avatar
jehan committed
427
		gcmContext = (bctbx_aes_gcm_context_t *)*cryptoContext;
428
	}
429

430
	if (length != 0) {
jehan's avatar
jehan committed
431
		bctbx_aes_gcm_process_chunk(gcmContext, (const unsigned char *)cipher, length, (unsigned char *)plain);
432
	} else { /* lenght is 0, finish the stream */
jehan's avatar
jehan committed
433
		bctbx_aes_gcm_finish(gcmContext, NULL, 0);
434 435 436 437 438 439 440
		*cryptoContext = NULL;
	}

	return 0;
}


441 442
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
443 444 445
	int retval;

	/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
446 447 448 449 450 451 452 453 454
	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 */
jehan's avatar
jehan committed
455
	retval = bctbx_aes_gcm_decrypt_and_auth(key->key, 24, /* key is 192 bits long */
johan's avatar
johan committed
456 457 458 459 460 461
			encryptedMessage+16,  messageLength-16, /* encrypted message first 16 bytes store the authentication tag, then is the actual message */
			authenticatedData, 28, /* additionnal data needed for authentication */
			key->key+24, 8, /* last 8 bytes of key is the initialisation vector */
			encryptedMessage, 16, /* first 16 bytes of message is the authentication tag */
			plainMessage);

462 463 464 465 466 467
	/* add the null termination char */
	plainMessage[messageLength-16] = '\0';

	return retval;
}

468
int lime_createMultipartMessage(void *cachedb, const char *contentType, uint8_t *message, const char *selfURI, const char *peerURI, uint8_t **output) {
469
	uint8_t selfZidHex[25];
johan's avatar
johan committed
470 471
	uint8_t selfZid[12]; /* same data but in byte buffer */
	uint32_t encryptedMessageLength;
Ghislain MARY's avatar
Ghislain MARY committed
472
	uint32_t encryptedContentTypeLength;
johan's avatar
johan committed
473 474 475
	limeURIKeys_t associatedKeys;
	xmlDocPtr xmlOutputMessage;
	xmlNodePtr rootNode;
johan's avatar
johan committed
476
	int i,ret;
johan's avatar
johan committed
477
	int xmlStringLength;
478
	xmlChar *local_output = NULL;
johan's avatar
johan committed
479

480 481
	/* retrieve selfZIDHex from cache */
	if (bzrtp_getSelfZID(cachedb, selfURI, selfZid, NULL) != 0) {
482 483 484 485
		return LIME_UNABLE_TO_ENCRYPT_MESSAGE;
	}

	/* encrypted message length is plaintext + 16 for tag */
486
	encryptedMessageLength = (uint32_t)strlen((char *)message) + 16;
Ghislain MARY's avatar
Ghislain MARY committed
487
	encryptedContentTypeLength = (uint32_t)strlen((char *)contentType) + 16;
488 489

	/* retrieve keys associated to the peer URI */
490 491
	associatedKeys.peerURI = bctbx_strdup(peerURI);
	associatedKeys.selfURI = bctbx_strdup(selfURI);
492 493 494
	associatedKeys.associatedZIDNumber  = 0;
	associatedKeys.peerKeys = NULL;

495
	if ((ret = lime_getCachedSndKeysByURI(cachedb, &associatedKeys)) != 0) {
496
		lime_freeKeys(&associatedKeys);
johan's avatar
johan committed
497
		return ret;
498 499 500
	}

	/* create an xml doc to hold the multipart message */
johan's avatar
johan committed
501
	xmlOutputMessage = xmlNewDoc((const xmlChar *)"1.0");
502
	/* root tag is "doc" */
johan's avatar
johan committed
503
	rootNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"doc", NULL);
504
	xmlDocSetRootElement(xmlOutputMessage, rootNode);
505
	/* add the self ZID child, convert it to an hexa string  */
506
	bctbx_int8_to_str(selfZidHex, selfZid, 12);
507
	selfZidHex[24] = '\0'; /* add a NULL termination for libxml */
508 509 510 511
	xmlNewTextChild(rootNode, NULL, (const xmlChar *)"ZID", selfZidHex);

	/* loop on all keys found */
	for (i=0; i<associatedKeys.associatedZIDNumber; i++) {
johan's avatar
johan committed
512 513 514
		uint8_t peerZidHex[25];
		uint8_t sessionIndexHex[9];
		xmlNodePtr msgNode;
johan's avatar
johan committed
515 516
		size_t b64Size = 0;
		unsigned char *encryptedMessageb64;
Ghislain MARY's avatar
Ghislain MARY committed
517
		unsigned char *encryptedContentTypeb64;
johan's avatar
johan committed
518

519 520 521
		/* encrypt message with current key */
		limeKey_t *currentKey = associatedKeys.peerKeys[i];
		/* encrypted message include a 16 bytes tag */
522
		uint8_t *encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
Ghislain MARY's avatar
Ghislain MARY committed
523
		uint8_t *encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
524
		lime_encryptMessage(currentKey, message, (uint32_t)strlen((char *)message), selfZid, encryptedMessage);
Ghislain MARY's avatar
Ghislain MARY committed
525
		lime_encryptMessage(currentKey, (const uint8_t *)contentType, (uint32_t)strlen((char *)contentType), selfZid, encryptedContentType);
526 527 528 529 530
		/* add a "msg" node the the output message, doc node is :
		 * <msg>
		 * 		<pzid>peerZID</pzid>
		 * 		<index>session index</index>
		 * 		<text>ciphertext</text>
Ghislain MARY's avatar
Ghislain MARY committed
531
		 * 		<content-type>ciphertext</content-type>
532
		 * </msg> */
johan's avatar
johan committed
533
		msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
534
		bctbx_int8_to_str(peerZidHex, currentKey->peerZID, 12);
535
		peerZidHex[24] = '\0';
536
		bctbx_uint32_to_str(sessionIndexHex, currentKey->sessionIndex);
537 538 539 540 541

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

		/* convert the cipherText to base 64 */
jehan's avatar
jehan committed
542
		bctbx_base64_encode(NULL, &b64Size, encryptedMessage, encryptedMessageLength); /* b64Size is 0, so it is set to the requested output buffer size */
543
		encryptedMessageb64 = reinterpret_cast<unsigned char *>(ms_malloc(b64Size+1)); /* allocate a buffer of requested size +1 for NULL termination */
jehan's avatar
jehan committed
544
		bctbx_base64_encode(encryptedMessageb64, &b64Size, encryptedMessage, encryptedMessageLength); /* b64Size is 0, so it is set to the requested output buffer size */
545 546
		encryptedMessageb64[b64Size] = '\0'; /* libxml need a null terminated string */
		xmlNewTextChild(msgNode, NULL, (const xmlChar *)"text", (const xmlChar *)encryptedMessageb64);
547 548
		ms_free(encryptedMessage);
		ms_free(encryptedMessageb64);
549

Ghislain MARY's avatar
Ghislain MARY committed
550 551 552
		/* convert the encrypted content-type to base 64 */
		b64Size = 0;
		bctbx_base64_encode(NULL, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
553
		encryptedContentTypeb64 = reinterpret_cast<unsigned char *>(ms_malloc(b64Size+1)); /* allocate a buffer of requested size +1 for NULL termination */
Ghislain MARY's avatar
Ghislain MARY committed
554 555 556 557 558 559
		bctbx_base64_encode(encryptedContentTypeb64, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
		encryptedContentTypeb64[b64Size] = '\0'; /* libxml need a null terminated string */
		xmlNewTextChild(msgNode, NULL, (const xmlChar *)"content-type", (const xmlChar *)encryptedContentTypeb64);
		ms_free(encryptedContentType);
		ms_free(encryptedContentTypeb64);

560 561
		/* add the message Node into the doc */
		xmlAddChild(rootNode, msgNode);
562

563 564
		/* update the key used */
		lime_deriveKey(currentKey);
565
		lime_setCachedKey(cachedb, currentKey, LIME_SENDER, 0); /* never update validity when sending a message */
566 567 568
	}

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

Benjamin REIS's avatar
Benjamin REIS committed
571 572
	*output = (uint8_t *)ms_malloc((size_t)xmlStringLength + 1);
	memcpy(*output, local_output, (size_t)xmlStringLength);
573
	(*output)[xmlStringLength] = '\0';
574

575 576
	xmlFree(local_output);
	xmlFreeDoc(xmlOutputMessage);
577
	lime_freeKeys(&associatedKeys);
578 579 580 581

	return 0;
}

582
int lime_decryptMultipartMessage(void *cachedb, uint8_t *message, const char *selfURI, const char *peerURI, uint8_t **output, char **content_type, uint64_t validityTimeSpan) {
Ghislain MARY's avatar
Ghislain MARY committed
583
	int retval = 0;
johan's avatar
johan committed
584 585
	uint8_t selfZidHex[25];
	uint8_t selfZid[12]; /* same data but in byte buffer */
Ghislain MARY's avatar
Ghislain MARY committed
586
	char xpath_str[MAX_XPATH_LENGTH];
johan's avatar
johan committed
587
	limeKey_t associatedKey;
Simon Morlat's avatar
Simon Morlat committed
588
	char *peerZidHex = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
589
	char *sessionIndexHex = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
590 591
	xmlparsing_context_t *xml_ctx;
	xmlXPathObjectPtr msg_object;
johan's avatar
johan committed
592
	uint8_t *encryptedMessage = NULL;
johan's avatar
johan committed
593
	size_t encryptedMessageLength = 0;
Ghislain MARY's avatar
Ghislain MARY committed
594 595
	uint8_t *encryptedContentType = NULL;
	size_t encryptedContentTypeLength = 0;
johan's avatar
johan committed
596
	uint32_t usedSessionIndex = 0;
Ghislain MARY's avatar
Ghislain MARY committed
597
	int i;
598

599
	if (cachedb == NULL) {
600 601
		return LIME_INVALID_CACHE;
	}
602 603 604

	/* retrieve selfZID from cache, and convert it to an Hexa buffer to easily match it against hex string containg in xml message as pzid */
	if (bzrtp_getSelfZID(cachedb, selfURI, selfZid, NULL) != 0) {
605
		ms_error("[LIME] Couldn't get self ZID");
606 607
		return LIME_UNABLE_TO_DECRYPT_MESSAGE;
	}
608
	bctbx_int8_to_str(selfZidHex, selfZid, 12);
609
	selfZidHex[24]='\0';
610

Ghislain MARY's avatar
Ghislain MARY committed
611 612 613 614
	xml_ctx = linphone_xmlparsing_context_new();
	xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
	xml_ctx->doc = xmlReadDoc((const unsigned char*)message, 0, NULL, 0);
	if (xml_ctx->doc == NULL) {
615
		ms_error("[LIME] XML doc is null");
Ghislain MARY's avatar
Ghislain MARY committed
616 617
		retval = LIME_INVALID_ENCRYPTED_MESSAGE;
		goto error;
618
	}
619

Ghislain MARY's avatar
Ghislain MARY committed
620
	if (linphone_create_xml_xpath_context(xml_ctx) < 0) {
621
		ms_error("[LIME] Couldn't create xml xpath context");
Ghislain MARY's avatar
Ghislain MARY committed
622 623
		retval = LIME_INVALID_ENCRYPTED_MESSAGE;
		goto error;
624 625
	}

Ghislain MARY's avatar
Ghislain MARY committed
626 627
	/* Retrieve the sender ZID */
	peerZidHex = linphone_get_xml_text_content(xml_ctx, "/doc/ZID");
628
	if (peerZidHex != NULL) {
Ghislain MARY's avatar
Ghislain MARY committed
629
		/* Convert it from hexa string to bytes string and set the result in the associatedKey structure */
630
		bctbx_str_to_uint8(associatedKey.peerZID, (const uint8_t *)peerZidHex, (uint16_t)strlen(peerZidHex));
Ghislain MARY's avatar
Ghislain MARY committed
631
		linphone_free_xml_text_content(peerZidHex);
632

Ghislain MARY's avatar
Ghislain MARY committed
633
		/* Get the matching key from cache */
634
		retval = lime_getCachedRcvKeyByZid(cachedb, &associatedKey, selfURI, peerURI);
635
		if (retval != 0) {
636
		ms_error("[LIME] Couldn't get cache rcv key by ZID");
Ghislain MARY's avatar
Ghislain MARY committed
637
			goto error;
638
		}
639

640
		/* Retrieve the portion of message which is encrypted with our key(seek for a pzid matching our) */
Ghislain MARY's avatar
Ghislain MARY committed
641 642 643
		msg_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/doc/msg");
		if ((msg_object != NULL) && (msg_object->nodesetval != NULL)) {
			for (i = 1; i <= msg_object->nodesetval->nodeNr; i++) {
Simon Morlat's avatar
Simon Morlat committed
644
				char *currentZidHex;
645

Simon Morlat's avatar
Simon Morlat committed
646 647
				char *encryptedMessageb64;
				char *encryptedContentTypeb64;
Ghislain MARY's avatar
Ghislain MARY committed
648 649 650
				snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/pzid", i);
				currentZidHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
				if ((currentZidHex != NULL) && (strcmp(currentZidHex, (char *)selfZidHex) == 0)) {
Simon Morlat's avatar
Simon Morlat committed
651
					linphone_free_xml_text_content(currentZidHex);
Ghislain MARY's avatar
Ghislain MARY committed
652 653 654 655
					/* We found the msg node we are looking for */
					snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/index", i);
					sessionIndexHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
					if (sessionIndexHex != NULL) {
656
						usedSessionIndex = bctbx_str_to_uint32((const unsigned char *)sessionIndexHex);
Ghislain MARY's avatar
Ghislain MARY committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
					}
					snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/text", i);
					encryptedMessageb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
					if (encryptedMessageb64 != NULL) {
						bctbx_base64_decode(NULL, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64)); /* encryptedMessageLength is 0, so it will be set to the requested buffer length */
						encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
						bctbx_base64_decode(encryptedMessage, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64));
						linphone_free_xml_text_content(encryptedMessageb64);
					}
					snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/content-type", i);
					encryptedContentTypeb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
					if (encryptedContentTypeb64 != NULL) {
						bctbx_base64_decode(NULL, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64)); /* encryptedContentTypeLength is 0, so it will be set to the requested buffer length */
						encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
						bctbx_base64_decode(encryptedContentType, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64));
						linphone_free_xml_text_content(encryptedContentTypeb64);
					}
					break;
				}
676
			}
Ghislain MARY's avatar
Ghislain MARY committed
677
		}
Simon Morlat's avatar
Simon Morlat committed
678
		if (msg_object != NULL) xmlXPathFreeObject(msg_object);
679 680 681 682
	}

	/* do we have retrieved correctly all the needed data */
	if (encryptedMessage == NULL) {
683
		ms_error("[LIME] Encrypted message is null, something went wrong...");
684 685
		retval = LIME_UNABLE_TO_DECRYPT_MESSAGE;
		goto error;
686 687 688
	}

	/* shall we derive our key before going for decryption */
689
	if (usedSessionIndex < associatedKey.sessionIndex) {
690 691 692
		/* something wen't wrong with the cache, this shall never happen */
		uint8_t associatedKeyIndexHex[9];
		bctbx_uint32_to_str(associatedKeyIndexHex, associatedKey.sessionIndex);
693
		ms_error("[LIME] Session index [%s] < associated key's session index [%s], should not happen !", sessionIndexHex, associatedKeyIndexHex);
694
		ms_free(encryptedMessage);
695 696
		retval = LIME_UNABLE_TO_DECRYPT_MESSAGE;
		goto error;
697 698 699 700
	}

	if ((usedSessionIndex - associatedKey.sessionIndex > MAX_DERIVATION_NUMBER) ) {
		/* we missed to many messages, ask for a cache reset via a ZRTP call */
701
		ms_error("[LIME] Too many messages missed (%i), cache should be reset by ZRTP call", usedSessionIndex - associatedKey.sessionIndex);
702
		ms_free(encryptedMessage);
703 704
		retval = LIME_UNABLE_TO_DECRYPT_MESSAGE;
		goto error;
705 706
	}

707
	if (associatedKey.sessionIndex != usedSessionIndex) {
708 709
		uint8_t associatedKeyIndexHex[9];
		bctbx_uint32_to_str(associatedKeyIndexHex, associatedKey.sessionIndex);
710
		ms_warning("LIME] unexpected session index [%s] received from [%s] (expected [%s]), [%i] messages will be discarded",
711
			sessionIndexHex, peerURI, associatedKeyIndexHex, usedSessionIndex-associatedKey.sessionIndex);
712
	}
713

714 715
	while (usedSessionIndex>associatedKey.sessionIndex) {
		lime_deriveKey(&associatedKey);
716 717
	}

Ghislain MARY's avatar
Ghislain MARY committed
718 719
	/* Decrypt the message */
	*output = (uint8_t *)ms_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 */
720
	retval = lime_decryptMessage(&associatedKey, encryptedMessage, (uint32_t)encryptedMessageLength, selfZid, *output);
721 722 723
	ms_free(encryptedMessage);
	if (retval != 0) {
		ms_free(*output);
724
		*output = NULL;
725
		ms_error("[LIME] Couldn't decrypt message");
726 727
		retval = LIME_UNABLE_TO_DECRYPT_MESSAGE;
		goto error;
728 729
	}

Ghislain MARY's avatar
Ghislain MARY committed
730 731 732 733 734 735 736 737
	/* Decrypt the content-type */
	if (encryptedContentType != NULL) {
		*content_type = (char *)ms_malloc(encryptedContentTypeLength - 16 + 1); /* content-type is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
		retval = lime_decryptMessage(&associatedKey, encryptedContentType, (uint32_t)encryptedContentTypeLength, selfZid, *((uint8_t **)content_type));
		ms_free(encryptedContentType);
		if (retval != 0) {
			ms_free(*content_type);
			*content_type = NULL;
738
			ms_error("[LIME] Couldn't decrypt content type");
739 740
			retval = LIME_UNABLE_TO_DECRYPT_MESSAGE;
			goto error;
Ghislain MARY's avatar
Ghislain MARY committed
741 742 743
		}
	}

744 745
	/* update used key */
	lime_deriveKey(&associatedKey);
746
	lime_setCachedKey(cachedb, &associatedKey, LIME_RECEIVER, validityTimeSpan);
747

Ghislain MARY's avatar
Ghislain MARY committed
748
error:
749 750 751
	if (sessionIndexHex != NULL) {
		linphone_free_xml_text_content(sessionIndexHex);
	}
Ghislain MARY's avatar
Ghislain MARY committed
752 753
	linphone_xmlparsing_context_destroy(xml_ctx);
	return retval;
754
}
755

756 757
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
	if (cr) {
Ghislain MARY's avatar
Ghislain MARY committed
758
		switch (linphone_core_lime_enabled(linphone_chat_room_get_core(cr))) {
759
			case LinphoneLimeDisabled: return FALSE;
760
			case LinphoneLimeMandatory:
761
			case LinphoneLimePreferred: {
Ghislain MARY's avatar
Ghislain MARY committed
762
				void *zrtp_cache_db = linphone_core_get_zrtp_cache_db(linphone_chat_room_get_core(cr));
763 764 765 766
				if (zrtp_cache_db != NULL) {
					bool_t res;
					limeURIKeys_t associatedKeys;
					char *peer = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
767

768 769 770 771 772 773 774 775 776
					/* retrieve keys associated to the peer URI */
					associatedKeys.peerURI = bctbx_strdup(peer);
					associatedKeys.selfURI = NULL; /* TODO : there is no sender associated to chatroom so check for any local URI available, shall we add sender to chatroom? */
					associatedKeys.associatedZIDNumber  = 0;
					associatedKeys.peerKeys = NULL;
					/* with NULL is selfURI, just retrieve keys for any local uri found in cache, shall we use a dedicated function which would
					return the list of possible uris and store the selected one in the chatroom ? */
					res = (lime_getCachedSndKeysByURI(zrtp_cache_db, &associatedKeys) == 0);
					lime_freeKeys(&associatedKeys);
Simon Morlat's avatar
Simon Morlat committed
777
					ms_free(peer);
778
					return res;
779 780 781 782 783
				}
			}
		}
	}
	return FALSE;
784 785
}

786 787
int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
	LinphoneCore *lc = linphone_im_encryption_engine_get_core(engine);
788 789
	int errcode = -1;
	/* check if we have a xml/cipher message to be decrypted */
790 791 792
	if (linphone_chat_message_get_content_type(msg) && 
		(strcmp("xml/cipher", linphone_chat_message_get_content_type(msg)) == 0 || 
		strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", linphone_chat_message_get_content_type(msg)) == 0)) {
793
		errcode = 0;
794 795 796 797 798 799 800 801 802 803
		int retval;
		void *zrtp_cache_db = NULL; /* use a void * instead of sqlite3 * to avoid problems and ifdef when SQLITE is not available(the get function shall return NULL in that case) */
		uint8_t *decrypted_body = NULL;
		char *decrypted_content_type = NULL;
		char *peerUri = NULL;
		char *selfUri = NULL;

		zrtp_cache_db = linphone_core_get_zrtp_cache_db(lc);
		if (zrtp_cache_db == NULL) {
			ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
804 805
			errcode = 500;
			return errcode;
806
		}
807 808 809
		peerUri = linphone_address_as_string_uri_only(linphone_chat_message_get_from_address(msg));
		selfUri = linphone_address_as_string_uri_only(linphone_chat_message_get_to_address(msg));
		retval = lime_decryptMultipartMessage(zrtp_cache_db, (uint8_t *)linphone_chat_message_get_text(msg), selfUri, peerUri, &decrypted_body, &decrypted_content_type,
Simon Morlat's avatar
Simon Morlat committed
810 811 812
						      bctbx_time_string_to_sec(lp_config_get_string(lc->config, "sip", "lime_key_validity", "0")));
		ms_free(peerUri);
		ms_free(selfUri);
813 814 815 816 817
		if (retval != 0) {
			ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval));
			if (decrypted_body) ms_free(decrypted_body);
			errcode = 488;
			return errcode;
818
		} else {
819
			/* swap encrypted message with plain text message */
820
			linphone_chat_message_set_text(msg, (char *)decrypted_body);
Ghislain MARY's avatar
Ghislain MARY committed
821
			ms_free(decrypted_body);
822 823
			if (decrypted_content_type != NULL) {
				linphone_chat_message_set_content_type(msg, decrypted_content_type);
Simon Morlat's avatar
Simon Morlat committed
824
				ms_free(decrypted_content_type);
825
			} else {
826
				if (strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", linphone_chat_message_get_content_type(msg)) == 0) {
827
					linphone_chat_message_set_content_type(msg, "application/vnd.gsma.rcs-ft-http+xml");
828
				} else {
829
					linphone_chat_message_set_content_type(msg, "text/plain");
830
				}
831 832 833 834 835 836
			}
		}
	}
	return errcode;
}

837 838
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
	LinphoneCore *lc = linphone_im_encryption_engine_get_core(engine);
839
	int errcode = -1;
840
	const char *new_content_type = "xml/cipher";
Ghislain MARY's avatar
Ghislain MARY committed
841
	if(linphone_core_lime_enabled(linphone_chat_room_get_core(room))) {
842
		if (linphone_chat_room_lime_available(room)) {
843
			void *zrtp_cache_db = NULL; /* use a void * instead of sqlite3 * to avoid problems and ifdef when SQLITE is not available(the get function shall return NULL in that case) */
844 845
			if (linphone_chat_message_get_content_type(msg)) {
				if (strcmp(linphone_chat_message_get_content_type(msg), "application/vnd.gsma.rcs-ft-http+xml") == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
846 847 848 849 850
					/* It's a file transfer, content type shall be set to application/cipher.vnd.gsma.rcs-ft-http+xml
					   TODO: As of january 2017, the content type is now included in the encrypted body, this
					   application/cipher.vnd.gsma.rcs-ft-http+xml is kept for compatibility with previous versions,
					   but may be dropped in the future to use xml/cipher instead. */
					new_content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
851
				} else if (strcmp(linphone_chat_message_get_content_type(msg), "application/im-iscomposing+xml") == 0) {
852 853 854
					/* We don't encrypt composing messages */
					return errcode;
				}
855
			}
Ghislain MARY's avatar
Ghislain MARY committed
856

857
			/* access the zrtp cache to get keys needed to cipher the message */
858
			zrtp_cache_db = linphone_core_get_zrtp_cache_db(lc);
859
			errcode = 0;
860
			if (zrtp_cache_db == NULL) {
861
				ms_warning("Unable to access ZRTP ZID cache to encrypt message");
862 863
				errcode = 488;
			} else {
864 865
				int retval;
				uint8_t *crypted_body = NULL;
866
				char *selfUri = linphone_address_as_string_uri_only(linphone_chat_message_get_from_address(msg));
867
				char *peerUri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(room));
868

869
				retval = lime_createMultipartMessage(zrtp_cache_db, linphone_chat_message_get_content_type(msg), (uint8_t *)linphone_chat_message_get_text(msg), selfUri, peerUri, &crypted_body);
870
				if (retval != 0) { /* fail to encrypt */
Ghislain MARY's avatar
Ghislain MARY committed
871
					ms_warning("Unable to encrypt message for %s : %s", peerUri, lime_error_code_to_string(retval));
872 873
					if (crypted_body) ms_free(crypted_body);
					errcode = 488;
874
				} else { /* encryption ok, swap plain text message body by encrypted one */
875
					linphone_chat_message_set_text(msg, (char *)crypted_body);
Ghislain MARY's avatar
Ghislain MARY committed
876
					ms_free(crypted_body);
Simon Morlat's avatar
Simon Morlat committed
877
					linphone_chat_message_set_content_type(msg, new_content_type);
878
				}
879 880
				ms_free(peerUri);
				ms_free(selfUri);
881
			}
882
		} else {
883 884 885 886
			if (linphone_core_lime_enabled(lc) == LinphoneLimeMandatory) {
				ms_warning("Unable to access ZRTP ZID cache to encrypt message");
				errcode = 488;
			}
887 888 889 890
		}
	}
	return errcode;
}
891

892
int lime_im_encryption_engine_process_downloading_file_cb(LinphoneImEncryptionEngine *engine, LinphoneChatMessage *msg, size_t offset, const uint8_t *buffer, size_t size, uint8_t *decrypted_buffer) {
893 894 895 896 897 898 899
	LinphoneContent *content = linphone_chat_message_get_file_transfer_information(msg);
	if (!content)
		return -1;
	if (!linphone_content_get_key(content)) {
		linphone_content_unref(content);
		return -1;
	}
900

901 902 903 904
	if (!buffer || (size == 0)) {
		int result = lime_decryptFile(linphone_content_get_cryptoContext_address(content), NULL, 0, NULL, NULL);
		linphone_content_unref(content);
		return result;
905
	}
906

907 908 909 910
	int result = lime_decryptFile(linphone_content_get_cryptoContext_address(content),
		(unsigned char *)linphone_content_get_key(content), size, (char *)decrypted_buffer, (char *)buffer);
	linphone_content_unref(content);
	return result;
911 912
}

913
int lime_im_encryption_engine_process_uploading_file_cb(LinphoneImEncryptionEngine *engine, LinphoneChatMessage *msg, size_t offset, const uint8_t *buffer, size_t *size, uint8_t *encrypted_buffer) {
914 915 916 917 918 919 920
	LinphoneContent *content = linphone_chat_message_get_file_transfer_information(msg);
	if (!content)
		return -1;
	if (!linphone_content_get_key(content)) {
		linphone_content_unref(content);
		return -1;
	}
921

922 923 924 925
	if (!buffer || (*size == 0)) {
		int result = lime_encryptFile(linphone_content_get_cryptoContext_address(content), NULL, 0, NULL, NULL);
		linphone_content_unref(content);
		return result;
926
	}
927

928
	size_t file_size = linphone_content_get_size(content);
929 930 931
	if (file_size == 0) {
		ms_warning("File size has not been set, encryption will fail if not done in one step (if file is larger than 16K)");
	} else if (offset + *size < file_size) {
932 933
		*size -= (*size % 16);
	}
934

935 936 937 938
	int result = lime_encryptFile(linphone_content_get_cryptoContext_address(content),
		(unsigned char *)linphone_content_get_key(content), *size, (char *)buffer, (char *)encrypted_buffer);
	linphone_content_unref(content);
	return result;
939 940
}

941 942
bool_t lime_im_encryption_engine_is_file_encryption_enabled_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room) {
	LinphoneCore *lc = linphone_im_encryption_engine_get_core(engine);
943 944 945
	return linphone_chat_room_lime_available(room) && linphone_core_lime_for_file_sharing_enabled(lc);
}

946
void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
947 948 949 950
	char keyBuffer [FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of initial vector */
	/* generate a random 192 bits key + 64 bits of initial vector and store it into the
		* file_transfer_information->key field of the msg */
	sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
951 952 953 954 955
	LinphoneContent *content = linphone_chat_message_get_file_transfer_information(msg);
	if (!content)
		return;
	linphone_content_set_key(content, keyBuffer, FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
	linphone_content_unref(content);
956 957
}

958 959 960 961
#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;}
962 963
int lime_decryptMultipartMessage(void *cachedb, uint8_t *message, const char *selfURI, const char *peerURI, uint8_t **output, char **content_type, uint64_t validityTimeSpan) { return LIME_NOT_ENABLED;}
int lime_createMultipartMessage(void *cachedb, const char *contentType, uint8_t *message, const char *selfURI, const char *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
964
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {return LIME_NOT_ENABLED;}
965
void lime_freeKeys(limeURIKeys_t *associatedKeys){
966
}
967
int lime_getCachedSndKeysByURI(void *cachedb, limeURIKeys_t *associatedKeys){
968 969
	return LIME_NOT_ENABLED;
}
Ghislain MARY's avatar
Ghislain MARY committed
970
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
jehan's avatar
jehan committed
971 972
	return LIME_NOT_ENABLED;
}
973
int lime_setCachedKey(void * cacheDb, limeKey_t *associatedKey, uint8_t role, uint64_t validityTimeSpan) {
jehan's avatar
jehan committed
974 975
	return LIME_NOT_ENABLED;
}
976
int lime_getCachedRcvKeyByZid(void * cacheDb, limeKey_t *associatedKey, const char *selfURI, const char *peerURI) {
jehan's avatar
jehan committed
977 978 979 980 981
	return LIME_NOT_ENABLED;
}
int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) {
	return LIME_NOT_ENABLED;
}
982 983 984
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
	return FALSE;
}
985
int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
986 987
	return 500;
}
988
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
989 990
	return 500;
}
991
int lime_im_encryption_engine_process_downloading_file_cb(LinphoneImEncryptionEngine *engine, LinphoneChatMessage *msg, size_t offset, const uint8_t *buffer, size_t size, uint8_t *decrypted_buffer) {
992 993
	return 500;
}
994
int lime_im_encryption_engine_process_uploading_file_cb(LinphoneImEncryptionEngine *engine, LinphoneChatMessage *msg, size_t offset, const uint8_t *buffer, size_t *size, uint8_t *encrypted_buffer) {
995
	return 500;
996
}
997
bool_t lime_im_encryption_engine_is_file_encryption_enabled_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room) {
998 999
	return FALSE;
}
1000
void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
1001

1002
}
1003 1004
#endif /* HAVE_LIME */

1005
const char *lime_error_code_to_string(int errorCode) {
1006 1007 1008 1009 1010 1011
	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";
johan's avatar
johan committed
1012
		case LIME_PEER_KEY_HAS_EXPIRED: return "Any key matching peer Uri has expired";
1013
		case LIME_INVALID_ENCRYPTED_MESSAGE: return "Invalid encrypted message";
1014
		case LIME_NOT_ENABLED: return "Lime not enabled at build";
1015 1016 1017 1018
	}
	return "Unknow error";

}