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