1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2025-11-11 19:08:26 -06:00

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.

This commit is contained in:
Mounir IDRASSI
2016-08-08 00:49:00 +02:00
parent 3fb2eedab8
commit 5b381ce7d7
5 changed files with 286 additions and 30 deletions

View File

@@ -566,10 +566,63 @@ begin_format:
// Fill reserved header sectors (including the backup header area) with random data // Fill reserved header sectors (including the backup header area) with random data
if (!volParams->hiddenVol) if (!volParams->hiddenVol)
{ {
BOOL bUpdateBackup = FALSE;
nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE); nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE);
if (nStatus != ERR_SUCCESS) if (nStatus != ERR_SUCCESS)
goto error; 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 #ifndef DEBUG

View File

@@ -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
&& (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); nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, cryptoInfo->VolumeSize.Value, !backupHeader, backupHeader);
if (nStatus != ERR_SUCCESS) if (nStatus != ERR_SUCCESS)
goto error; 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); FlushFileBuffers (dev);

View File

@@ -809,7 +809,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea,
unsigned char *p = (unsigned char *) header; unsigned char *p = (unsigned char *) header;
static CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo; static CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo;
int nUserKeyLen = password->Length; int nUserKeyLen = password? password->Length : 0;
PCRYPTO_INFO cryptoInfo = crypto_open (); PCRYPTO_INFO cryptoInfo = crypto_open ();
static char dk[MASTER_KEYDATA_SIZE]; static char dk[MASTER_KEYDATA_SIZE];
int x; int x;
@@ -844,7 +844,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea,
} }
if (!RandgetBytes (hwndDlg, keyInfo.master_keydata, bytesNeeded, TRUE)) if (!RandgetBytes (hwndDlg, keyInfo.master_keydata, bytesNeeded, TRUE))
{
crypto_close (cryptoInfo);
return ERR_CIPHER_INIT_WEAK_KEY; return ERR_CIPHER_INIT_WEAK_KEY;
}
} }
else else
{ {
@@ -853,9 +856,17 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea,
} }
// User key // User key
memcpy (keyInfo.userKey, password->Text, nUserKeyLen); if (password)
keyInfo.keyLength = nUserKeyLen; {
keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, pim, FALSE, bBoot); 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 // User selected encryption algorithm
cryptoInfo->ea = ea; cryptoInfo->ea = ea;
@@ -871,34 +882,51 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea,
// Salt for header key derivation // Salt for header key derivation
if (!RandgetBytes (hwndDlg, keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode)) if (!RandgetBytes (hwndDlg, keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode))
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)
{ {
case SHA512: crypto_close (cryptoInfo);
derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, return ERR_CIPHER_INIT_WEAK_KEY;
PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); }
break;
case SHA256: if (password)
derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, {
PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); // PBKDF2 (PKCS5) is used to derive primary header key(s) and secondary header key(s) (XTS) from the password/keyfiles
break; switch (pkcs5_prf)
{
case SHA512:
derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt,
PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize());
break;
case RIPEMD160: case SHA256:
derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt,
PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize());
break; break;
case WHIRLPOOL: case RIPEMD160:
derive_key_whirlpool (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt,
PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize());
break; break;
default: case WHIRLPOOL:
// Unknown/wrong ID derive_key_whirlpool (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt,
TC_THROW_FATAL_EXCEPTION; 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 */ /* Header setup */
@@ -950,6 +978,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea,
|| sectorSize > TC_MAX_VOLUME_SECTOR_SIZE || sectorSize > TC_MAX_VOLUME_SECTOR_SIZE
|| sectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) || sectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
{ {
crypto_close (cryptoInfo);
TC_THROW_FATAL_EXCEPTION; 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); retVal = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks);
if (retVal != ERR_SUCCESS) if (retVal != ERR_SUCCESS)
{
crypto_close (cryptoInfo);
return retVal; return retVal;
}
// Mode of operation // Mode of operation
if (!EAInitMode (cryptoInfo)) if (!EAInitMode (cryptoInfo))
{
crypto_close (cryptoInfo);
return ERR_OUTOFMEMORY; return ERR_OUTOFMEMORY;
}
// Encrypt the entire header (except the salt) // 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) // Init with the master key(s)
retVal = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks); retVal = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks);
if (retVal != ERR_SUCCESS) if (retVal != ERR_SUCCESS)
{
crypto_close (cryptoInfo);
return retVal; return retVal;
}
memcpy (cryptoInfo->master_keydata, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); 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 // Mode of operation
if (!EAInitMode (cryptoInfo)) if (!EAInitMode (cryptoInfo))
{
crypto_close (cryptoInfo);
return ERR_OUTOFMEMORY; return ERR_OUTOFMEMORY;
}
#ifdef VOLFORMAT #ifdef VOLFORMAT

View File

@@ -804,7 +804,7 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas
cryptoInfo->RequiredProgramVersion, cryptoInfo->RequiredProgramVersion,
cryptoInfo->HeaderFlags, cryptoInfo->HeaderFlags,
cryptoInfo->SectorSize, cryptoInfo->SectorSize,
TRUE ); // use slow poll FALSE ); // use slow poll
if (ci != NULL) if (ci != NULL)
crypto_close (ci); crypto_close (ci);
@@ -818,8 +818,7 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas
goto error; goto error;
} }
nStatus = _lwrite ((HFILE) dev, buffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer))
if (nStatus != TC_VOLUME_HEADER_EFFECTIVE_SIZE)
{ {
nStatus = ERR_OS_ERROR; nStatus = ERR_OS_ERROR;
goto error; goto error;
@@ -835,9 +834,51 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas
) )
{ {
//DebugAddProgressDlgStatus(hwndDlg, L"WriteRandomDataToReservedHeaderAreas() ...\r\n"); //DebugAddProgressDlgStatus(hwndDlg, L"WriteRandomDataToReservedHeaderAreas() ...\r\n");
PCRYPTO_INFO dummyInfo = NULL;
LARGE_INTEGER hiddenOffset;
nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, newDataAreaSize, !backupHeader, backupHeader); nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, newDataAreaSize, !backupHeader, backupHeader);
if (nStatus != ERR_SUCCESS) if (nStatus != ERR_SUCCESS)
goto error; 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); FlushFileBuffers (dev);

View File

@@ -566,6 +566,8 @@ int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, vol
// Prepare the backup header // Prepare the backup header
for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++) for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++)
{ {
PCRYPTO_INFO dummyInfo = NULL;
nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
header, header,
volParams->ea, volParams->ea,
@@ -607,6 +609,47 @@ int EncryptPartitionInPlaceBegin (volatile FORMAT_VOL_PARAMETERS *volParams, vol
if (nStatus != ERR_SUCCESS) if (nStatus != ERR_SUCCESS)
goto closing_seq; 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++) for (int wipePass = 0; wipePass < (wipeAlgorithm == TC_WIPE_NONE ? 1 : PRAND_HEADER_WIPE_PASSES); wipePass++)
{ {
PCRYPTO_INFO dummyInfo = NULL;
nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE,
header, header,
headerCryptoInfo->ea, headerCryptoInfo->ea,
@@ -1081,6 +1126,40 @@ inplace_enc_read:
if (nStatus != ERR_SUCCESS) if (nStatus != ERR_SUCCESS)
goto closing_seq; 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 // Update the configuration files