changes from RCP console
[public/netxms.git] / src / libnetxms / crypto.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** NetXMS Foundation Library
7aad6641 4** Copyright (C) 2003-2013 Victor Kirhenshtein
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
68f384ea
VK
7** it under the terms of the GNU Lesser General Public License as published
8** by the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
68f384ea 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: crypto.cpp
21**
22**/
23
24#include "libnetxms.h"
4e2debcc 25#include "ice.h"
5039dede 26
b1e9b6b3
VK
27/**
28 * Constants
29 */
5039dede
AK
30#define KEY_BUFFER_SIZE 4096
31
b1e9b6b3
VK
32/**
33 * Supported ciphers. By default, we support all ciphers compiled
34 * into OpenSSL library.
35 */
5039dede
AK
36static DWORD m_dwSupportedCiphers =
37#ifdef _WITH_ENCRYPTION
38#ifndef OPENSSL_NO_AES
39 CSCP_SUPPORT_AES_256 |
9e9d631e 40 CSCP_SUPPORT_AES_128 |
5039dede
AK
41#endif
42#ifndef OPENSSL_NO_BF
43 CSCP_SUPPORT_BLOWFISH |
44#endif
45#ifndef OPENSSL_NO_IDEA
46 CSCP_SUPPORT_IDEA |
47#endif
48#ifndef OPENSSL_NO_DES
49 CSCP_SUPPORT_3DES |
50#endif
51#endif /* _WITH_ENCRYPTION */
52 0;
53
b1e9b6b3
VK
54/**
55 * Static data
56 */
5039dede
AK
57#ifdef _WITH_ENCRYPTION
58
9e9d631e 59typedef OPENSSL_CONST EVP_CIPHER * (*CIPHER_FUNC)();
5039dede
AK
60static CIPHER_FUNC m_pfCipherList[NETXMS_MAX_CIPHERS] =
61{
62#ifndef OPENSSL_NO_AES
63 EVP_aes_256_cbc,
64#else
65 NULL,
66#endif
67#ifndef OPENSSL_NO_BF
68 EVP_bf_cbc,
69#else
70 NULL,
71#endif
72#ifndef OPENSSL_NO_IDEA
73 EVP_idea_cbc,
74#else
75 NULL,
76#endif
77#ifndef OPENSSL_NO_DES
9e9d631e
VK
78 EVP_des_ede3_cbc,
79#else
80 NULL,
81#endif
82#ifndef OPENSSL_NO_AES
83 EVP_aes_128_cbc
5039dede
AK
84#else
85 NULL
86#endif
87};
88static WORD m_wNoEncryptionFlag = 0;
89static MUTEX *m_pCryptoMutexList = NULL;
90
b1e9b6b3
VK
91/**
92 * Locking callback for CRYPTO library
93 */
5039dede
AK
94static void CryptoLockingCallback(int nMode, int nLock, const char *pszFile, int nLine)
95{
96 if (nMode & CRYPTO_LOCK)
c17f6cbc 97 MutexLock(m_pCryptoMutexList[nLock]);
5039dede
AK
98 else
99 MutexUnlock(m_pCryptoMutexList[nLock]);
100}
101
b1e9b6b3
VK
102/**
103 * ID callback for CRYPTO library
104 */
5039dede
AK
105#ifndef _WIN32
106
9e9d631e 107static unsigned long CryptoIdCallback()
5039dede
AK
108{
109 return (unsigned long)GetCurrentThreadId();
110}
111
112#endif
113
114#endif /* _WITH_ENCRYPTION */
115
b1e9b6b3
VK
116/**
117 * Initialize OpenSSL library
118 */
5039dede
AK
119BOOL LIBNETXMS_EXPORTABLE InitCryptoLib(DWORD dwEnabledCiphers)
120{
121#ifdef _WITH_ENCRYPTION
122 BYTE random[8192];
123 int i;
124
125 CRYPTO_malloc_init();
126 ERR_load_CRYPTO_strings();
127 OpenSSL_add_all_algorithms();
128 RAND_seed(random, 8192);
129 m_dwSupportedCiphers &= dwEnabledCiphers;
130 m_wNoEncryptionFlag = htons(MF_DONT_ENCRYPT);
131 m_pCryptoMutexList = (MUTEX *)malloc(sizeof(MUTEX) * CRYPTO_num_locks());
132 for(i = 0; i < CRYPTO_num_locks(); i++)
133 m_pCryptoMutexList[i] = MutexCreate();
134 CRYPTO_set_locking_callback(CryptoLockingCallback);
135#ifndef _WIN32
136 CRYPTO_set_id_callback(CryptoIdCallback);
137#endif /* _WIN32 */
138#endif /* _WITH_ENCRYPTION */
139 return TRUE;
140}
141
b1e9b6b3
VK
142/**
143 * Get supported ciphers
144 */
9e9d631e 145DWORD LIBNETXMS_EXPORTABLE CSCPGetSupportedCiphers()
5039dede
AK
146{
147 return m_dwSupportedCiphers;
148}
149
b1e9b6b3
VK
150/**
151 * Encrypt message
152 */
98abc9f1 153CSCP_ENCRYPTED_MESSAGE LIBNETXMS_EXPORTABLE *CSCPEncryptMessage(NXCPEncryptionContext *pCtx, CSCP_MESSAGE *pMsg)
5039dede
AK
154{
155#ifdef _WITH_ENCRYPTION
156 CSCP_ENCRYPTED_MESSAGE *pEnMsg;
157 CSCP_ENCRYPTED_PAYLOAD_HEADER header;
158 int nSize;
159 EVP_CIPHER_CTX cipher;
160 DWORD dwMsgSize;
161
162 if (pMsg->wFlags & m_wNoEncryptionFlag)
163 return (CSCP_ENCRYPTED_MESSAGE *)nx_memdup(pMsg, ntohl(pMsg->dwSize));
164
98abc9f1 165 if (m_pfCipherList[pCtx->getCipher()] == NULL)
5039dede
AK
166 return NULL; // Unsupported cipher
167
98abc9f1
VK
168 EVP_EncryptInit(&cipher, m_pfCipherList[pCtx->getCipher()](), pCtx->getSessionKey(), pCtx->getIV());
169 EVP_CIPHER_CTX_set_key_length(&cipher, pCtx->getKeyLength());
5039dede
AK
170
171 dwMsgSize = ntohl(pMsg->dwSize);
172 pEnMsg = (CSCP_ENCRYPTED_MESSAGE *)malloc(dwMsgSize + CSCP_ENCRYPTION_HEADER_SIZE +
173 EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(&cipher)) + 8);
174 pEnMsg->wCode = htons(CMD_ENCRYPTED_MESSAGE);
175 pEnMsg->nReserved = 0;
176
177 header.dwChecksum = htonl(CalculateCRC32((BYTE *)pMsg, dwMsgSize, 0));
178 header.dwReserved = 0;
179 EVP_EncryptUpdate(&cipher, pEnMsg->data, &nSize, (BYTE *)&header, CSCP_EH_ENCRYPTED_BYTES);
180 dwMsgSize = nSize;
181 EVP_EncryptUpdate(&cipher, pEnMsg->data + dwMsgSize, &nSize, (BYTE *)pMsg, ntohl(pMsg->dwSize));
182 dwMsgSize += nSize;
183 EVP_EncryptFinal(&cipher, pEnMsg->data + dwMsgSize, &nSize);
184 dwMsgSize += nSize + CSCP_EH_UNENCRYPTED_BYTES;
185 EVP_CIPHER_CTX_cleanup(&cipher);
186
187 if (dwMsgSize % 8 != 0)
188 {
189 pEnMsg->nPadding = (BYTE)(8 - (dwMsgSize % 8));
190 dwMsgSize += pEnMsg->nPadding;
191 }
192 else
193 {
194 pEnMsg->nPadding = 0;
195 }
196 pEnMsg->dwSize = htonl(dwMsgSize);
197
198 return pEnMsg;
199#else /* _WITH_ENCRYPTION */
200 return NULL;
201#endif
202}
203
7aad6641
VK
204/**
205 * Decrypt message
206 */
98abc9f1 207BOOL LIBNETXMS_EXPORTABLE CSCPDecryptMessage(NXCPEncryptionContext *pCtx,
5039dede
AK
208 CSCP_ENCRYPTED_MESSAGE *pMsg,
209 BYTE *pDecryptionBuffer)
210{
211#ifdef _WITH_ENCRYPTION
212 int nSize;
213 EVP_CIPHER_CTX cipher;
214 DWORD dwChecksum, dwMsgSize;
215 CSCP_MESSAGE *pClearMsg;
216
98abc9f1 217 if (m_pfCipherList[pCtx->getCipher()] == NULL)
5039dede
AK
218 return FALSE; // Unsupported cipher
219
220 pMsg->dwSize = ntohl(pMsg->dwSize);
98abc9f1
VK
221 EVP_DecryptInit(&cipher, m_pfCipherList[pCtx->getCipher()](), pCtx->getSessionKey(), pCtx->getIV());
222 EVP_CIPHER_CTX_set_key_length(&cipher, pCtx->getKeyLength());
5039dede
AK
223 EVP_DecryptUpdate(&cipher, pDecryptionBuffer, &nSize, pMsg->data,
224 pMsg->dwSize - CSCP_EH_UNENCRYPTED_BYTES - pMsg->nPadding);
225 EVP_DecryptFinal(&cipher, pDecryptionBuffer + nSize, &nSize);
226 EVP_CIPHER_CTX_cleanup(&cipher);
227
228 pClearMsg = (CSCP_MESSAGE *)(pDecryptionBuffer + CSCP_EH_ENCRYPTED_BYTES);
229 dwMsgSize = ntohl(pClearMsg->dwSize);
230 if (dwMsgSize > pMsg->dwSize)
231 return FALSE; // Message decrypted incorrectly, because it can't be larger than encrypted
232 dwChecksum = CalculateCRC32((BYTE *)pClearMsg, dwMsgSize, 0);
233 if (dwChecksum != ntohl(((CSCP_ENCRYPTED_PAYLOAD_HEADER *)pDecryptionBuffer)->dwChecksum))
234 return FALSE; // Bad checksum
235
236 memcpy(pMsg, pClearMsg, dwMsgSize);
237 return TRUE;
238#else /* _WITH_ENCRYPTION */
239 return FALSE;
240#endif
241}
242
b1e9b6b3
VK
243/**
244 * Setup encryption context
245 * Function will determine is it called on initiator or responder side
246 * by message code. Initiator should provide it's private key,
247 * and responder should provide pointer to response message.
248 */
5039dede 249DWORD LIBNETXMS_EXPORTABLE SetupEncryptionContext(CSCPMessage *pMsg,
98abc9f1 250 NXCPEncryptionContext **ppCtx,
5039dede
AK
251 CSCPMessage **ppResponse,
252 RSA *pPrivateKey, int nNXCPVersion)
253{
254 DWORD dwResult = RCC_NOT_IMPLEMENTED;
255
98abc9f1 256 *ppCtx = NULL;
5039dede
AK
257#ifdef _WITH_ENCRYPTION
258 if (pMsg->GetCode() == CMD_REQUEST_SESSION_KEY)
259 {
260 DWORD dwCiphers;
261
262 *ppResponse = new CSCPMessage(nNXCPVersion);
263 (*ppResponse)->SetCode(CMD_SESSION_KEY);
264 (*ppResponse)->SetId(pMsg->GetId());
265 (*ppResponse)->DisableEncryption();
266
267 dwCiphers = pMsg->GetVariableLong(VID_SUPPORTED_ENCRYPTION) & m_dwSupportedCiphers;
268 if (dwCiphers == 0)
269 {
270 (*ppResponse)->SetVariable(VID_RCC, RCC_NO_CIPHERS);
271 dwResult = RCC_NO_CIPHERS;
272 }
273 else
274 {
98abc9f1
VK
275 BYTE *pBufPos, ucKeyBuffer[KEY_BUFFER_SIZE];
276 RSA *pServerKey;
277 DWORD dwKeySize;
5039dede 278
98abc9f1 279 *ppCtx = NXCPEncryptionContext::create(dwCiphers);
5039dede
AK
280
281 // Encrypt key
282 dwKeySize = pMsg->GetVariableBinary(VID_PUBLIC_KEY, ucKeyBuffer, KEY_BUFFER_SIZE);
283 pBufPos = ucKeyBuffer;
284 pServerKey = d2i_RSAPublicKey(NULL, (OPENSSL_CONST BYTE **)&pBufPos, dwKeySize);
285 if (pServerKey != NULL)
286 {
287 (*ppResponse)->SetVariable(VID_RCC, RCC_SUCCESS);
98abc9f1 288 dwKeySize = RSA_public_encrypt((*ppCtx)->getKeyLength(), (*ppCtx)->getSessionKey(),
5039dede
AK
289 ucKeyBuffer, pServerKey, RSA_PKCS1_OAEP_PADDING);
290 (*ppResponse)->SetVariable(VID_SESSION_KEY, ucKeyBuffer, dwKeySize);
98abc9f1 291 dwKeySize = RSA_public_encrypt(EVP_MAX_IV_LENGTH, (*ppCtx)->getIV(),
5039dede
AK
292 ucKeyBuffer, pServerKey, RSA_PKCS1_OAEP_PADDING);
293 (*ppResponse)->SetVariable(VID_SESSION_IV, ucKeyBuffer, dwKeySize);
98abc9f1
VK
294 (*ppResponse)->SetVariable(VID_CIPHER, (WORD)(*ppCtx)->getCipher());
295 (*ppResponse)->SetVariable(VID_KEY_LENGTH, (WORD)(*ppCtx)->getKeyLength());
5039dede
AK
296 (*ppResponse)->SetVariable(VID_IV_LENGTH, (WORD)EVP_MAX_IV_LENGTH);
297 RSA_free(pServerKey);
298 dwResult = RCC_SUCCESS;
299 }
300 else
301 {
302 (*ppResponse)->SetVariable(VID_RCC, RCC_INVALID_PUBLIC_KEY);
303 dwResult = RCC_INVALID_PUBLIC_KEY;
304 }
305 }
306 }
307 else if (pMsg->GetCode() == CMD_SESSION_KEY)
308 {
309 dwResult = pMsg->GetVariableLong(VID_RCC);
310 if (dwResult == RCC_SUCCESS)
311 {
98abc9f1
VK
312 *ppCtx = NXCPEncryptionContext::create(pMsg, pPrivateKey);
313 if (*ppCtx == NULL)
314 {
315 dwResult = RCC_INVALID_SESSION_KEY;
316 }
5039dede
AK
317 }
318 }
319
320 if ((dwResult != RCC_SUCCESS) && (*ppCtx != NULL))
321 {
98abc9f1 322 delete *ppCtx;
5039dede
AK
323 *ppCtx = NULL;
324 }
325#else
326 if (pMsg->GetCode() == CMD_REQUEST_SESSION_KEY)
327 {
328 *ppResponse = new CSCPMessage(nNXCPVersion);
329 (*ppResponse)->SetCode(CMD_SESSION_KEY);
330 (*ppResponse)->SetId(pMsg->GetId());
331 (*ppResponse)->DisableEncryption();
332 (*ppResponse)->SetVariable(VID_RCC, dwResult);
333 }
334#endif
335
336 return dwResult;
337}
338
b1e9b6b3
VK
339/**
340 * Prepare session key request message
341 */
9e9d631e 342void LIBNETXMS_EXPORTABLE PrepareKeyRequestMsg(CSCPMessage *pMsg, RSA *pServerKey, bool useX509Format)
5039dede
AK
343{
344#ifdef _WITH_ENCRYPTION
345 int iLen;
346 BYTE *pKeyBuffer, *pBufPos;
347
348 pMsg->SetCode(CMD_REQUEST_SESSION_KEY);
349 pMsg->SetVariable(VID_SUPPORTED_ENCRYPTION, m_dwSupportedCiphers);
350
9e9d631e
VK
351 if (useX509Format)
352 {
353 iLen = i2d_RSA_PUBKEY(pServerKey, NULL);
354 pKeyBuffer = (BYTE *)malloc(iLen);
355 pBufPos = pKeyBuffer;
356 i2d_RSA_PUBKEY(pServerKey, &pBufPos);
357 }
358 else
359 {
360 iLen = i2d_RSAPublicKey(pServerKey, NULL);
361 pKeyBuffer = (BYTE *)malloc(iLen);
362 pBufPos = pKeyBuffer;
363 i2d_RSAPublicKey(pServerKey, &pBufPos);
364 }
365 pMsg->SetVariable(VID_PUBLIC_KEY, pKeyBuffer, iLen);
5039dede
AK
366 free(pKeyBuffer);
367#endif
368}
369
7aad6641
VK
370/**
371 * Load RSA key(s) from file
372 */
5039dede
AK
373RSA LIBNETXMS_EXPORTABLE *LoadRSAKeys(const TCHAR *pszKeyFile)
374{
375#ifdef _WITH_ENCRYPTION
376 FILE *fp;
377 BYTE *pKeyBuffer, *pBufPos, hash[SHA1_DIGEST_SIZE];
378 DWORD dwLen;
379 RSA *pKey = NULL;
380
381 fp = _tfopen(pszKeyFile, _T("rb"));
382 if (fp != NULL)
383 {
6d738067 384 if (fread(&dwLen, 1, sizeof(DWORD), fp) == sizeof(DWORD) && dwLen < 10 * 1024)
5039dede
AK
385 {
386 pKeyBuffer = (BYTE *)malloc(dwLen);
387 pBufPos = pKeyBuffer;
388 if (fread(pKeyBuffer, 1, dwLen, fp) == dwLen)
389 {
390 BYTE hash2[SHA1_DIGEST_SIZE];
391
7aad6641 392 if (fread(hash, 1, SHA1_DIGEST_SIZE, fp) == SHA1_DIGEST_SIZE)
5039dede 393 {
7aad6641
VK
394 CalculateSHA1Hash(pKeyBuffer, dwLen, hash2);
395 if (!memcmp(hash, hash2, SHA1_DIGEST_SIZE))
5039dede 396 {
7aad6641
VK
397 pKey = d2i_RSAPublicKey(NULL, (OPENSSL_CONST BYTE **)&pBufPos, dwLen);
398 if (pKey != NULL)
5039dede 399 {
7aad6641
VK
400 if (d2i_RSAPrivateKey(&pKey, (OPENSSL_CONST BYTE **)&pBufPos,
401 dwLen - CAST_FROM_POINTER((pBufPos - pKeyBuffer), DWORD)) == NULL)
402 {
403 RSA_free(pKey);
404 pKey = NULL;
405 }
5039dede
AK
406 }
407 }
408 }
409 }
410 free(pKeyBuffer);
411 }
412 fclose(fp);
413 }
414
415 return pKey;
416#else
417 return NULL;
418#endif
419}
420
7aad6641
VK
421/**
422 * Create signature for message using certificate (MS CAPI version)
423 * Paraeters:
424 * pMsg and dwMsgLen - message to sign and it's length
425 * pCert - certificate
426 * pBuffer - output buffer
427 * dwBufSize - buffer size
428 * pdwSigLen - actual signature size
429 */
5039dede
AK
430#ifdef _WIN32
431
432BOOL LIBNETXMS_EXPORTABLE SignMessageWithCAPI(BYTE *pMsg, DWORD dwMsgLen, const CERT_CONTEXT *pCert,
433 BYTE *pBuffer, DWORD dwBufSize, DWORD *pdwSigLen)
434{
435 BOOL bFreeProv, bRet = FALSE;
436 DWORD i, j, dwLen, dwKeySpec;
437 HCRYPTPROV hProv;
438 HCRYPTHASH hHash;
439 BYTE *pTemp;
440
441 if (CryptAcquireCertificatePrivateKey(pCert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
442 NULL, &hProv, &dwKeySpec, &bFreeProv))
443 {
444 if (CryptCreateHash(hProv, CALG_SHA1, NULL, 0, &hHash))
445 {
446 if (CryptHashData(hHash, pMsg, dwMsgLen, 0))
447 {
448 dwLen = dwBufSize;
449 pTemp = (BYTE *)malloc(dwBufSize);;
450 bRet = CryptSignHash(hHash, dwKeySpec, NULL, 0, pTemp, &dwLen);
451 *pdwSigLen = dwLen;
452 // we have to reverse the byte-order in the result from CryptSignHash()
453 for(i = 0, j = dwLen - 1; i < dwLen; i++, j--)
454 pBuffer[i] = pTemp[j];
455 }
456 CryptDestroyHash(hHash);
457 }
458
459 if (bFreeProv)
460 CryptReleaseContext(hProv, 0);
461 }
462 return bRet;
463}
464
465#endif
4e2debcc 466
7aad6641
VK
467/**
468 * Encrypt data block with ICE
469 */
4e2debcc
VK
470void LIBNETXMS_EXPORTABLE ICEEncryptData(const BYTE *in, int inLen, BYTE *out, const BYTE *key)
471{
472 ICE_KEY *ice = ice_key_create(1);
473 ice_key_set(ice, key);
474
475 int stopPos = inLen - (inLen % 8);
476 const BYTE *curr = in;
477 for(int pos = 0; pos < stopPos; pos += 8)
478 ice_key_encrypt(ice, &in[pos], &out[pos]);
479
480 if (stopPos < inLen)
481 {
482 BYTE plainText[8], encrypted[8];
483
484 memcpy(plainText, &in[stopPos], inLen - stopPos);
485 ice_key_encrypt(ice, plainText, encrypted);
486 memcpy(&out[stopPos], encrypted, inLen - stopPos);
487 }
488
489 ice_key_destroy(ice);
490}
491
7aad6641
VK
492/**
493 * Decrypt data block with ICE
494 */
4e2debcc
VK
495void LIBNETXMS_EXPORTABLE ICEDecryptData(const BYTE *in, int inLen, BYTE *out, const BYTE *key)
496{
497 ICE_KEY *ice = ice_key_create(1);
498 ice_key_set(ice, key);
499
500 int stopPos = inLen - (inLen % 8);
501 const BYTE *curr = in;
502 for(int pos = 0; pos < stopPos; pos += 8)
503 ice_key_decrypt(ice, &in[pos], &out[pos]);
504
505 if (stopPos < inLen)
506 {
507 BYTE plainText[8], encrypted[8];
508
509 memcpy(encrypted, &in[stopPos], inLen - stopPos);
510 ice_key_decrypt(ice, encrypted, plainText);
511 memcpy(&out[stopPos], plainText, inLen - stopPos);
512 }
513
514 ice_key_destroy(ice);
515}
98abc9f1 516
7aad6641
VK
517/**
518 * Encryption context constructor
519 */
98abc9f1
VK
520NXCPEncryptionContext::NXCPEncryptionContext()
521{
522 m_sessionKey = NULL;
523}
524
7aad6641
VK
525/**
526 * Encryption context destructor
527 */
98abc9f1
VK
528NXCPEncryptionContext::~NXCPEncryptionContext()
529{
530 safe_free(m_sessionKey);
531}
532
7aad6641
VK
533/**
534 * Create encryption context from CMD_SESSION_KEY NXCP message
535 */
98abc9f1
VK
536NXCPEncryptionContext *NXCPEncryptionContext::create(CSCPMessage *msg, RSA *privateKey)
537{
e4cba530 538#ifdef _WITH_ENCRYPTION
98abc9f1
VK
539 BYTE ucKeyBuffer[KEY_BUFFER_SIZE], ucSessionKey[KEY_BUFFER_SIZE];
540 DWORD dwKeySize;
541 int nSize, nIVLen;
542 NXCPEncryptionContext *ctx = new NXCPEncryptionContext;
543
544 ctx->m_cipher = msg->GetVariableShort(VID_CIPHER);
545 ctx->m_keyLength = msg->GetVariableShort(VID_KEY_LENGTH);
546 ctx->m_sessionKey = (BYTE *)malloc(ctx->m_keyLength);
547
548 // Decrypt session key
549 dwKeySize = msg->GetVariableBinary(VID_SESSION_KEY, ucKeyBuffer, KEY_BUFFER_SIZE);
550 nSize = RSA_private_decrypt(dwKeySize, ucKeyBuffer, ucSessionKey, privateKey, RSA_PKCS1_OAEP_PADDING);
551 if (nSize == ctx->m_keyLength)
552 {
553 memcpy(ctx->m_sessionKey, ucSessionKey, nSize);
554
555 // Decrypt session IV
556 nIVLen = msg->GetVariableShort(VID_IV_LENGTH);
557 if (nIVLen == 0) // Versions prior to 0.2.13 don't send IV length, assume 16
558 nIVLen = 16;
559 dwKeySize = msg->GetVariableBinary(VID_SESSION_IV, ucKeyBuffer, KEY_BUFFER_SIZE);
560 nSize = RSA_private_decrypt(dwKeySize, ucKeyBuffer, ucSessionKey, privateKey, RSA_PKCS1_OAEP_PADDING);
561 if ((nSize == nIVLen) &&
562 (nIVLen <= EVP_CIPHER_iv_length(m_pfCipherList[ctx->m_cipher]())))
563 {
564 memcpy(ctx->m_iv, ucSessionKey, min(EVP_MAX_IV_LENGTH, nIVLen));
565 }
566 else
567 {
568 delete_and_null(ctx);
569 }
570 }
571 else
572 {
573 delete_and_null(ctx);
574 }
575 return ctx;
e4cba530
VK
576#else
577 return new NXCPEncryptionContext;
578#endif
98abc9f1
VK
579}
580
7aad6641
VK
581/**
582 * Create encryption context from CMD_REQUEST_SESSION_KEY NXCP message
583 */
98abc9f1
VK
584NXCPEncryptionContext *NXCPEncryptionContext::create(DWORD ciphers)
585{
586 NXCPEncryptionContext *ctx = new NXCPEncryptionContext;
587
e4cba530 588#ifdef _WITH_ENCRYPTION
98abc9f1
VK
589 // Select cipher
590 if (ciphers & CSCP_SUPPORT_AES_256)
591 {
592 ctx->m_cipher = CSCP_CIPHER_AES_256;
593 ctx->m_keyLength = 32;
594 }
595 else if (ciphers & CSCP_SUPPORT_BLOWFISH)
596 {
597 ctx->m_cipher = CSCP_CIPHER_BLOWFISH;
598 ctx->m_keyLength = 32;
599 }
9e9d631e
VK
600 else if (ciphers & CSCP_SUPPORT_AES_128)
601 {
602 ctx->m_cipher = CSCP_CIPHER_AES_128;
603 ctx->m_keyLength = 16;
604 }
98abc9f1
VK
605 else if (ciphers & CSCP_SUPPORT_IDEA)
606 {
607 ctx->m_cipher = CSCP_CIPHER_IDEA;
608 ctx->m_keyLength = 16;
609 }
610 else if (ciphers & CSCP_SUPPORT_3DES)
611 {
612 ctx->m_cipher = CSCP_CIPHER_3DES;
613 ctx->m_keyLength = 24;
614 }
615
616 // Generate key
617 ctx->m_sessionKey = (BYTE *)malloc(ctx->m_keyLength);
618 RAND_bytes(ctx->m_sessionKey, ctx->m_keyLength);
619 RAND_bytes(ctx->m_iv, EVP_MAX_IV_LENGTH);
e4cba530 620#endif
98abc9f1
VK
621
622 return ctx;
623}