From cf48b532b447faa969347fef183c6e8921c4ded2 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 26 Feb 2019 01:50:27 +0100 Subject: Windows: Implement RAM encryption for keys on 64-bit machines using ChaCha12 cipher and t1ha non-cryptographic fast hash (https://github.com/leo-yuriev/t1ha) --- src/Common/Apidrvr.h | 1 + src/Common/Crypto.c | 209 ++++++++++++++++++++++++++++++++++++++ src/Common/Crypto.h | 21 ++++ src/Common/EncryptionThreadPool.c | 45 +++++++- src/Common/Tcdefs.h | 9 +- 5 files changed, 279 insertions(+), 6 deletions(-) (limited to 'src/Common') diff --git a/src/Common/Apidrvr.h b/src/Common/Apidrvr.h index 63de40ae..2a6941a1 100644 --- a/src/Common/Apidrvr.h +++ b/src/Common/Apidrvr.h @@ -418,5 +418,6 @@ typedef struct #define VC_DRIVER_CONFIG_ALLOW_WINDOWS_DEFRAG 0x200 #define VC_DRIVER_CONFIG_CLEAR_KEYS_ON_NEW_DEVICE_INSERTION 0x400 #define VC_DRIVER_CONFIG_ENABLE_CPU_RNG 0x800 +#define VC_DRIVER_CONFIG_ENABLE_RAM_ENCRYPTION 0x1000 #endif /* _WIN32 */ diff --git a/src/Common/Crypto.c b/src/Common/Crypto.c index 249a4117..94fca8e8 100644 --- a/src/Common/Crypto.c +++ b/src/Common/Crypto.c @@ -1220,6 +1220,7 @@ BOOL IsHwEncryptionEnabled () #ifndef TC_WINDOWS_BOOT static BOOL CpuRngDisabled = TRUE; +static BOOL RamEncryptionEnabled = FALSE; BOOL IsCpuRngSupported () { @@ -1239,6 +1240,214 @@ BOOL IsCpuRngEnabled () return !CpuRngDisabled; } +BOOL IsRamEncryptionSupported () +{ +#ifdef _WIN64 + return TRUE; +#else + return FALSE; +#endif +} + +void EnableRamEncryption (BOOL enable) +{ + RamEncryptionEnabled = enable; +} + +BOOL IsRamEncryptionEnabled () +{ + return RamEncryptionEnabled; +} + +/* masking for random index to remove bias */ +byte GetRngMask (byte count) +{ + if (count >= 128) + return 0xFF; + if (count >= 64) + return 0x7F; + if (count >= 32) + return 0x3F; + if (count >= 16) + return 0x1F; + if (count >= 8) + return 0x0F; + if (count >= 4) + return 0x07; + if (count >= 2) + return 0x03; + return 1; +} + +byte GetRandomIndex (ChaCha20RngCtx* pCtx, byte elementsCount) +{ + byte index = 0; + byte mask = GetRngMask (elementsCount); + + while (TRUE) + { + ChaCha20RngGetBytes (pCtx, &index, 1); + index &= mask; + if (index < elementsCount) + break; + } + + return index; +} + +#if defined(_WIN64) && !defined (_UEFI) && defined(TC_WINDOWS_DRIVER) +/* declaration of variables and functions used for RAM encryption on 64-bit build */ +static byte* pbKeyDerivationArea = NULL; +static ULONG cbKeyDerivationArea = 0; + +static uint64 HashSeedMask = 0; +static uint64 CipherIVMask = 0; +#ifdef TC_WINDOWS_DRIVER +ULONG AllocTag = 'MMCV'; +#endif + +BOOL InitializeSecurityParameters(GetRandSeedFn rngCallback) +{ + ChaCha20RngCtx ctx; + byte pbSeed[CHACHA20RNG_KEYSZ + CHACHA20RNG_IVSZ]; +#ifdef TC_WINDOWS_DRIVER + byte i, tagLength; +#endif + + rngCallback (pbSeed, sizeof (pbSeed)); + + ChaCha20RngInit (&ctx, pbSeed, rngCallback, 0); + +#ifdef TC_WINDOWS_DRIVER + /* generate random tag length between 1 and 4 */ + tagLength = GetRandomIndex (&ctx, 4) + 1; + + /* generate random value for tag: + * Each ASCII character in the tag must be a value in the range 0x20 (space) to 0x7E (tilde) + * So we have 95 possibility + */ + AllocTag = 0; + for (i = 0; i < tagLength; i++) + { + AllocTag = (AllocTag << 8) + (((ULONG) GetRandomIndex (&ctx, 95)) + 0x20); + } + +#endif + + cbKeyDerivationArea = 1024 * 1024; + pbKeyDerivationArea = (byte*) TCalloc(cbKeyDerivationArea); + if (!pbKeyDerivationArea) + { + cbKeyDerivationArea = 2 * PAGE_SIZE; + pbKeyDerivationArea = (byte*) TCalloc(cbKeyDerivationArea); + } + + if (!pbKeyDerivationArea) + { + cbKeyDerivationArea = 0; + return FALSE; + } + + /* fill key derivation area with random bytes */ + ChaCha20RngGetBytes (&ctx, pbKeyDerivationArea, cbKeyDerivationArea); + + /* generate hash seed mask */ + ChaCha20RngGetBytes(&ctx, (unsigned char*) &HashSeedMask, sizeof (HashSeedMask)); + + /* generate IV mask */ + ChaCha20RngGetBytes(&ctx, (unsigned char*) &CipherIVMask, sizeof (CipherIVMask)); + + FAST_ERASE64 (pbSeed, sizeof (pbSeed)); + burn (&ctx, sizeof (ctx)); + burn (&tagLength, 1); + + return TRUE; +} + +void ClearSecurityParameters() +{ + if (pbKeyDerivationArea) + { + FAST_ERASE64 (pbKeyDerivationArea, cbKeyDerivationArea); + TCfree (pbKeyDerivationArea); + pbKeyDerivationArea =NULL; + cbKeyDerivationArea = 0; + } + + FAST_ERASE64 (&HashSeedMask, 8); + FAST_ERASE64 (&CipherIVMask, 8); +#ifdef TC_WINDOWS_DRIVER + burn (&AllocTag, sizeof (AllocTag)); +#endif +} + +#ifdef TC_WINDOWS_DRIVER +static void VcProtectMemory (uint64 encID, unsigned char* pbData, size_t cbData, unsigned char* pbData2, size_t cbData2) +#else +static void VcProtectMemory (uint64 encID, unsigned char* pbData, size_t cbData, + unsigned char* pbData2, size_t cbData2, + unsigned char* pbData3, size_t cbData3, + unsigned char* pbData4, size_t cbData4) +#endif +{ + if (pbKeyDerivationArea) + { + uint64 hashLow, hashHigh, hashSeed, cipherIV; + uint64 pbKey[4]; + ChaCha256Ctx ctx; + + hashSeed = (((uint64) pbKeyDerivationArea) + encID) ^ HashSeedMask; + hashLow = t1ha2_atonce128(&hashHigh, pbKeyDerivationArea, cbKeyDerivationArea, hashSeed); + + /* set the key to the hash result */ + pbKey[0] = pbKey[2] = hashLow; + pbKey[1] = pbKey[3] = hashHigh; + + /* Initialize ChaCha12 cipher */ + cipherIV = encID ^ CipherIVMask; + ChaCha256Init (&ctx, (unsigned char*) pbKey, (unsigned char*) &cipherIV, 12); + + ChaCha256Encrypt (&ctx, pbData, cbData, pbData); + ChaCha256Encrypt (&ctx, pbData2, cbData2, pbData2); +#ifndef TC_WINDOWS_DRIVER + ChaCha256Encrypt (&ctx, pbData3, cbData3, pbData3); + ChaCha256Encrypt (&ctx, pbData4, cbData4, pbData4); +#endif + FAST_ERASE64 (pbKey, sizeof(pbKey)); + FAST_ERASE64 (&hashLow, 8); + FAST_ERASE64 (&hashHigh, 8); + FAST_ERASE64 (&hashSeed, 8); + FAST_ERASE64 (&cipherIV, 8); + burn (&ctx, sizeof (ctx)); + } +} + +uint64 VcGetEncryptionID (PCRYPTO_INFO pCryptoInfo) +{ + return ((uint64) pCryptoInfo->ks) + ((uint64) pCryptoInfo->ks2) +#ifndef TC_WINDOWS_DRIVER + + ((uint64) pCryptoInfo->master_keydata) + ((uint64) pCryptoInfo->k2) +#endif + ; +} + +void VcProtectKeys (PCRYPTO_INFO pCryptoInfo, uint64 encID) +{ +#ifdef TC_WINDOWS_DRIVER + VcProtectMemory (encID, pCryptoInfo->ks, MAX_EXPANDED_KEY, pCryptoInfo->ks2, MAX_EXPANDED_KEY); +#else + VcProtectMemory (encID, pCryptoInfo->ks, MAX_EXPANDED_KEY, + pCryptoInfo->ks2, MAX_EXPANDED_KEY, + pCryptoInfo->master_keydata, MASTER_KEYDATA_SIZE, + pCryptoInfo->k2, MASTER_KEYDATA_SIZE); +#endif +} + +void VcUnprotectKeys (PCRYPTO_INFO pCryptoInfo, uint64 encID) +{ + VcProtectKeys (pCryptoInfo, encID); +} +#endif #endif diff --git a/src/Common/Crypto.h b/src/Common/Crypto.h index 6c2befb1..0951b20b 100644 --- a/src/Common/Crypto.h +++ b/src/Common/Crypto.h @@ -208,6 +208,10 @@ typedef struct # include "GostCipher.h" # include "kuznyechik.h" # include "Camellia.h" +# include "chachaRng.h" +# ifdef _WIN64 +# include "t1ha.h" +# endif #else # include "CamelliaSmall.h" #endif @@ -381,6 +385,19 @@ void DecryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *s void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo); void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo); +#if defined(_WIN64) && !defined (_UEFI) && defined(TC_WINDOWS_DRIVER) +BOOL InitializeSecurityParameters(GetRandSeedFn rngCallback); +void ClearSecurityParameters(); +uint64 VcGetEncryptionID (PCRYPTO_INFO pCryptoInfo); +void VcProtectKeys (PCRYPTO_INFO pCryptoInfo, uint64 encID); +void VcUnprotectKeys (PCRYPTO_INFO pCryptoInfo, uint64 encID); +void EncryptDataUnitsCurrentThreadEx (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci); +void DecryptDataUnitsCurrentThreadEx (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci); +#else +#define EncryptDataUnitsCurrentThreadEx EncryptDataUnitsCurrentThread +#define DecryptDataUnitsCurrentThreadEx DecryptDataUnitsCurrentThread +#endif + BOOL IsAesHwCpuSupported (); void EnableHwEncryption (BOOL enable); BOOL IsHwEncryptionEnabled (); @@ -389,6 +406,10 @@ BOOL IsCpuRngSupported (); void EnableCpuRng (BOOL enable); BOOL IsCpuRngEnabled (); +BOOL IsRamEncryptionSupported (); +void EnableRamEncryption (BOOL enable); +BOOL IsRamEncryptionEnabled (); + #ifdef __cplusplus } #endif diff --git a/src/Common/EncryptionThreadPool.c b/src/Common/EncryptionThreadPool.c index 618e8474..fdf1101c 100644 --- a/src/Common/EncryptionThreadPool.c +++ b/src/Common/EncryptionThreadPool.c @@ -111,6 +111,43 @@ static TC_MUTEX DequeueMutex; static TC_EVENT WorkItemReadyEvent; static TC_EVENT WorkItemCompletedEvent; +#if defined(_WIN64) && defined(TC_WINDOWS_DRIVER) +void EncryptDataUnitsCurrentThreadEx (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci) +{ + if (IsRamEncryptionEnabled()) + { + CRYPTO_INFO tmpCI; + memcpy (&tmpCI, ci, sizeof (CRYPTO_INFO)); + VcUnprotectKeys (&tmpCI, VcGetEncryptionID (ci)); + + EncryptDataUnitsCurrentThread (buf, structUnitNo, nbrUnits, &tmpCI); + + burn (&tmpCI, sizeof(CRYPTO_INFO)); + } + else + EncryptDataUnitsCurrentThread (buf, structUnitNo, nbrUnits, ci); +} + +void DecryptDataUnitsCurrentThreadEx (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci) +{ + if (IsRamEncryptionEnabled()) + { + CRYPTO_INFO tmpCI; + memcpy (&tmpCI, ci, sizeof (CRYPTO_INFO)); + VcUnprotectKeys (&tmpCI, VcGetEncryptionID (ci)); + + DecryptDataUnitsCurrentThread (buf, structUnitNo, nbrUnits, &tmpCI); + + burn (&tmpCI, sizeof(CRYPTO_INFO)); + } + else + DecryptDataUnitsCurrentThread (buf, structUnitNo, nbrUnits, ci); +} + +#else +#define EncryptDataUnitsCurrentThreadEx EncryptDataUnitsCurrentThread +#define DecryptDataUnitsCurrentThreadEx DecryptDataUnitsCurrentThread +#endif static WorkItemState GetWorkItemState (EncryptionThreadPoolWorkItem *workItem) { @@ -152,11 +189,11 @@ static TC_THREAD_PROC EncryptionThreadProc (void *threadArg) switch (workItem->Type) { case DecryptDataUnitsWork: - DecryptDataUnitsCurrentThread (workItem->Encryption.Data, &workItem->Encryption.StartUnitNo, workItem->Encryption.UnitCount, workItem->Encryption.CryptoInfo); + DecryptDataUnitsCurrentThreadEx (workItem->Encryption.Data, &workItem->Encryption.StartUnitNo, workItem->Encryption.UnitCount, workItem->Encryption.CryptoInfo); break; case EncryptDataUnitsWork: - EncryptDataUnitsCurrentThread (workItem->Encryption.Data, &workItem->Encryption.StartUnitNo, workItem->Encryption.UnitCount, workItem->Encryption.CryptoInfo); + EncryptDataUnitsCurrentThreadEx (workItem->Encryption.Data, &workItem->Encryption.StartUnitNo, workItem->Encryption.UnitCount, workItem->Encryption.CryptoInfo); break; case DeriveKeyWork: @@ -414,11 +451,11 @@ void EncryptionThreadPoolDoWork (EncryptionThreadPoolWorkType type, byte *data, switch (type) { case DecryptDataUnitsWork: - DecryptDataUnitsCurrentThread (data, startUnitNo, unitCount, cryptoInfo); + DecryptDataUnitsCurrentThreadEx (data, startUnitNo, unitCount, cryptoInfo); break; case EncryptDataUnitsWork: - EncryptDataUnitsCurrentThread (data, startUnitNo, unitCount, cryptoInfo); + EncryptDataUnitsCurrentThreadEx (data, startUnitNo, unitCount, cryptoInfo); break; default: diff --git a/src/Common/Tcdefs.h b/src/Common/Tcdefs.h index ed0c94bb..47a4bc54 100644 --- a/src/Common/Tcdefs.h +++ b/src/Common/Tcdefs.h @@ -248,9 +248,14 @@ void ThrowFatalException(int line); /* variables used in the implementation of enhanced protection of NX pool under Windows 8 and later */ extern POOL_TYPE ExDefaultNonPagedPoolType; extern ULONG ExDefaultMdlProtection; +#ifdef _WIN64 +extern ULONG AllocTag; +#else +#define AllocTag 'MMCV' +#endif -#define TCalloc(size) ((void *) ExAllocatePoolWithTag( ExDefaultNonPagedPoolType, size, 'MMCV' )) -#define TCfree(memblock) ExFreePoolWithTag( memblock, 'MMCV' ) +#define TCalloc(size) ((void *) ExAllocatePoolWithTag( ExDefaultNonPagedPoolType, size, AllocTag )) +#define TCfree(memblock) ExFreePoolWithTag( memblock, AllocTag ) #define DEVICE_DRIVER -- cgit v1.2.3