From 5b381ce7d7ec45e02765b4a180f9aa1ee8aeee40 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Mon, 8 Aug 2016 00:49:00 +0200 Subject: Windows: Fix vulnerability inherited from TrueCrypt that allows an attacker to detect with high probability if a hidden volume is present. Vulnerability reported by Ivanov Alexey Mikhailovich. --- src/Common/Format.c | 53 ++++++++++++++++++++++ src/Common/Password.c | 42 +++++++++++++++++ src/Common/Volumes.c | 99 +++++++++++++++++++++++++++++------------ src/ExpandVolume/ExpandVolume.c | 47 +++++++++++++++++-- src/Format/InPlace.c | 79 ++++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/Common/Format.c b/src/Common/Format.c index f3114e5e..7d6f3edb 100644 --- a/src/Common/Format.c +++ b/src/Common/Format.c @@ -566,10 +566,63 @@ begin_format: // Fill reserved header sectors (including the backup header area) with random data if (!volParams->hiddenVol) { + BOOL bUpdateBackup = FALSE; + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE); if (nStatus != ERR_SUCCESS) goto error; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes. + + while (TRUE) + { + PCRYPTO_INFO dummyInfo = NULL; + LARGE_INTEGER hiddenOffset; + + hiddenOffset.QuadPart = bUpdateBackup ? dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE + TC_HIDDEN_VOLUME_HEADER_OFFSET: TC_HIDDEN_VOLUME_HEADER_OFFSET; + + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + volParams->ea, + FIRST_MODE_OF_OPERATION_ID, + NULL, + 0, + 0, + NULL, + &dummyInfo, + dataAreaSize, + dataAreaSize, + dataOffset, + dataAreaSize, + 0, + volParams->headerFlags, + FormatSectorSize, + FALSE); + + if (nStatus != ERR_SUCCESS) + goto error; + + crypto_close (dummyInfo); + + if (!SetFilePointerEx ((HANDLE) dev, hiddenOffset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (!WriteEffectiveVolumeHeader (volParams->bDevice, dev, header)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (bUpdateBackup) + break; + + bUpdateBackup = TRUE; + } } #ifndef DEBUG diff --git a/src/Common/Password.c b/src/Common/Password.c index 2c9e77c3..1c9083a3 100644 --- a/src/Common/Password.c +++ b/src/Common/Password.c @@ -437,9 +437,51 @@ int ChangePwd (const wchar_t *lpszVolume, Password *oldPassword, int old_pkcs5, && (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) != 0 && (cryptoInfo->HeaderFlags & ~TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0) { + PCRYPTO_INFO dummyInfo = NULL; + LARGE_INTEGER hiddenOffset; + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, cryptoInfo->VolumeSize.Value, !backupHeader, backupHeader); if (nStatus != ERR_SUCCESS) goto error; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes + hiddenOffset.QuadPart = backupHeader ? cryptoInfo->VolumeSize.Value + TC_VOLUME_HEADER_GROUP_SIZE + TC_HIDDEN_VOLUME_HEADER_OFFSET: TC_HIDDEN_VOLUME_HEADER_OFFSET; + + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + buffer, + cryptoInfo->ea, + cryptoInfo->mode, + NULL, + 0, + 0, + NULL, + &dummyInfo, + cryptoInfo->VolumeSize.Value, + cryptoInfo->VolumeSize.Value, + cryptoInfo->EncryptedAreaStart.Value, + cryptoInfo->EncryptedAreaLength.Value, + truecryptMode? 0 : cryptoInfo->RequiredProgramVersion, + cryptoInfo->HeaderFlags, + cryptoInfo->SectorSize, + wipePass < wipePassCount - 1); + + if (nStatus != ERR_SUCCESS) + goto error; + + crypto_close (dummyInfo); + + if (!SetFilePointerEx ((HANDLE) dev, hiddenOffset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer)) + { + nStatus = ERR_OS_ERROR; + goto error; + } } FlushFileBuffers (dev); diff --git a/src/Common/Volumes.c b/src/Common/Volumes.c index 816d94f0..8ad40ac1 100644 --- a/src/Common/Volumes.c +++ b/src/Common/Volumes.c @@ -809,7 +809,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, unsigned char *p = (unsigned char *) header; static CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo; - int nUserKeyLen = password->Length; + int nUserKeyLen = password? password->Length : 0; PCRYPTO_INFO cryptoInfo = crypto_open (); static char dk[MASTER_KEYDATA_SIZE]; int x; @@ -844,7 +844,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, } if (!RandgetBytes (hwndDlg, keyInfo.master_keydata, bytesNeeded, TRUE)) + { + crypto_close (cryptoInfo); return ERR_CIPHER_INIT_WEAK_KEY; + } } else { @@ -853,9 +856,17 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, } // User key - memcpy (keyInfo.userKey, password->Text, nUserKeyLen); - keyInfo.keyLength = nUserKeyLen; - keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, pim, FALSE, bBoot); + if (password) + { + memcpy (keyInfo.userKey, password->Text, nUserKeyLen); + keyInfo.keyLength = nUserKeyLen; + keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, pim, FALSE, bBoot); + } + else + { + keyInfo.keyLength = 0; + keyInfo.noIterations = 0; + } // User selected encryption algorithm cryptoInfo->ea = ea; @@ -871,34 +882,51 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, // Salt for header key derivation if (!RandgetBytes (hwndDlg, keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode)) - return ERR_CIPHER_INIT_WEAK_KEY; + { + crypto_close (cryptoInfo); + return ERR_CIPHER_INIT_WEAK_KEY; + } - // PBKDF2 (PKCS5) is used to derive primary header key(s) and secondary header key(s) (XTS) from the password/keyfiles - switch (pkcs5_prf) + if (password) { - case SHA512: - derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; - - case SHA256: - derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; - - case RIPEMD160: - derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; - - case WHIRLPOOL: - derive_key_whirlpool (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; + // PBKDF2 (PKCS5) is used to derive primary header key(s) and secondary header key(s) (XTS) from the password/keyfiles + switch (pkcs5_prf) + { + case SHA512: + derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; - default: - // Unknown/wrong ID - TC_THROW_FATAL_EXCEPTION; + case SHA256: + derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; + + case RIPEMD160: + derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; + + case WHIRLPOOL: + derive_key_whirlpool (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; + + + default: + // Unknown/wrong ID + crypto_close (cryptoInfo); + TC_THROW_FATAL_EXCEPTION; + } + } + else + { + // generate a random key + if (!RandgetBytes(hwndDlg, dk, GetMaxPkcs5OutSize(), !bWipeMode)) + { + crypto_close (cryptoInfo); + return ERR_CIPHER_INIT_WEAK_KEY; + } } /* Header setup */ @@ -950,6 +978,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, || sectorSize > TC_MAX_VOLUME_SECTOR_SIZE || sectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) { + crypto_close (cryptoInfo); TC_THROW_FATAL_EXCEPTION; } @@ -978,11 +1007,17 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, retVal = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) + { + crypto_close (cryptoInfo); return retVal; + } // Mode of operation if (!EAInitMode (cryptoInfo)) + { + crypto_close (cryptoInfo); return ERR_OUTOFMEMORY; + } // Encrypt the entire header (except the salt) @@ -996,7 +1031,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, // Init with the master key(s) retVal = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) + { + crypto_close (cryptoInfo); return retVal; + } memcpy (cryptoInfo->master_keydata, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); @@ -1010,7 +1048,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, // Mode of operation if (!EAInitMode (cryptoInfo)) + { + crypto_close (cryptoInfo); return ERR_OUTOFMEMORY; + } #ifdef VOLFORMAT diff --git a/src/ExpandVolume/ExpandVolume.c b/src/ExpandVolume/ExpandVolume.c index abe09557..82c4207e 100644 --- a/src/ExpandVolume/ExpandVolume.c +++ b/src/ExpandVolume/ExpandVolume.c @@ -804,7 +804,7 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas cryptoInfo->RequiredProgramVersion, cryptoInfo->HeaderFlags, cryptoInfo->SectorSize, - TRUE ); // use slow poll + FALSE ); // use slow poll if (ci != NULL) crypto_close (ci); @@ -818,8 +818,7 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas goto error; } - nStatus = _lwrite ((HFILE) dev, buffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - if (nStatus != TC_VOLUME_HEADER_EFFECTIVE_SIZE) + if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer)) { nStatus = ERR_OS_ERROR; goto error; @@ -835,9 +834,51 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas ) { //DebugAddProgressDlgStatus(hwndDlg, L"WriteRandomDataToReservedHeaderAreas() ...\r\n"); + PCRYPTO_INFO dummyInfo = NULL; + LARGE_INTEGER hiddenOffset; + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, newDataAreaSize, !backupHeader, backupHeader); if (nStatus != ERR_SUCCESS) goto error; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes + hiddenOffset.QuadPart = headerOffset.QuadPart + TC_HIDDEN_VOLUME_HEADER_OFFSET; + + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + buffer, + cryptoInfo->ea, + cryptoInfo->mode, + NULL, + 0, + 0, + NULL, + &dummyInfo, + newDataAreaSize, + newDataAreaSize, // hiddenVolumeSize + cryptoInfo->EncryptedAreaStart.Value, + newDataAreaSize, + cryptoInfo->RequiredProgramVersion, + cryptoInfo->HeaderFlags, + cryptoInfo->SectorSize, + FALSE ); // use slow poll + + if (nStatus != ERR_SUCCESS) + goto error; + + crypto_close (dummyInfo); + + if (!SetFilePointerEx ((HANDLE) dev, hiddenOffset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer)) + { + nStatus = ERR_OS_ERROR; + goto error; + } } FlushFileBuffers (dev); diff --git a/src/Format/InPlace.c b/src/Format/InPlace.c index d3b3212b..f37cb8be 100644 --- a/src/Format/InPlace.c +++ b/src/Format/InPlace.c @@ -566,6 +566,8 @@ int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, vol // Prepare the backup header for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) { + PCRYPTO_INFO dummyInfo = NULL; + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, header, volParams->ea, @@ -607,6 +609,47 @@ int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, vol if (nStatus != ERR_SUCCESS) goto closing_seq; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + volParams->ea, + FIRST_MODE_OF_OPERATION_ID, + NULL, + 0, + 0, + NULL, + &dummyInfo, + dataAreaSize, + dataAreaSize, + TC_VOLUME_DATA_OFFSET + dataAreaSize, // Start of the encrypted area = the first byte of the backup heeader (encrypting from the end) + dataAreaSize, // No data is encrypted yet + 0, + volParams->headerFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC, + volParams->sectorSize, + wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + crypto_close (dummyInfo); + + offset.QuadPart += TC_HIDDEN_VOLUME_HEADER_OFFSET; + + if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + + // Write the fake hidden backup header to the partition + if (!WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } + } @@ -1045,6 +1088,8 @@ inplace_enc_read: for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) { + PCRYPTO_INFO dummyInfo = NULL; + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, header, headerCryptoInfo->ea, @@ -1081,6 +1126,40 @@ inplace_enc_read: if (nStatus != ERR_SUCCESS) goto closing_seq; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + headerCryptoInfo->ea, + headerCryptoInfo->mode, + NULL, + 0, + 0, + NULL, + &dummyInfo, + masterCryptoInfo->VolumeSize.Value, + masterCryptoInfo->VolumeSize.Value, + masterCryptoInfo->EncryptedAreaStart.Value, + masterCryptoInfo->EncryptedAreaLength.Value, + masterCryptoInfo->RequiredProgramVersion, + masterCryptoInfo->HeaderFlags | TC_HEADER_FLAG_NONSYS_INPLACE_ENC, + masterCryptoInfo->SectorSize, + wipeAlgorithm == TC_WIPE_NONE ? FALSE : (wipePass < PRAND_HEADER_WIPE_PASSES - 1)); + + if (nStatus != ERR_SUCCESS) + goto closing_seq; + + crypto_close (dummyInfo); + + offset.QuadPart += TC_HIDDEN_VOLUME_HEADER_OFFSET; + + if (SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) == 0 + || !WriteEffectiveVolumeHeader (TRUE, dev, (byte *) header)) + { + nStatus = ERR_OS_ERROR; + goto closing_seq; + } } // Update the configuration files -- cgit v1.2.3