static bool OpenVolume (byte drive, Password &password, CRYPTO_INFO **cryptoInfo, uint32 *headerSaltCrc32, bool skipNormal, bool skipHidden) { int volumeType; bool hiddenVolume; uint64 headerSec; AcquireSectorBuffer(); for (volumeType = 1; volumeType <= 2; ++volumeType) { hiddenVolume = (volumeType == 2); if (hiddenVolume) { if (skipHidden || PartitionFollowingActive.Drive != drive || PartitionFollowingActive.SectorCount <= ActivePartition.SectorCount) continue; headerSec = PartitionFollowingActive.StartSector + TC_HIDDEN_VOLUME_HEADER_OFFSET / TC_LB_SIZE; } else { if (skipNormal) continue; headerSec.HighPart = 0; headerSec.LowPart = TC_BOOT_VOLUME_HEADER_SECTOR; } if (ReadSectors (SectorBuffer, drive, headerSec, 1) != BiosResultSuccess) continue; if (ReadVolumeHeader (!hiddenVolume, (char *) SectorBuffer, &password, cryptoInfo, nullptr) == ERR_SUCCESS) { // Prevent opening a non-system hidden volume if (hiddenVolume && !((*cryptoInfo)->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM)) { crypto_close (*cryptoInfo); continue; } if (headerSaltCrc32) *headerSaltCrc32 = GetCrc32 (SectorBuffer, PKCS5_SALT_SIZE); break; } } ReleaseSectorBuffer(); return volumeType != 3; }
void GSTCloseVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) { if (DeviceObject); /* Remove compiler warning */ if (Extension->hDeviceFile != NULL) { if (Extension->bRawDevice == FALSE && Extension->bTimeStampValid) { RestoreTimeStamp (Extension); } ZwClose (Extension->hDeviceFile); } ObDereferenceObject (Extension->pfoDeviceFile); crypto_close (Extension->cryptoInfo); }
int TCFormatVolume (volatile FORMAT_VOL_PARAMETERS *volParams) { int nStatus; PCRYPTO_INFO cryptoInfo = NULL; HANDLE dev = INVALID_HANDLE_VALUE; DWORD dwError; char header[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; unsigned __int64 num_sectors, startSector; fatparams ft; FILETIME ftCreationTime; FILETIME ftLastWriteTime; FILETIME ftLastAccessTime; BOOL bTimeStampValid = FALSE; BOOL bInstantRetryOtherFilesys = FALSE; char dosDev[TC_MAX_PATH] = { 0 }; char devName[MAX_PATH] = { 0 }; int driveLetter = -1; WCHAR deviceName[MAX_PATH]; uint64 dataOffset, dataAreaSize; LARGE_INTEGER offset; BOOL bFailedRequiredDASD = FALSE; HWND hwndDlg = volParams->hwndDlg; FormatSectorSize = volParams->sectorSize; if (FormatSectorSize < TC_MIN_VOLUME_SECTOR_SIZE || FormatSectorSize > TC_MAX_VOLUME_SECTOR_SIZE || FormatSectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) { Error ("SECTOR_SIZE_UNSUPPORTED", hwndDlg); return ERR_DONT_REPORT; } /* WARNING: Note that if Windows fails to format the volume as NTFS and the volume size is less than the maximum FAT size, the user is asked within this function whether he wants to instantly retry FAT format instead (to avoid having to re-create the whole container again). If the user answers yes, some of the input parameters are modified, the code below 'begin_format' is re-executed and some destructive operations that were performed during the first attempt must be (and are) skipped. Therefore, whenever adding or modifying any potentially destructive operations below 'begin_format', determine whether they (or their portions) need to be skipped during such a second attempt; if so, use the 'bInstantRetryOtherFilesys' flag to skip them. */ if (volParams->hiddenVol) { dataOffset = volParams->hiddenVolHostSize - TC_VOLUME_HEADER_GROUP_SIZE - volParams->size; } else { if (volParams->size <= TC_TOTAL_VOLUME_HEADERS_SIZE) return ERR_VOL_SIZE_WRONG; dataOffset = TC_VOLUME_DATA_OFFSET; } dataAreaSize = GetVolumeDataAreaSize (volParams->hiddenVol, volParams->size); num_sectors = dataAreaSize / FormatSectorSize; if (volParams->bDevice) { StringCbCopyA ((char *)deviceName, sizeof(deviceName), volParams->volumePath); ToUNICODE ((char *)deviceName, sizeof(deviceName)); driveLetter = GetDiskDeviceDriveLetter (deviceName); } VirtualLock (header, sizeof (header)); nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, header, volParams->ea, FIRST_MODE_OF_OPERATION_ID, volParams->password, volParams->pkcs5, volParams->pim, NULL, &cryptoInfo, dataAreaSize, volParams->hiddenVol ? dataAreaSize : 0, dataOffset, dataAreaSize, 0, volParams->headerFlags, FormatSectorSize, FALSE); if (nStatus != 0) { burn (header, sizeof (header)); VirtualUnlock (header, sizeof (header)); return nStatus; } begin_format: if (volParams->bDevice) { /* Device-hosted volume */ DWORD dwResult; int nPass; if (FakeDosNameForDevice (volParams->volumePath, dosDev, sizeof(dosDev), devName, sizeof(devName), FALSE) != 0) return ERR_OS_ERROR; if (IsDeviceMounted (devName)) { if ((dev = DismountDrive (devName, volParams->volumePath)) == INVALID_HANDLE_VALUE) { Error ("FORMAT_CANT_DISMOUNT_FILESYS", hwndDlg); nStatus = ERR_DONT_REPORT; goto error; } /* Gain "raw" access to the partition (it contains a live filesystem and the filesystem driver would otherwise prevent us from writing to hidden sectors). */ if (!DeviceIoControl (dev, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwResult, NULL)) { bFailedRequiredDASD = TRUE; } } else if (IsOSAtLeast (WIN_VISTA) && driveLetter == -1) { // Windows Vista doesn't allow overwriting sectors belonging to an unformatted partition // to which no drive letter has been assigned under the system. This problem can be worked // around by assigning a drive letter to the partition temporarily. char szDriveLetter[] = { 'A', ':', 0 }; char rootPath[] = { 'A', ':', '\\', 0 }; char uniqVolName[MAX_PATH+1] = { 0 }; int tmpDriveLetter = -1; BOOL bResult = FALSE; tmpDriveLetter = GetFirstAvailableDrive (); if (tmpDriveLetter != -1) { rootPath[0] += (char) tmpDriveLetter; szDriveLetter[0] += (char) tmpDriveLetter; if (DefineDosDevice (DDD_RAW_TARGET_PATH, szDriveLetter, volParams->volumePath)) { bResult = GetVolumeNameForVolumeMountPoint (rootPath, uniqVolName, MAX_PATH); DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION|DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, volParams->volumePath); if (bResult && SetVolumeMountPoint (rootPath, uniqVolName)) { // The drive letter can be removed now DeleteVolumeMountPoint (rootPath); } } } } // For extra safety, we will try to gain "raw" access to the partition. Note that this should actually be // redundant because if the filesystem was mounted, we already tried to obtain DASD above. If we failed, // bFailedRequiredDASD was set to TRUE and therefore we will perform pseudo "quick format" below. However, // for extra safety, in case IsDeviceMounted() failed to detect a live filesystem, we will blindly // send FSCTL_ALLOW_EXTENDED_DASD_IO (possibly for a second time) without checking the result. DeviceIoControl (dev, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwResult, NULL); // If DASD is needed but we failed to obtain it, perform open - 'quick format' - close - open // so that the filesystem driver does not prevent us from formatting hidden sectors. for (nPass = (bFailedRequiredDASD ? 0 : 1); nPass < 2; nPass++) { int retryCount; retryCount = 0; // Try exclusive access mode first // Note that when exclusive access is denied, it is worth retrying (usually succeeds after a few tries). while (dev == INVALID_HANDLE_VALUE && retryCount++ < EXCL_ACCESS_MAX_AUTO_RETRIES) { dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (retryCount > 1) Sleep (EXCL_ACCESS_AUTO_RETRY_DELAY); } if (dev == INVALID_HANDLE_VALUE) { // Exclusive access denied -- retry in shared mode dev = CreateFile (devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (dev != INVALID_HANDLE_VALUE) { if (!volParams->bForceOperation && (Silent || (IDNO == MessageBoxW (volParams->hwndDlg, GetString ("DEVICE_IN_USE_FORMAT"), lpszTitle, MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2)))) { nStatus = ERR_DONT_REPORT; goto error; } } else { handleWin32Error (volParams->hwndDlg, SRC_POS); Error ("CANT_ACCESS_VOL", hwndDlg); nStatus = ERR_DONT_REPORT; goto error; } } if (volParams->hiddenVol || bInstantRetryOtherFilesys) break; // The following "quick format" operation would damage the outer volume if (nPass == 0) { char buf [2 * TC_MAX_VOLUME_SECTOR_SIZE]; DWORD bw; // Perform pseudo "quick format" so that the filesystem driver does not prevent us from // formatting hidden sectors memset (buf, 0, sizeof (buf)); if (!WriteFile (dev, buf, sizeof (buf), &bw, NULL)) { nStatus = ERR_OS_ERROR; goto error; } FlushFileBuffers (dev); CloseHandle (dev); dev = INVALID_HANDLE_VALUE; } } if (DeviceIoControl (dev, FSCTL_IS_VOLUME_MOUNTED, NULL, 0, NULL, 0, &dwResult, NULL)) { Error ("FORMAT_CANT_DISMOUNT_FILESYS", hwndDlg); nStatus = ERR_DONT_REPORT; goto error; } } else { /* File-hosted volume */ dev = CreateFile (volParams->volumePath, GENERIC_READ | GENERIC_WRITE, (volParams->hiddenVol || bInstantRetryOtherFilesys) ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : 0, NULL, (volParams->hiddenVol || bInstantRetryOtherFilesys) ? OPEN_EXISTING : CREATE_ALWAYS, 0, NULL); if (dev == INVALID_HANDLE_VALUE) { nStatus = ERR_OS_ERROR; goto error; } DisableFileCompression (dev); if (!volParams->hiddenVol && !bInstantRetryOtherFilesys) { LARGE_INTEGER volumeSize; volumeSize.QuadPart = dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE; if (volParams->sparseFileSwitch && volParams->quickFormat) { // Create as sparse file container DWORD tmp; if (!DeviceIoControl (dev, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &tmp, NULL)) { nStatus = ERR_OS_ERROR; goto error; } } // Preallocate the file if (!SetFilePointerEx (dev, volumeSize, NULL, FILE_BEGIN) || !SetEndOfFile (dev) || SetFilePointer (dev, 0, NULL, FILE_BEGIN) != 0) { nStatus = ERR_OS_ERROR; goto error; } } } if (volParams->hiddenVol && !volParams->bDevice && bPreserveTimestamp) { if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) bTimeStampValid = FALSE; else bTimeStampValid = TRUE; } if (volParams->hwndDlg && volParams->bGuiMode) KillTimer (volParams->hwndDlg, TIMER_ID_RANDVIEW); /* Volume header */ // Hidden volume setup if (volParams->hiddenVol) { LARGE_INTEGER headerOffset; // Check hidden volume size if (volParams->hiddenVolHostSize < TC_MIN_HIDDEN_VOLUME_HOST_SIZE || volParams->hiddenVolHostSize > TC_MAX_HIDDEN_VOLUME_HOST_SIZE) { nStatus = ERR_VOL_SIZE_WRONG; goto error; } // Seek to hidden volume header location headerOffset.QuadPart = TC_HIDDEN_VOLUME_HEADER_OFFSET; if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } } else if (bInstantRetryOtherFilesys) { // The previous file system format failed and the user wants to try again with a different file system. // The volume header had been written successfully so we need to seek to the byte after the header. LARGE_INTEGER offset; offset.QuadPart = TC_VOLUME_DATA_OFFSET; if (!SetFilePointerEx ((HANDLE) dev, offset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } } if (!bInstantRetryOtherFilesys) { // Write the volume header if (!WriteEffectiveVolumeHeader (volParams->bDevice, dev, header)) { nStatus = ERR_OS_ERROR; goto error; } // To prevent fragmentation, write zeroes to reserved header sectors which are going to be filled with random data if (!volParams->bDevice && !volParams->hiddenVol) { byte buf[TC_VOLUME_HEADER_GROUP_SIZE - TC_VOLUME_HEADER_EFFECTIVE_SIZE]; DWORD bytesWritten; ZeroMemory (buf, sizeof (buf)); if (!WriteFile (dev, buf, sizeof (buf), &bytesWritten, NULL)) { nStatus = ERR_OS_ERROR; goto error; } if (bytesWritten != sizeof (buf)) { nStatus = ERR_PARAMETER_INCORRECT; goto error; } } } if (volParams->hiddenVol) { // Calculate data area position of hidden volume cryptoInfo->hiddenVolumeOffset = dataOffset; // Validate the offset if (dataOffset % FormatSectorSize != 0) { nStatus = ERR_VOL_SIZE_WRONG; goto error; } volParams->quickFormat = TRUE; // To entirely format a hidden volume would be redundant } /* Data area */ startSector = dataOffset / FormatSectorSize; // Format filesystem switch (volParams->fileSystem) { case FILESYS_NONE: case FILESYS_NTFS: if (volParams->bDevice && !StartFormatWriteThread()) { nStatus = ERR_OS_ERROR; goto error; } nStatus = FormatNoFs (hwndDlg, startSector, num_sectors, dev, cryptoInfo, volParams->quickFormat); if (volParams->bDevice) StopFormatWriteThread(); break; case FILESYS_FAT: if (num_sectors > 0xFFFFffff) { nStatus = ERR_VOL_SIZE_WRONG; goto error; } // Calculate the fats, root dir etc ft.num_sectors = (unsigned int) (num_sectors); #if TC_MAX_VOLUME_SECTOR_SIZE > 0xFFFF #error TC_MAX_VOLUME_SECTOR_SIZE > 0xFFFF #endif ft.sector_size = (uint16) FormatSectorSize; ft.cluster_size = volParams->clusterSize; memcpy (ft.volume_name, "NO NAME ", 11); GetFatParams (&ft); *(volParams->realClusterSize) = ft.cluster_size * FormatSectorSize; if (volParams->bDevice && !StartFormatWriteThread()) { nStatus = ERR_OS_ERROR; goto error; } nStatus = FormatFat (hwndDlg, startSector, &ft, (void *) dev, cryptoInfo, volParams->quickFormat); if (volParams->bDevice) StopFormatWriteThread(); break; default: nStatus = ERR_PARAMETER_INCORRECT; goto error; } if (nStatus != ERR_SUCCESS) goto error; // Write header backup offset.QuadPart = volParams->hiddenVol ? volParams->hiddenVolHostSize - TC_HIDDEN_VOLUME_HEADER_OFFSET : dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE; if (!SetFilePointerEx ((HANDLE) dev, offset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, header, volParams->ea, FIRST_MODE_OF_OPERATION_ID, volParams->password, volParams->pkcs5, volParams->pim, cryptoInfo->master_keydata, &cryptoInfo, dataAreaSize, volParams->hiddenVol ? dataAreaSize : 0, dataOffset, dataAreaSize, 0, volParams->headerFlags, FormatSectorSize, FALSE); if (!WriteEffectiveVolumeHeader (volParams->bDevice, dev, header)) { nStatus = ERR_OS_ERROR; goto error; } // Fill reserved header sectors (including the backup header area) with random data if (!volParams->hiddenVol) { nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE); if (nStatus != ERR_SUCCESS) goto error; } #ifndef DEBUG if (volParams->quickFormat && volParams->fileSystem != FILESYS_NTFS) Sleep (500); // User-friendly GUI #endif error: dwError = GetLastError(); burn (header, sizeof (header)); VirtualUnlock (header, sizeof (header)); if (dev != INVALID_HANDLE_VALUE) { if (!volParams->bDevice && !volParams->hiddenVol && nStatus != 0) { // Remove preallocated part before closing file handle if format failed if (SetFilePointer (dev, 0, NULL, FILE_BEGIN) == 0) SetEndOfFile (dev); } FlushFileBuffers (dev); if (bTimeStampValid) SetFileTime (dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime); CloseHandle (dev); dev = INVALID_HANDLE_VALUE; } if (nStatus != 0) { SetLastError(dwError); goto fv_end; } if (volParams->fileSystem == FILESYS_NTFS) { // Quick-format volume as NTFS int driveNo = GetLastAvailableDrive (); MountOptions mountOptions; int retCode; ZeroMemory (&mountOptions, sizeof (mountOptions)); if (driveNo == -1) { if (!Silent) { MessageBoxW (volParams->hwndDlg, GetString ("NO_FREE_DRIVES"), lpszTitle, ICON_HAND); MessageBoxW (volParams->hwndDlg, GetString ("FORMAT_NTFS_STOP"), lpszTitle, ICON_HAND); } nStatus = ERR_NO_FREE_DRIVES; goto fv_end; } mountOptions.ReadOnly = FALSE; mountOptions.Removable = FALSE; mountOptions.ProtectHiddenVolume = FALSE; mountOptions.PreserveTimestamp = bPreserveTimestamp; mountOptions.PartitionInInactiveSysEncScope = FALSE; mountOptions.UseBackupHeader = FALSE; if (MountVolume (volParams->hwndDlg, driveNo, volParams->volumePath, volParams->password, volParams->pkcs5, volParams->pim, FALSE, FALSE, TRUE, &mountOptions, FALSE, TRUE) < 1) { if (!Silent) { MessageBoxW (volParams->hwndDlg, GetString ("CANT_MOUNT_VOLUME"), lpszTitle, ICON_HAND); MessageBoxW (volParams->hwndDlg, GetString ("FORMAT_NTFS_STOP"), lpszTitle, ICON_HAND); } nStatus = ERR_VOL_MOUNT_FAILED; goto fv_end; } if (!Silent && !IsAdmin () && IsUacSupported ()) retCode = UacFormatNtfs (volParams->hwndDlg, driveNo, volParams->clusterSize); else retCode = FormatNtfs (driveNo, volParams->clusterSize); if (retCode != TRUE) { if (!UnmountVolumeAfterFormatExCall (volParams->hwndDlg, driveNo) && !Silent) MessageBoxW (volParams->hwndDlg, GetString ("CANT_DISMOUNT_VOLUME"), lpszTitle, ICON_HAND); if (dataAreaSize <= TC_MAX_FAT_SECTOR_COUNT * FormatSectorSize) { if (AskErrYesNo ("FORMAT_NTFS_FAILED_ASK_FAT", hwndDlg) == IDYES) { // NTFS format failed and the user wants to try FAT format immediately volParams->fileSystem = FILESYS_FAT; bInstantRetryOtherFilesys = TRUE; volParams->quickFormat = TRUE; // Volume has already been successfully TC-formatted volParams->clusterSize = 0; // Default cluster size goto begin_format; } } else Error ("FORMAT_NTFS_FAILED", hwndDlg); nStatus = ERR_DONT_REPORT; goto fv_end; } if (!UnmountVolumeAfterFormatExCall (volParams->hwndDlg, driveNo) && !Silent) MessageBoxW (volParams->hwndDlg, GetString ("CANT_DISMOUNT_VOLUME"), lpszTitle, ICON_HAND); } fv_end: dwError = GetLastError(); if (dosDev[0]) RemoveFakeDosName (volParams->volumePath, dosDev); crypto_close (cryptoInfo); SetLastError (dwError); return nStatus; }
/* ExpandVolume Sets the volume size in the volume header (and backup header) to a larger value, and resizes the filesystem within the volume (only NTFS supported) Parameters: hwndDlg : HWND [in] handle to progress dialog lpszVolume : char * [in] Pointer to a string that contains the path to the truecrypt volume pVolumePassword : Password * [in] Pointer to the volume password newHostSize : uint64 [in] new value of the volume host size (can be zero for devices, which means the volume should use all space of the host device) initFreeSpace : BOOL [in] if true, the new volume space will be initalized with random data Return value: int with Truecrypt error code (ERR_SUCCESS on success) Remarks: a lot of code is from TrueCrypt 'Common\Password.c' :: ChangePwd() */ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePassword, int VolumePkcs5, int VolumePim, uint64 newHostSize, BOOL initFreeSpace) { int nDosLinkCreated = 1, nStatus = ERR_OS_ERROR; wchar_t szDiskFile[TC_MAX_PATH], szCFDevice[TC_MAX_PATH]; wchar_t szDosDevice[TC_MAX_PATH]; char buffer[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; PCRYPTO_INFO cryptoInfo = NULL, ci = NULL; void *dev = INVALID_HANDLE_VALUE; DWORD dwError; BOOL bDevice; uint64 hostSize=0, newDataAreaSize, currentVolSize; DWORD HostSectorSize; FILETIME ftCreationTime; FILETIME ftLastWriteTime; FILETIME ftLastAccessTime; BOOL bTimeStampValid = FALSE; LARGE_INTEGER headerOffset; BOOL backupHeader; byte *wipeBuffer = NULL; uint32 workChunkSize = TC_VOLUME_HEADER_GROUP_SIZE; if (pVolumePassword->Length == 0) return -1; WaitCursor (); CreateFullVolumePath (szDiskFile, sizeof(szDiskFile), lpszVolume, &bDevice); if (bDevice == FALSE) { wcscpy (szCFDevice, szDiskFile); } else { nDosLinkCreated = FakeDosNameForDevice (szDiskFile, szDosDevice, sizeof(szDosDevice), szCFDevice, sizeof(szCFDevice), FALSE); if (nDosLinkCreated != 0) // note: nStatus == ERR_OS_ERROR goto error; } dev = CreateFile (szCFDevice, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (dev == INVALID_HANDLE_VALUE) goto error; if (bDevice) { /* This is necessary to determine the hidden volume header offset */ if (dev == INVALID_HANDLE_VALUE) { goto error; } else { PARTITION_INFORMATION diskInfo; DWORD dwResult; BOOL bResult; bResult = GetPartitionInfo (lpszVolume, &diskInfo); if (bResult) { hostSize = diskInfo.PartitionLength.QuadPart; HostSectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME; //TO DO: get the real host disk sector size } else { DISK_GEOMETRY driveInfo; bResult = DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveInfo, sizeof (driveInfo), &dwResult, NULL); if (!bResult) goto error; hostSize = driveInfo.Cylinders.QuadPart * driveInfo.BytesPerSector * driveInfo.SectorsPerTrack * driveInfo.TracksPerCylinder; HostSectorSize = driveInfo.BytesPerSector; } if (hostSize == 0) { nStatus = ERR_VOL_SIZE_WRONG; goto error; } } } else { LARGE_INTEGER fileSize; if (!GetFileSizeEx (dev, &fileSize)) { nStatus = ERR_OS_ERROR; goto error; } hostSize = fileSize.QuadPart; HostSectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME; //TO DO: get the real host disk sector size } if (Randinit ()) { if (CryptoAPILastError == ERROR_SUCCESS) nStatus = ERR_RAND_INIT_FAILED; else nStatus = ERR_CAPI_INIT_FAILED; goto error; } if (!bDevice && bPreserveTimestamp) { /* Remember the container modification/creation date and time, (used to reset file date and time of file-hosted volumes after password change (or attempt to), in order to preserve plausible deniability of hidden volumes (last password change time is stored in the volume header). */ if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) { bTimeStampValid = FALSE; MessageBoxW (hwndDlg, GetString ("GETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); } else bTimeStampValid = TRUE; } // Seek the volume header headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET; if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } /* Read in volume header */ nStatus = _lread ((HFILE) dev, buffer, sizeof (buffer)); if (nStatus != sizeof (buffer)) { // Windows may report EOF when reading sectors from the last cluster of a device formatted as NTFS memset (buffer, 0, sizeof (buffer)); } /* Try to decrypt the header */ nStatus = ReadVolumeHeader (FALSE, buffer, pVolumePassword, VolumePkcs5, VolumePim, FALSE, &cryptoInfo, NULL); if (nStatus == ERR_CIPHER_INIT_WEAK_KEY) nStatus = 0; // We can ignore this error here if (nStatus != 0) { cryptoInfo = NULL; goto error; } if (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM) { nStatus = ERR_SYS_HIDVOL_HEAD_REENC_MODE_WRONG; goto error; } if (bDevice && newHostSize == 0) { // this means we shall take all host space as new volume size newHostSize = hostSize; } if ( newHostSize % cryptoInfo->SectorSize != 0 || newHostSize > TC_MAX_VOLUME_SIZE || (bDevice && newHostSize > hostSize) ) { // 1. must be multiple of sector size // 2. truecrypt volume size limit // 3. for devices volume size can't be larger than host size cryptoInfo = NULL; nStatus = ERR_PARAMETER_INCORRECT; goto error; } newDataAreaSize = GetVolumeDataAreaSize (newHostSize, cryptoInfo->LegacyVolume); if (cryptoInfo->LegacyVolume) { if (bDevice) { if (initFreeSpace) { // unsupported cryptoInfo = NULL; nStatus = ERR_PARAMETER_INCORRECT; goto error; } else { // note: dummy value (only used for parameter checks) cryptoInfo->VolumeSize.Value = newDataAreaSize - TC_MINVAL_FS_EXPAND; } } else { cryptoInfo->VolumeSize.Value = GetVolumeDataAreaSize (hostSize, TRUE); } } currentVolSize = GetVolumeSizeByDataAreaSize (cryptoInfo->VolumeSize.Value, cryptoInfo->LegacyVolume); if ( newDataAreaSize < cryptoInfo->VolumeSize.Value + TC_MINVAL_FS_EXPAND ) { // shrinking a volume or enlarging by less then TC_MINVAL_FS_EXPAND is not allowed cryptoInfo = NULL; nStatus = ERR_PARAMETER_INCORRECT; goto error; } InitProgressBar ( newHostSize, currentVolSize, FALSE, FALSE, FALSE, TRUE); if (bVolTransformThreadCancel) { SetLastError(0); nStatus = ERR_USER_ABORT; goto error; } if (!bDevice) { LARGE_INTEGER liNewSize; liNewSize.QuadPart=(LONGLONG)newHostSize; // Preallocate the file if (!SetFilePointerEx (dev, liNewSize, NULL, FILE_BEGIN) || !SetEndOfFile (dev) || SetFilePointer (dev, 0, NULL, FILE_BEGIN) != 0) { nStatus = ERR_OS_ERROR; goto error; } } if (initFreeSpace) { uint64 startSector; int64 num_sectors; // fill new space with random data startSector = currentVolSize/HostSectorSize ; num_sectors = (newHostSize/HostSectorSize) - startSector; if (bDevice && !StartFormatWriteThread()) { nStatus = ERR_OS_ERROR; goto error; } DebugAddProgressDlgStatus(hwndDlg, L"Writing random data to new space ...\r\n"); SetFormatSectorSize(HostSectorSize); nStatus = FormatNoFs (hwndDlg, startSector, num_sectors, dev, cryptoInfo, FALSE); dwError = GetLastError(); StopFormatWriteThread(); SetLastError (dwError); } else { UpdateProgressBar(newHostSize); } if (nStatus != ERR_SUCCESS) { dwError = GetLastError(); DebugAddProgressDlgStatus(hwndDlg, L"Error: failed to write random data ...\r\n"); if ( !bDevice ) { // restore original size of the container file LARGE_INTEGER liOldSize; liOldSize.QuadPart=(LONGLONG)hostSize; if (!SetFilePointerEx (dev, liOldSize, NULL, FILE_BEGIN) || !SetEndOfFile (dev)) { DebugAddProgressDlgStatus(hwndDlg, L"Warning: failed to restore original size of the container file\r\n"); } } SetLastError (dwError); goto error; } RandSetHashFunction (cryptoInfo->pkcs5); // Re-encrypt the volume header forn non-legacy volumes: backup header first backupHeader = TRUE; headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET + newHostSize - TC_VOLUME_HEADER_GROUP_SIZE; /* note: updating the header is not neccessary for legay volumes */ while ( !cryptoInfo->LegacyVolume ) { if (backupHeader) DebugAddProgressDlgStatus(hwndDlg, L"Writing re-encrypted backup header ...\r\n"); else DebugAddProgressDlgStatus(hwndDlg, L"Writing re-encrypted primary header ...\r\n"); // Prepare new volume header nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, buffer, cryptoInfo->ea, cryptoInfo->mode, pVolumePassword, cryptoInfo->pkcs5, VolumePim, (char*)(cryptoInfo->master_keydata), &ci, newDataAreaSize, 0, // hiddenVolumeSize cryptoInfo->EncryptedAreaStart.Value, newDataAreaSize, cryptoInfo->RequiredProgramVersion, cryptoInfo->HeaderFlags, cryptoInfo->SectorSize, TRUE ); // use slow poll if (ci != NULL) crypto_close (ci); if (nStatus != 0) goto error; if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } nStatus = _lwrite ((HFILE) dev, buffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); if (nStatus != TC_VOLUME_HEADER_EFFECTIVE_SIZE) { nStatus = ERR_OS_ERROR; goto error; } if ( ( backupHeader && !initFreeSpace ) || ( bDevice && !cryptoInfo->LegacyVolume && !cryptoInfo->hiddenVolume && cryptoInfo->HeaderVersion == 4 // BUG in TrueCrypt: doing this only for v4 make no sense && (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) != 0 && (cryptoInfo->HeaderFlags & ~TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0 ) ) { //DebugAddProgressDlgStatus(hwndDlg, L"WriteRandomDataToReservedHeaderAreas() ...\r\n"); nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, newDataAreaSize, !backupHeader, backupHeader); if (nStatus != ERR_SUCCESS) goto error; } FlushFileBuffers (dev); if (!backupHeader) break; backupHeader = FALSE; headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET; // offset for main header } /* header successfully updated */ nStatus = ERR_SUCCESS; if (bVolTransformThreadCancel) { nStatus = ERR_USER_ABORT; goto error; } /* wipe old backup header */ if ( !cryptoInfo->LegacyVolume ) { byte wipeRandChars [TC_WIPE_RAND_CHAR_COUNT]; byte wipeRandCharsUpdate [TC_WIPE_RAND_CHAR_COUNT]; byte wipePass; UINT64_STRUCT unitNo; LARGE_INTEGER offset; WipeAlgorithmId wipeAlgorithm = TC_WIPE_35_GUTMANN; if ( !RandgetBytes (hwndDlg, wipeRandChars, TC_WIPE_RAND_CHAR_COUNT, TRUE) || !RandgetBytes (hwndDlg, wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT, TRUE) ) { nStatus = ERR_OS_ERROR; goto error; } DebugAddProgressDlgStatus(hwndDlg, L"Wiping old backup header ...\r\n"); wipeBuffer = (byte *) TCalloc (workChunkSize); if (!wipeBuffer) { nStatus = ERR_OUTOFMEMORY; goto error; } offset.QuadPart = currentVolSize - TC_VOLUME_HEADER_GROUP_SIZE; unitNo.Value = offset.QuadPart; for (wipePass = 1; wipePass <= GetWipePassCount (wipeAlgorithm); ++wipePass) { if (!WipeBuffer (wipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, workChunkSize)) { ULONG i; for (i = 0; i < workChunkSize; ++i) { wipeBuffer[i] = wipePass; } EncryptDataUnits (wipeBuffer, &unitNo, workChunkSize / ENCRYPTION_DATA_UNIT_SIZE, cryptoInfo); memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate)); } if ( !SetFilePointerEx (dev, offset, NULL, FILE_BEGIN) || _lwrite ((HFILE)dev, (LPCSTR)wipeBuffer, workChunkSize) == HFILE_ERROR ) { // Write error DebugAddProgressDlgStatus(hwndDlg, L"Warning: Failed to wipe old backup header\r\n"); MessageBoxW (hwndDlg, L"WARNING: Failed to wipe old backup header!\n\nIt may be possible to use the current volume password to decrypt the old backup header even after a future password change.\n", lpszTitle, MB_OK | MB_ICONEXCLAMATION); if (wipePass == 1) continue; // retry once // non-critical error - it's better to continue nStatus = ERR_SUCCESS; goto error; } FlushFileBuffers(dev); // we don't check FlushFileBuffers() return code, because it fails for devices // (same implementation in password.c - a bug or not ???) } burn (wipeRandChars, TC_WIPE_RAND_CHAR_COUNT); burn (wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT); } error: dwError = GetLastError (); if (wipeBuffer) { burn (wipeBuffer, workChunkSize); TCfree (wipeBuffer); wipeBuffer = NULL; } burn (buffer, sizeof (buffer)); if (cryptoInfo != NULL) crypto_close (cryptoInfo); if (bTimeStampValid) { // Restore the container timestamp (to preserve plausible deniability of possible hidden volume). if (SetFileTime (dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) MessageBoxW (hwndDlg, GetString ("SETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); } if (dev != INVALID_HANDLE_VALUE) CloseHandle ((HANDLE) dev); if (nDosLinkCreated == 0) RemoveFakeDosName (szDiskFile, szDosDevice); RandStop (FALSE); if (bVolTransformThreadCancel) nStatus = ERR_USER_ABORT; SetLastError (dwError); if (nStatus == ERR_SUCCESS) { nStatus = ExtendFileSystem (hwndDlg, lpszVolume, pVolumePassword, VolumePkcs5, VolumePim, newDataAreaSize); } return nStatus; }
int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo) { #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE char dk[32 * 2]; // 2 * 256-bit key char masterKey[32 * 2]; #else char dk[32 * 2 * 3]; // 6 * 256-bit key char masterKey[32 * 2 * 3]; #endif PCRYPTO_INFO cryptoInfo; int status; if (retHeaderCryptoInfo != NULL) cryptoInfo = retHeaderCryptoInfo; else cryptoInfo = *retInfo = crypto_open (); // PKCS5 PRF derive_key_ripemd160 (password->Text, (int) password->Length, header + HEADER_SALT_OFFSET, PKCS5_SALT_SIZE, bBoot ? 1000 : 2000, dk, sizeof (dk)); // Mode of operation cryptoInfo->mode = FIRST_MODE_OF_OPERATION_ID; // Test all available encryption algorithms for (cryptoInfo->ea = EAGetFirst (); cryptoInfo->ea != 0; cryptoInfo->ea = EAGetNext (cryptoInfo->ea)) { status = EAInit (cryptoInfo->ea, dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; // Secondary key schedule EAInit (cryptoInfo->ea, dk + EAGetKeySize (cryptoInfo->ea), cryptoInfo->ks2); // Try to decrypt header DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); // Check magic 'TRUE' and CRC-32 of header fields and master keydata if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x54525545 || (GetHeaderField16 (header, TC_HEADER_OFFSET_VERSION) >= 4 && GetHeaderField32 (header, TC_HEADER_OFFSET_HEADER_CRC) != GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC)) || GetHeaderField32 (header, TC_HEADER_OFFSET_KEY_AREA_CRC) != GetCrc32 (header + HEADER_MASTER_KEYDATA_OFFSET, MASTER_KEYDATA_SIZE)) { EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); continue; } // Header decrypted status = 0; // Hidden volume status cryptoInfo->VolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_HIDDEN_VOLUME_SIZE); cryptoInfo->hiddenVolume = (cryptoInfo->VolumeSize.LowPart != 0 || cryptoInfo->VolumeSize.HighPart != 0); // Volume size cryptoInfo->VolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_VOLUME_SIZE); // Encrypted area size and length cryptoInfo->EncryptedAreaStart = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_START); cryptoInfo->EncryptedAreaLength = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH); // Flags cryptoInfo->HeaderFlags = GetHeaderField32 (header, TC_HEADER_OFFSET_FLAGS); memcpy (masterKey, header + HEADER_MASTER_KEYDATA_OFFSET, sizeof (masterKey)); EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); if (retHeaderCryptoInfo) goto ret; // Init the encryption algorithm with the decrypted master key status = EAInit (cryptoInfo->ea, masterKey, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; // The secondary master key (if cascade, multiple concatenated) EAInit (cryptoInfo->ea, masterKey + EAGetKeySize (cryptoInfo->ea), cryptoInfo->ks2); goto ret; } status = ERR_PASSWORD_WRONG; err: if (cryptoInfo != retHeaderCryptoInfo) { crypto_close(cryptoInfo); *retInfo = NULL; } ret: burn (dk, sizeof(dk)); burn (masterKey, sizeof(masterKey)); return status; }
int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo) { char header[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; KEY_INFO keyInfo; PCRYPTO_INFO cryptoInfo; char dk[MASTER_KEYDATA_SIZE]; int enqPkcs5Prf, pkcs5_prf; uint16 headerVersion; int status = ERR_PARAMETER_INCORRECT; int primaryKeyOffset; TC_EVENT keyDerivationCompletedEvent; TC_EVENT noOutstandingWorkItemEvent; KeyDerivationWorkItem *keyDerivationWorkItems; KeyDerivationWorkItem *item; int pkcs5PrfCount = LAST_PRF_ID - FIRST_PRF_ID + 1; size_t encryptionThreadCount = GetEncryptionThreadCount(); size_t queuedWorkItems = 0; LONG outstandingWorkItemCount = 0; int i; if (retHeaderCryptoInfo != NULL) { cryptoInfo = retHeaderCryptoInfo; } else { cryptoInfo = *retInfo = crypto_open (); if (cryptoInfo == NULL) return ERR_OUTOFMEMORY; } if (encryptionThreadCount > 1) { keyDerivationWorkItems = TCalloc (sizeof (KeyDerivationWorkItem) * pkcs5PrfCount); if (!keyDerivationWorkItems) return ERR_OUTOFMEMORY; for (i = 0; i < pkcs5PrfCount; ++i) keyDerivationWorkItems[i].Free = TRUE; #ifdef DEVICE_DRIVER KeInitializeEvent (&keyDerivationCompletedEvent, SynchronizationEvent, FALSE); KeInitializeEvent (&noOutstandingWorkItemEvent, SynchronizationEvent, TRUE); #else keyDerivationCompletedEvent = CreateEvent (NULL, FALSE, FALSE, NULL); if (!keyDerivationCompletedEvent) { TCfree (keyDerivationWorkItems); return ERR_OUTOFMEMORY; } noOutstandingWorkItemEvent = CreateEvent (NULL, FALSE, TRUE, NULL); if (!noOutstandingWorkItemEvent) { CloseHandle (keyDerivationCompletedEvent); TCfree (keyDerivationWorkItems); return ERR_OUTOFMEMORY; } #endif } #ifndef DEVICE_DRIVER VirtualLock (&keyInfo, sizeof (keyInfo)); VirtualLock (&dk, sizeof (dk)); #endif crypto_loadkey (&keyInfo, password->Text, (int) password->Length); // PKCS5 is used to derive the primary header key(s) and secondary header key(s) (XTS mode) from the password memcpy (keyInfo.salt, encryptedHeader + HEADER_SALT_OFFSET, PKCS5_SALT_SIZE); // Test all available PKCS5 PRFs for (enqPkcs5Prf = FIRST_PRF_ID; enqPkcs5Prf <= LAST_PRF_ID || queuedWorkItems > 0; ++enqPkcs5Prf) { BOOL lrw64InitDone = FALSE; // Deprecated/legacy BOOL lrw128InitDone = FALSE; // Deprecated/legacy if (encryptionThreadCount > 1) { // Enqueue key derivation on thread pool if (queuedWorkItems < encryptionThreadCount && enqPkcs5Prf <= LAST_PRF_ID) { for (i = 0; i < pkcs5PrfCount; ++i) { item = &keyDerivationWorkItems[i]; if (item->Free) { item->Free = FALSE; item->KeyReady = FALSE; item->Pkcs5Prf = enqPkcs5Prf; EncryptionThreadPoolBeginKeyDerivation (&keyDerivationCompletedEvent, &noOutstandingWorkItemEvent, &item->KeyReady, &outstandingWorkItemCount, enqPkcs5Prf, keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, get_pkcs5_iteration_count (enqPkcs5Prf, bBoot), item->DerivedKey); ++queuedWorkItems; break; } } if (enqPkcs5Prf < LAST_PRF_ID) continue; } else --enqPkcs5Prf; // Wait for completion of a key derivation while (queuedWorkItems > 0) { for (i = 0; i < pkcs5PrfCount; ++i) { item = &keyDerivationWorkItems[i]; if (!item->Free && InterlockedExchangeAdd (&item->KeyReady, 0) == TRUE) { pkcs5_prf = item->Pkcs5Prf; keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, bBoot); memcpy (dk, item->DerivedKey, sizeof (dk)); item->Free = TRUE; --queuedWorkItems; goto KeyReady; } } if (queuedWorkItems > 0) TC_WAIT_EVENT (keyDerivationCompletedEvent); } continue; KeyReady: ; } else { pkcs5_prf = enqPkcs5Prf; keyInfo.noIterations = get_pkcs5_iteration_count (enqPkcs5Prf, bBoot); switch (pkcs5_prf) { case RIPEMD160: derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; case SHA512: derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; case SHA1: // Deprecated/legacy derive_key_sha1 (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 TC_THROW_FATAL_EXCEPTION; } } // Test all available modes of operation for (cryptoInfo->mode = FIRST_MODE_OF_OPERATION_ID; cryptoInfo->mode <= LAST_MODE_OF_OPERATION; cryptoInfo->mode++) { switch (cryptoInfo->mode) { case LRW: case CBC: case INNER_CBC: case OUTER_CBC: // For LRW (deprecated/legacy), copy the tweak key // For CBC (deprecated/legacy), copy the IV/whitening seed memcpy (cryptoInfo->k2, dk, LEGACY_VOL_IV_SIZE); primaryKeyOffset = LEGACY_VOL_IV_SIZE; break; default: primaryKeyOffset = 0; } // Test all available encryption algorithms for (cryptoInfo->ea = EAGetFirst (); cryptoInfo->ea != 0; cryptoInfo->ea = EAGetNext (cryptoInfo->ea)) { int blockSize; if (!EAIsModeSupported (cryptoInfo->ea, cryptoInfo->mode)) continue; // This encryption algorithm has never been available with this mode of operation blockSize = CipherGetBlockSize (EAGetFirstCipher (cryptoInfo->ea)); status = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; // Init objects related to the mode of operation if (cryptoInfo->mode == XTS) { // Copy the secondary key (if cascade, multiple concatenated) memcpy (cryptoInfo->k2, dk + EAGetKeySize (cryptoInfo->ea), EAGetKeySize (cryptoInfo->ea)); // Secondary key schedule if (!EAInitMode (cryptoInfo)) { status = ERR_MODE_INIT_FAILED; goto err; } } else if (cryptoInfo->mode == LRW && (blockSize == 8 && !lrw64InitDone || blockSize == 16 && !lrw128InitDone)) { // Deprecated/legacy if (!EAInitMode (cryptoInfo)) { status = ERR_MODE_INIT_FAILED; goto err; } if (blockSize == 8) lrw64InitDone = TRUE; else if (blockSize == 16) lrw128InitDone = TRUE; } // Copy the header for decryption memcpy (header, encryptedHeader, sizeof (header)); // Try to decrypt header DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); // Magic 'TRUE' if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x54525545) continue; // Header version headerVersion = GetHeaderField16 (header, TC_HEADER_OFFSET_VERSION); if (headerVersion > VOLUME_HEADER_VERSION) { status = ERR_NEW_VERSION_REQUIRED; goto err; } // Check CRC of the header fields if (!ReadVolumeHeaderRecoveryMode && headerVersion >= 4 && GetHeaderField32 (header, TC_HEADER_OFFSET_HEADER_CRC) != GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC)) continue; // Required program version cryptoInfo->RequiredProgramVersion = GetHeaderField16 (header, TC_HEADER_OFFSET_REQUIRED_VERSION); cryptoInfo->LegacyVolume = cryptoInfo->RequiredProgramVersion < 0x600; // Check CRC of the key set if (!ReadVolumeHeaderRecoveryMode && GetHeaderField32 (header, TC_HEADER_OFFSET_KEY_AREA_CRC) != GetCrc32 (header + HEADER_MASTER_KEYDATA_OFFSET, MASTER_KEYDATA_SIZE)) continue; // Now we have the correct password, cipher, hash algorithm, and volume type // Check the version required to handle this volume if (cryptoInfo->RequiredProgramVersion > VERSION_NUM) { status = ERR_NEW_VERSION_REQUIRED; goto err; } // Header version cryptoInfo->HeaderVersion = headerVersion; // Volume creation time (legacy) cryptoInfo->volume_creation_time = GetHeaderField64 (header, TC_HEADER_OFFSET_VOLUME_CREATION_TIME).Value; // Header creation time (legacy) cryptoInfo->header_creation_time = GetHeaderField64 (header, TC_HEADER_OFFSET_MODIFICATION_TIME).Value; // Hidden volume size (if any) cryptoInfo->hiddenVolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_HIDDEN_VOLUME_SIZE).Value; // Hidden volume status cryptoInfo->hiddenVolume = (cryptoInfo->hiddenVolumeSize != 0); // Volume size cryptoInfo->VolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_VOLUME_SIZE); // Encrypted area size and length cryptoInfo->EncryptedAreaStart = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_START); cryptoInfo->EncryptedAreaLength = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH); // Flags cryptoInfo->HeaderFlags = GetHeaderField32 (header, TC_HEADER_OFFSET_FLAGS); // Sector size if (headerVersion >= 5) cryptoInfo->SectorSize = GetHeaderField32 (header, TC_HEADER_OFFSET_SECTOR_SIZE); else cryptoInfo->SectorSize = TC_SECTOR_SIZE_LEGACY; if (cryptoInfo->SectorSize < TC_MIN_VOLUME_SECTOR_SIZE || cryptoInfo->SectorSize > TC_MAX_VOLUME_SECTOR_SIZE || cryptoInfo->SectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) { status = ERR_PARAMETER_INCORRECT; goto err; } // Preserve scheduled header keys if requested if (retHeaderCryptoInfo) { if (retInfo == NULL) { cryptoInfo->pkcs5 = pkcs5_prf; cryptoInfo->noIterations = keyInfo.noIterations; goto ret; } cryptoInfo = *retInfo = crypto_open (); if (cryptoInfo == NULL) { status = ERR_OUTOFMEMORY; goto err; } memcpy (cryptoInfo, retHeaderCryptoInfo, sizeof (*cryptoInfo)); } // Master key data memcpy (keyInfo.master_keydata, header + HEADER_MASTER_KEYDATA_OFFSET, MASTER_KEYDATA_SIZE); memcpy (cryptoInfo->master_keydata, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); // PKCS #5 memcpy (cryptoInfo->salt, keyInfo.salt, PKCS5_SALT_SIZE); cryptoInfo->pkcs5 = pkcs5_prf; cryptoInfo->noIterations = keyInfo.noIterations; // Init the cipher with the decrypted master key status = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; switch (cryptoInfo->mode) { case LRW: case CBC: case INNER_CBC: case OUTER_CBC: // For LRW (deprecated/legacy), the tweak key // For CBC (deprecated/legacy), the IV/whitening seed memcpy (cryptoInfo->k2, keyInfo.master_keydata, LEGACY_VOL_IV_SIZE); break; default: // The secondary master key (if cascade, multiple concatenated) memcpy (cryptoInfo->k2, keyInfo.master_keydata + EAGetKeySize (cryptoInfo->ea), EAGetKeySize (cryptoInfo->ea)); } if (!EAInitMode (cryptoInfo)) { status = ERR_MODE_INIT_FAILED; goto err; } status = ERR_SUCCESS; goto ret; } } } status = ERR_PASSWORD_WRONG; err: if (cryptoInfo != retHeaderCryptoInfo) { crypto_close(cryptoInfo); *retInfo = NULL; } ret: burn (&keyInfo, sizeof (keyInfo)); burn (dk, sizeof(dk)); #ifndef DEVICE_DRIVER VirtualUnlock (&keyInfo, sizeof (keyInfo)); VirtualUnlock (&dk, sizeof (dk)); #endif if (encryptionThreadCount > 1) { TC_WAIT_EVENT (noOutstandingWorkItemEvent); burn (keyDerivationWorkItems, sizeof (KeyDerivationWorkItem) * pkcs5PrfCount); TCfree (keyDerivationWorkItems); #ifndef DEVICE_DRIVER CloseHandle (keyDerivationCompletedEvent); CloseHandle (noOutstandingWorkItemEvent); #endif } return status; }
int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo) { #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE char dk[32 * 2]; // 2 * 256-bit key #else char dk[32 * 2 * 3]; // 6 * 256-bit key #endif PCRYPTO_INFO cryptoInfo; int status = ERR_SUCCESS; uint32 iterations = pim; iterations <<= 16; iterations |= bBoot; if (retHeaderCryptoInfo != NULL) cryptoInfo = retHeaderCryptoInfo; else cryptoInfo = *retInfo = crypto_open (); // PKCS5 PRF #ifdef TC_WINDOWS_BOOT_SHA2 derive_key_sha256 (password->Text, (int) password->Length, header + HEADER_SALT_OFFSET, PKCS5_SALT_SIZE, iterations, dk, sizeof (dk)); #else derive_key_ripemd160 (password->Text, (int) password->Length, header + HEADER_SALT_OFFSET, PKCS5_SALT_SIZE, iterations, dk, sizeof (dk)); #endif // Mode of operation cryptoInfo->mode = FIRST_MODE_OF_OPERATION_ID; #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE cryptoInfo->ea = 1; #else // Test all available encryption algorithms for (cryptoInfo->ea = EAGetFirst (); cryptoInfo->ea != 0; cryptoInfo->ea = EAGetNext (cryptoInfo->ea)) #endif { #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE #if defined (TC_WINDOWS_BOOT_SERPENT) serpent_set_key (dk, cryptoInfo->ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *) cryptoInfo->ks, (const u4byte *) dk); #elif defined (TC_WINDOWS_BOOT_CAMELLIA) camellia_set_key (dk, cryptoInfo->ks); #else status = EAInit (dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; #endif #else status = EAInit (cryptoInfo->ea, dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; #endif // Secondary key schedule #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE #if defined (TC_WINDOWS_BOOT_SERPENT) serpent_set_key (dk + 32, cryptoInfo->ks2); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *)cryptoInfo->ks2, (const u4byte *) (dk + 32)); #elif defined (TC_WINDOWS_BOOT_CAMELLIA) camellia_set_key (dk + 32, cryptoInfo->ks2); #else EAInit (dk + 32, cryptoInfo->ks2); #endif #else EAInit (cryptoInfo->ea, dk + EAGetKeySize (cryptoInfo->ea), cryptoInfo->ks2); #endif // Try to decrypt header DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); // Check magic 'VERA' and CRC-32 of header fields and master keydata if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241 || (GetHeaderField16 (header, TC_HEADER_OFFSET_VERSION) >= 4 && GetHeaderField32 (header, TC_HEADER_OFFSET_HEADER_CRC) != GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC)) || GetHeaderField32 (header, TC_HEADER_OFFSET_KEY_AREA_CRC) != GetCrc32 (header + HEADER_MASTER_KEYDATA_OFFSET, MASTER_KEYDATA_SIZE)) { EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE status = ERR_PASSWORD_WRONG; goto err; #else continue; #endif } // Header decrypted status = 0; // Hidden volume status cryptoInfo->VolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_HIDDEN_VOLUME_SIZE); cryptoInfo->hiddenVolume = (cryptoInfo->VolumeSize.LowPart != 0 || cryptoInfo->VolumeSize.HighPart != 0); // Volume size cryptoInfo->VolumeSize = GetHeaderField64 (header, TC_HEADER_OFFSET_VOLUME_SIZE); // Encrypted area size and length cryptoInfo->EncryptedAreaStart = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_START); cryptoInfo->EncryptedAreaLength = GetHeaderField64 (header, TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH); // Flags cryptoInfo->HeaderFlags = GetHeaderField32 (header, TC_HEADER_OFFSET_FLAGS); #ifdef TC_WINDOWS_BOOT_SHA2 cryptoInfo->pkcs5 = SHA256; #else cryptoInfo->pkcs5 = RIPEMD160; #endif memcpy (dk, header + HEADER_MASTER_KEYDATA_OFFSET, sizeof (dk)); EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); if (retHeaderCryptoInfo) goto ret; // Init the encryption algorithm with the decrypted master key #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE #if defined (TC_WINDOWS_BOOT_SERPENT) serpent_set_key (dk, cryptoInfo->ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *) cryptoInfo->ks, (const u4byte *) dk); #elif defined (TC_WINDOWS_BOOT_CAMELLIA) camellia_set_key (dk, cryptoInfo->ks); #else status = EAInit (dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; #endif #else status = EAInit (cryptoInfo->ea, dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) goto err; #endif // The secondary master key (if cascade, multiple concatenated) #ifdef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE #if defined (TC_WINDOWS_BOOT_SERPENT) serpent_set_key (dk + 32, cryptoInfo->ks2); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *)cryptoInfo->ks2, (const u4byte *) (dk + 32)); #elif defined (TC_WINDOWS_BOOT_CAMELLIA) camellia_set_key (dk + 32, cryptoInfo->ks2); #else EAInit (dk + 32, cryptoInfo->ks2); #endif #else EAInit (cryptoInfo->ea, dk + EAGetKeySize (cryptoInfo->ea), cryptoInfo->ks2); #endif goto ret; } status = ERR_PASSWORD_WRONG; err: if (cryptoInfo != retHeaderCryptoInfo) { crypto_close(cryptoInfo); *retInfo = NULL; } ret: burn (dk, sizeof(dk)); return status; }
NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT *mount, PWSTR pwszMountVolume, BOOL bRawDevice) { FILE_STANDARD_INFORMATION FileStandardInfo; FILE_BASIC_INFORMATION FileBasicInfo; OBJECT_ATTRIBUTES oaFileAttributes; UNICODE_STRING FullFileName; IO_STATUS_BLOCK IoStatusBlock; PCRYPTO_INFO cryptoInfoPtr = NULL; PCRYPTO_INFO tmpCryptoInfo = NULL; LARGE_INTEGER lDiskLength; __int64 partitionStartingOffset = 0; int volumeType; char *readBuffer = 0; NTSTATUS ntStatus = 0; BOOL forceAccessCheck = (!bRawDevice && !(OsMajorVersion == 5 &&OsMinorVersion == 0)); // Windows 2000 does not support OBJ_FORCE_ACCESS_CHECK attribute BOOL disableBuffering = TRUE; BOOL exclusiveAccess = mount->bExclusiveAccess; Extension->pfoDeviceFile = NULL; Extension->hDeviceFile = NULL; Extension->bTimeStampValid = FALSE; RtlInitUnicodeString (&FullFileName, pwszMountVolume); InitializeObjectAttributes (&oaFileAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | (forceAccessCheck ? OBJ_FORCE_ACCESS_CHECK : 0) | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent (&Extension->keVolumeEvent, NotificationEvent, FALSE); if (Extension->SecurityClientContextValid) { ntStatus = SeImpersonateClientEx (&Extension->SecurityClientContext, NULL); if (!NT_SUCCESS (ntStatus)) goto error; } mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = FALSE; // If we are opening a device, query its size first if (bRawDevice) { PARTITION_INFORMATION pi; PARTITION_INFORMATION_EX pix; LARGE_INTEGER diskLengthInfo; DISK_GEOMETRY dg; STORAGE_PROPERTY_QUERY storagePropertyQuery = {0}; STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR storageDescriptor = {0}; ntStatus = IoGetDeviceObjectPointer (&FullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES, &Extension->pfoDeviceFile, &Extension->pFsdDevice); if (!NT_SUCCESS (ntStatus)) goto error; ntStatus = TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_DRIVE_GEOMETRY, (char *) &dg, sizeof (dg)); if (!NT_SUCCESS (ntStatus)) goto error; lDiskLength.QuadPart = dg.Cylinders.QuadPart * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.BytesPerSector; Extension->HostBytesPerSector = dg.BytesPerSector; storagePropertyQuery.PropertyId = StorageAccessAlignmentProperty; storagePropertyQuery.QueryType = PropertyStandardQuery; /* IOCTL_STORAGE_QUERY_PROPERTY supported only on Vista and above */ if (NT_SUCCESS (TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IOCTL_STORAGE_QUERY_PROPERTY, (char*) &storagePropertyQuery, sizeof(storagePropertyQuery), (char *) &storageDescriptor, sizeof (storageDescriptor)))) { Extension->HostBytesPerPhysicalSector = storageDescriptor.BytesPerPhysicalSector; } else { Extension->HostBytesPerPhysicalSector = dg.BytesPerSector; } // Drive geometry is used only when IOCTL_DISK_GET_PARTITION_INFO fails if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO_EX, (char *) &pix, sizeof (pix)))) { lDiskLength.QuadPart = pix.PartitionLength.QuadPart; partitionStartingOffset = pix.StartingOffset.QuadPart; } // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO, (char *) &pi, sizeof (pi)))) { lDiskLength.QuadPart = pi.PartitionLength.QuadPart; partitionStartingOffset = pi.StartingOffset.QuadPart; } else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_LENGTH_INFO, &diskLengthInfo, sizeof (diskLengthInfo)))) { lDiskLength = diskLengthInfo; } ProbingHostDeviceForWrite = TRUE; if (!mount->bMountReadOnly && TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IsHiddenSystemRunning() ? TC_IOCTL_DISK_IS_WRITABLE : IOCTL_DISK_IS_WRITABLE, NULL, 0) == STATUS_MEDIA_WRITE_PROTECTED) { mount->bMountReadOnly = TRUE; DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = TRUE; } ProbingHostDeviceForWrite = FALSE; // Some Windows tools (e.g. diskmgmt, diskpart, vssadmin) fail or experience timeouts when there is a raw device // open for exclusive access. Therefore, exclusive access is used only for file-hosted volumes. // Applications requiring a consistent device image need to acquire exclusive write access first. This is prevented // when a device-hosted volume is mounted. exclusiveAccess = FALSE; } else { // Limit the maximum required buffer size if (mount->BytesPerSector > 128 * BYTES_PER_KB) { ntStatus = STATUS_INVALID_PARAMETER; goto error; } Extension->HostBytesPerSector = mount->BytesPerSector; Extension->HostBytesPerPhysicalSector = mount->BytesPerPhysicalSector; if (Extension->HostBytesPerSector != TC_SECTOR_SIZE_FILE_HOSTED_VOLUME) disableBuffering = FALSE; } // Open the volume hosting file/device if (!mount->bMountReadOnly) { ntStatus = ZwCreateFile (&Extension->hDeviceFile, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &oaFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, exclusiveAccess ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); } /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ if (ntStatus == 0xc000001b) ntStatus = STATUS_ACCESS_DENIED; mount->VolumeMountedReadOnlyAfterAccessDenied = FALSE; if (mount->bMountReadOnly || ntStatus == STATUS_ACCESS_DENIED) { ntStatus = ZwCreateFile (&Extension->hDeviceFile, GENERIC_READ | SYNCHRONIZE, &oaFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, exclusiveAccess ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (NT_SUCCESS (ntStatus) && !mount->bMountReadOnly) mount->VolumeMountedReadOnlyAfterAccessDenied = TRUE; Extension->bReadOnly = TRUE; DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; } else Extension->bReadOnly = FALSE; /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ if (ntStatus == 0xc000001b) { /* Partitions which return this code can still be opened with FILE_SHARE_READ but this causes NT problems elsewhere in particular if you do FILE_SHARE_READ NT will die later if anyone even tries to open the partition (or file for that matter...) */ ntStatus = STATUS_SHARING_VIOLATION; } if (!NT_SUCCESS (ntStatus)) { goto error; } // If we have opened a file, query its size now if (bRawDevice == FALSE) { ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, &IoStatusBlock, &FileBasicInfo, sizeof (FileBasicInfo), FileBasicInformation); if (NT_SUCCESS (ntStatus)) { if (mount->bPreserveTimestamp) { Extension->fileCreationTime = FileBasicInfo.CreationTime; Extension->fileLastAccessTime = FileBasicInfo.LastAccessTime; Extension->fileLastWriteTime = FileBasicInfo.LastWriteTime; Extension->fileLastChangeTime = FileBasicInfo.ChangeTime; Extension->bTimeStampValid = TRUE; } ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, &IoStatusBlock, &FileStandardInfo, sizeof (FileStandardInfo), FileStandardInformation); } if (!NT_SUCCESS (ntStatus)) { Dump ("ZwQueryInformationFile failed while opening file: NTSTATUS 0x%08x\n", ntStatus); goto error; } lDiskLength.QuadPart = FileStandardInfo.EndOfFile.QuadPart; if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED) { Dump ("File \"%ls\" is marked as compressed - not supported!\n", pwszMountVolume); mount->nReturnCode = ERR_COMPRESSION_NOT_SUPPORTED; ntStatus = STATUS_SUCCESS; goto error; } ntStatus = ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &Extension->pfoDeviceFile, 0); if (!NT_SUCCESS (ntStatus)) { goto error; } /* Get the FSD device for the file (probably either NTFS or FAT) */ Extension->pFsdDevice = IoGetRelatedDeviceObject (Extension->pfoDeviceFile); } else { // Try to gain "raw" access to the partition in case there is a live filesystem on it (otherwise, // the NTFS driver guards hidden sectors and prevents mounting using a backup header e.g. after the user // accidentally quick-formats a dismounted partition-hosted TrueCrypt volume as NTFS). PFILE_OBJECT pfoTmpDeviceFile = NULL; if (NT_SUCCESS (ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &pfoTmpDeviceFile, NULL)) && pfoTmpDeviceFile != NULL) { TCFsctlCall (pfoTmpDeviceFile, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0); ObDereferenceObject (pfoTmpDeviceFile); } } // Check volume size if (lDiskLength.QuadPart < TC_MIN_VOLUME_SIZE_LEGACY || lDiskLength.QuadPart > TC_MAX_VOLUME_SIZE) { mount->nReturnCode = ERR_VOL_SIZE_WRONG; ntStatus = STATUS_SUCCESS; goto error; } Extension->DiskLength = lDiskLength.QuadPart; Extension->HostLength = lDiskLength.QuadPart; readBuffer = TCalloc (max (max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, PAGE_SIZE), Extension->HostBytesPerSector)); if (readBuffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto error; } // Go through all volume types (e.g., normal, hidden) for (volumeType = TC_VOLUME_TYPE_NORMAL; volumeType < TC_VOLUME_TYPE_COUNT; volumeType++) { Dump ("Trying to open volume type %d\n", volumeType); /* Read the volume header */ if (!mount->bPartitionInInactiveSysEncScope || (mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_HIDDEN)) { // Header of a volume that is not within the scope of system encryption, or // header of a system hidden volume (containing a hidden OS) LARGE_INTEGER headerOffset; if (mount->UseBackupHeader && lDiskLength.QuadPart <= TC_TOTAL_VOLUME_HEADERS_SIZE) continue; switch (volumeType) { case TC_VOLUME_TYPE_NORMAL: headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_VOLUME_HEADER_GROUP_SIZE : TC_VOLUME_HEADER_OFFSET; break; case TC_VOLUME_TYPE_HIDDEN: if (lDiskLength.QuadPart <= TC_VOLUME_HEADER_GROUP_SIZE) continue; headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_HIDDEN_VOLUME_HEADER_OFFSET : TC_HIDDEN_VOLUME_HEADER_OFFSET; break; } Dump ("Reading volume header at %I64d\n", headerOffset.QuadPart); ntStatus = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &IoStatusBlock, readBuffer, bRawDevice ? max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector) : TC_VOLUME_HEADER_EFFECTIVE_SIZE, &headerOffset, NULL); } else { // Header of a partition that is within the scope of system encryption WCHAR parentDrivePath [47+1] = {0}; HANDLE hParentDeviceFile = NULL; UNICODE_STRING FullParentPath; OBJECT_ATTRIBUTES oaParentFileAttributes; LARGE_INTEGER parentKeyDataOffset; RtlStringCbPrintfW (parentDrivePath, sizeof (parentDrivePath), WIDE ("\\Device\\Harddisk%d\\Partition0"), mount->nPartitionInInactiveSysEncScopeDriveNo); Dump ("Mounting partition within scope of system encryption (reading key data from: %ls)\n", parentDrivePath); RtlInitUnicodeString (&FullParentPath, parentDrivePath); InitializeObjectAttributes (&oaParentFileAttributes, &FullParentPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ntStatus = ZwCreateFile (&hParentDeviceFile, GENERIC_READ | SYNCHRONIZE, &oaParentFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | FILE_NO_INTERMEDIATE_BUFFERING | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS (ntStatus)) { if (hParentDeviceFile != NULL) ZwClose (hParentDeviceFile); Dump ("Cannot open %ls\n", parentDrivePath); goto error; } parentKeyDataOffset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; ntStatus = ZwReadFile (hParentDeviceFile, NULL, NULL, NULL, &IoStatusBlock, readBuffer, max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector), &parentKeyDataOffset, NULL); if (hParentDeviceFile != NULL) ZwClose (hParentDeviceFile); } if (!NT_SUCCESS (ntStatus) && ntStatus != STATUS_END_OF_FILE) { Dump ("Read failed: NTSTATUS 0x%08x\n", ntStatus); goto error; } if (ntStatus == STATUS_END_OF_FILE || IoStatusBlock.Information < TC_VOLUME_HEADER_EFFECTIVE_SIZE) { Dump ("Read didn't read enough data\n"); // If FSCTL_ALLOW_EXTENDED_DASD_IO failed and there is a live filesystem on the partition, then the // filesystem driver may report EOF when we are reading hidden sectors (when the filesystem is // shorter than the partition). This can happen for example after the user quick-formats a dismounted // partition-hosted TrueCrypt volume and then tries to mount the volume using the embedded backup header. memset (readBuffer, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); } /* Attempt to recognize the volume (decrypt the header) */ ReadVolumeHeaderRecoveryMode = mount->RecoveryMode; if ((volumeType == TC_VOLUME_TYPE_HIDDEN) && mount->bProtectHiddenVolume) { mount->nReturnCode = ReadVolumeHeaderWCache ( FALSE, mount->bCache, mount->bCachePim, readBuffer, &mount->ProtectedHidVolPassword, mount->ProtectedHidVolPkcs5Prf, mount->ProtectedHidVolPim, mount->bTrueCryptMode, &tmpCryptoInfo); } else { mount->nReturnCode = ReadVolumeHeaderWCache ( mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_NORMAL, mount->bCache, mount->bCachePim, readBuffer, &mount->VolumePassword, mount->pkcs5_prf, mount->VolumePim, mount->bTrueCryptMode, &Extension->cryptoInfo); } ReadVolumeHeaderRecoveryMode = FALSE; if (mount->nReturnCode == 0 || mount->nReturnCode == ERR_CIPHER_INIT_WEAK_KEY) { /* Volume header successfully decrypted */ if (!Extension->cryptoInfo) { /* should never happen */ mount->nReturnCode = ERR_OUTOFMEMORY; ntStatus = STATUS_SUCCESS; goto error; } Dump ("Volume header decrypted\n"); Dump ("Required program version = %x\n", (int) Extension->cryptoInfo->RequiredProgramVersion); Dump ("Legacy volume = %d\n", (int) Extension->cryptoInfo->LegacyVolume); if (IsHiddenSystemRunning() && !Extension->cryptoInfo->hiddenVolume) { Extension->bReadOnly = mount->bMountReadOnly = TRUE; HiddenSysLeakProtectionCount++; } Extension->cryptoInfo->bProtectHiddenVolume = FALSE; Extension->cryptoInfo->bHiddenVolProtectionAction = FALSE; Extension->cryptoInfo->bPartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; /* compute the ID of this volume: SHA-512 of the effective header */ sha256 (Extension->volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); if (volumeType == TC_VOLUME_TYPE_NORMAL) { if (mount->bPartitionInInactiveSysEncScope) { if (Extension->cryptoInfo->EncryptedAreaStart.Value > (unsigned __int64) partitionStartingOffset || Extension->cryptoInfo->EncryptedAreaStart.Value + Extension->cryptoInfo->VolumeSize.Value <= (unsigned __int64) partitionStartingOffset) { // The partition is not within the key scope of system encryption mount->nReturnCode = ERR_PASSWORD_WRONG; ntStatus = STATUS_SUCCESS; goto error; } if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) { // Partial encryption is not supported for volumes mounted as regular mount->nReturnCode = ERR_ENCRYPTION_NOT_COMPLETED; ntStatus = STATUS_SUCCESS; goto error; } } else if (Extension->cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) { if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) { // Non-system in-place encryption process has not been completed on this volume mount->nReturnCode = ERR_NONSYS_INPLACE_ENC_INCOMPLETE; ntStatus = STATUS_SUCCESS; goto error; } } } Extension->cryptoInfo->FirstDataUnitNo.Value = 0; if (Extension->cryptoInfo->hiddenVolume && IsHiddenSystemRunning()) { // Prevent mount of a hidden system partition if the system hosted on it is currently running if (memcmp (Extension->cryptoInfo->master_keydata, GetSystemDriveCryptoInfo()->master_keydata, EAGetKeySize (Extension->cryptoInfo->ea)) == 0) { mount->nReturnCode = ERR_VOL_ALREADY_MOUNTED; ntStatus = STATUS_SUCCESS; goto error; } } switch (volumeType) { case TC_VOLUME_TYPE_NORMAL: Extension->cryptoInfo->hiddenVolume = FALSE; if (mount->bPartitionInInactiveSysEncScope) { Extension->cryptoInfo->volDataAreaOffset = 0; Extension->DiskLength = lDiskLength.QuadPart; Extension->cryptoInfo->FirstDataUnitNo.Value = partitionStartingOffset / ENCRYPTION_DATA_UNIT_SIZE; } else if (Extension->cryptoInfo->LegacyVolume) { Extension->cryptoInfo->volDataAreaOffset = TC_VOLUME_HEADER_SIZE_LEGACY; Extension->DiskLength = lDiskLength.QuadPart - TC_VOLUME_HEADER_SIZE_LEGACY; } else { Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->EncryptedAreaStart.Value; Extension->DiskLength = Extension->cryptoInfo->VolumeSize.Value; } break; case TC_VOLUME_TYPE_HIDDEN: cryptoInfoPtr = mount->bProtectHiddenVolume ? tmpCryptoInfo : Extension->cryptoInfo; Extension->cryptoInfo->hiddenVolumeOffset = cryptoInfoPtr->EncryptedAreaStart.Value; Dump ("Hidden volume offset = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset); Dump ("Hidden volume size = %I64d\n", cryptoInfoPtr->hiddenVolumeSize); Dump ("Hidden volume end = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset + cryptoInfoPtr->hiddenVolumeSize - 1); // Validate the offset if (Extension->cryptoInfo->hiddenVolumeOffset % ENCRYPTION_DATA_UNIT_SIZE != 0) { mount->nReturnCode = ERR_VOL_SIZE_WRONG; ntStatus = STATUS_SUCCESS; goto error; } // If we are supposed to actually mount the hidden volume (not just to protect it) if (!mount->bProtectHiddenVolume) { Extension->DiskLength = cryptoInfoPtr->hiddenVolumeSize; Extension->cryptoInfo->hiddenVolume = TRUE; Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->hiddenVolumeOffset; } else { // Hidden volume protection Extension->cryptoInfo->hiddenVolume = FALSE; Extension->cryptoInfo->bProtectHiddenVolume = TRUE; Extension->cryptoInfo->hiddenVolumeProtectedSize = tmpCryptoInfo->hiddenVolumeSize; Dump ("Hidden volume protection active: %I64d-%I64d (%I64d)\n", Extension->cryptoInfo->hiddenVolumeOffset, Extension->cryptoInfo->hiddenVolumeProtectedSize + Extension->cryptoInfo->hiddenVolumeOffset - 1, Extension->cryptoInfo->hiddenVolumeProtectedSize); } break; } Dump ("Volume data offset = %I64d\n", Extension->cryptoInfo->volDataAreaOffset); Dump ("Volume data size = %I64d\n", Extension->DiskLength); Dump ("Volume data end = %I64d\n", Extension->cryptoInfo->volDataAreaOffset + Extension->DiskLength - 1); if (Extension->DiskLength == 0) { Dump ("Incorrect volume size\n"); continue; } // If this is a hidden volume, make sure we are supposed to actually // mount it (i.e. not just to protect it) if (volumeType == TC_VOLUME_TYPE_NORMAL || !mount->bProtectHiddenVolume) { // Validate sector size if (bRawDevice && Extension->cryptoInfo->SectorSize != Extension->HostBytesPerSector) { mount->nReturnCode = ERR_PARAMETER_INCORRECT; ntStatus = STATUS_SUCCESS; goto error; } // Calculate virtual volume geometry Extension->TracksPerCylinder = 1; Extension->SectorsPerTrack = 1; Extension->BytesPerSector = Extension->cryptoInfo->SectorSize; Extension->NumberOfCylinders = Extension->DiskLength / Extension->BytesPerSector; Extension->PartitionType = 0; Extension->bRawDevice = bRawDevice; memset (Extension->wszVolume, 0, sizeof (Extension->wszVolume)); if (wcsstr (pwszMountVolume, WIDE ("\\??\\UNC\\")) == pwszMountVolume) { /* UNC path */ RtlStringCbPrintfW (Extension->wszVolume, sizeof (Extension->wszVolume), WIDE ("\\??\\\\%s"), pwszMountVolume + 7); } else { RtlStringCbCopyW (Extension->wszVolume, sizeof(Extension->wszVolume),pwszMountVolume); } memset (Extension->wszLabel, 0, sizeof (Extension->wszLabel)); RtlStringCbCopyW (Extension->wszLabel, sizeof(Extension->wszLabel), mount->wszLabel); } // If we are to protect a hidden volume we cannot exit yet, for we must also // decrypt the hidden volume header. if (!(volumeType == TC_VOLUME_TYPE_NORMAL && mount->bProtectHiddenVolume)) { TCfree (readBuffer); if (tmpCryptoInfo != NULL) { crypto_close (tmpCryptoInfo); tmpCryptoInfo = NULL; } return STATUS_SUCCESS; } } else if ((mount->bProtectHiddenVolume && volumeType == TC_VOLUME_TYPE_NORMAL) || mount->nReturnCode != ERR_PASSWORD_WRONG) { /* If we are not supposed to protect a hidden volume, the only error that is tolerated is ERR_PASSWORD_WRONG (to allow mounting a possible hidden volume). If we _are_ supposed to protect a hidden volume, we do not tolerate any error (both volume headers must be successfully decrypted). */ break; } } /* Failed due to some non-OS reason so we drop through and return NT SUCCESS then nReturnCode is checked later in user-mode */ if (mount->nReturnCode == ERR_OUTOFMEMORY) ntStatus = STATUS_INSUFFICIENT_RESOURCES; else ntStatus = STATUS_SUCCESS; error: if (mount->nReturnCode == ERR_SUCCESS) mount->nReturnCode = ERR_PASSWORD_WRONG; if (tmpCryptoInfo != NULL) { crypto_close (tmpCryptoInfo); tmpCryptoInfo = NULL; } if (Extension->cryptoInfo) { crypto_close (Extension->cryptoInfo); Extension->cryptoInfo = NULL; } if (Extension->bTimeStampValid) { RestoreTimeStamp (Extension); } /* Close the hDeviceFile */ if (Extension->hDeviceFile != NULL) ZwClose (Extension->hDeviceFile); /* The cryptoInfo pointer is deallocated if the readheader routines fail so there is no need to deallocate here */ /* Dereference the user-mode file object */ if (Extension->pfoDeviceFile != NULL) ObDereferenceObject (Extension->pfoDeviceFile); /* Free the tmp IO buffers */ if (readBuffer != NULL) TCfree (readBuffer); return ntStatus; }
int ChangePwd (const char *lpszVolume, Password *oldPassword, int old_pkcs5, int old_pim, BOOL truecryptMode, Password *newPassword, int pkcs5, int pim, int wipePassCount, HWND hwndDlg) { int nDosLinkCreated = 1, nStatus = ERR_OS_ERROR; char szDiskFile[TC_MAX_PATH], szCFDevice[TC_MAX_PATH]; char szDosDevice[TC_MAX_PATH]; char buffer[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; PCRYPTO_INFO cryptoInfo = NULL, ci = NULL; void *dev = INVALID_HANDLE_VALUE; DWORD dwError; DWORD bytesRead; BOOL bDevice; unsigned __int64 hostSize = 0; int volumeType; int wipePass; FILETIME ftCreationTime; FILETIME ftLastWriteTime; FILETIME ftLastAccessTime; BOOL bTimeStampValid = FALSE; LARGE_INTEGER headerOffset; BOOL backupHeader; DISK_GEOMETRY driveInfo; if (oldPassword->Length == 0 || newPassword->Length == 0) return -1; if ((wipePassCount <= 0) || (truecryptMode && (old_pkcs5 == SHA256))) { nStatus = ERR_PARAMETER_INCORRECT; handleError (hwndDlg, nStatus, SRC_POS); return nStatus; } if (!lpszVolume) { nStatus = ERR_OUTOFMEMORY; handleError (hwndDlg, nStatus, SRC_POS); return nStatus; } WaitCursor (); CreateFullVolumePath (szDiskFile, sizeof(szDiskFile), lpszVolume, &bDevice); if (bDevice == FALSE) { strcpy (szCFDevice, szDiskFile); } else { nDosLinkCreated = FakeDosNameForDevice (szDiskFile, szDosDevice, sizeof(szDosDevice), szCFDevice, sizeof(szCFDevice),FALSE); if (nDosLinkCreated != 0) goto error; } dev = CreateFile (szCFDevice, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (dev == INVALID_HANDLE_VALUE) goto error; if (bDevice) { /* This is necessary to determine the hidden volume header offset */ if (dev == INVALID_HANDLE_VALUE) { goto error; } else { PARTITION_INFORMATION diskInfo; DWORD dwResult; BOOL bResult; bResult = DeviceIoControl (dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveInfo, sizeof (driveInfo), &dwResult, NULL); if (!bResult) goto error; bResult = GetPartitionInfo (lpszVolume, &diskInfo); if (bResult) { hostSize = diskInfo.PartitionLength.QuadPart; } else { hostSize = driveInfo.Cylinders.QuadPart * driveInfo.BytesPerSector * driveInfo.SectorsPerTrack * driveInfo.TracksPerCylinder; } if (hostSize == 0) { nStatus = ERR_VOL_SIZE_WRONG; goto error; } } } else { LARGE_INTEGER fileSize; if (!GetFileSizeEx (dev, &fileSize)) { nStatus = ERR_OS_ERROR; goto error; } hostSize = fileSize.QuadPart; } if (Randinit ()) { if (CryptoAPILastError == ERROR_SUCCESS) nStatus = ERR_RAND_INIT_FAILED; else nStatus = ERR_CAPI_INIT_FAILED; goto error; } SetRandomPoolEnrichedByUserStatus (FALSE); /* force the display of the random enriching dialog */ if (!bDevice && bPreserveTimestamp) { if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) bTimeStampValid = FALSE; else bTimeStampValid = TRUE; } for (volumeType = TC_VOLUME_TYPE_NORMAL; volumeType < TC_VOLUME_TYPE_COUNT; volumeType++) { // Seek the volume header switch (volumeType) { case TC_VOLUME_TYPE_NORMAL: headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET; break; case TC_VOLUME_TYPE_HIDDEN: if (TC_HIDDEN_VOLUME_HEADER_OFFSET + TC_VOLUME_HEADER_SIZE > hostSize) continue; headerOffset.QuadPart = TC_HIDDEN_VOLUME_HEADER_OFFSET; break; } if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } /* Read in volume header */ if (!ReadEffectiveVolumeHeader (bDevice, dev, buffer, &bytesRead)) { nStatus = ERR_OS_ERROR; goto error; } if (bytesRead != sizeof (buffer)) { // Windows may report EOF when reading sectors from the last cluster of a device formatted as NTFS memset (buffer, 0, sizeof (buffer)); } /* Try to decrypt the header */ nStatus = ReadVolumeHeader (FALSE, buffer, oldPassword, old_pkcs5, old_pim, truecryptMode, &cryptoInfo, NULL); if (nStatus == ERR_CIPHER_INIT_WEAK_KEY) nStatus = 0; // We can ignore this error here if (nStatus == ERR_PASSWORD_WRONG) { continue; // Try next volume type } else if (nStatus != 0) { cryptoInfo = NULL; goto error; } else break; } if (nStatus != 0) { cryptoInfo = NULL; goto error; } if (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM) { nStatus = ERR_SYS_HIDVOL_HEAD_REENC_MODE_WRONG; goto error; } // Change the PKCS-5 PRF if requested by user if (pkcs5 != 0) cryptoInfo->pkcs5 = pkcs5; RandSetHashFunction (cryptoInfo->pkcs5); NormalCursor(); UserEnrichRandomPool (hwndDlg); EnableElevatedCursorChange (hwndDlg); WaitCursor(); /* Re-encrypt the volume header */ backupHeader = FALSE; while (TRUE) { /* The header will be re-encrypted wipePassCount times to prevent adversaries from using techniques such as magnetic force microscopy or magnetic force scanning tunnelling microscopy to recover the overwritten header. According to Peter Gutmann, data should be overwritten 22 times (ideally, 35 times) using non-random patterns and pseudorandom data. However, as users might impatiently interupt the process (etc.) we will not use the Gutmann's patterns but will write the valid re-encrypted header, i.e. pseudorandom data, and there will be many more passes than Guttman recommends. During each pass we will write a valid working header. Each pass will use the same master key, and also the same header key, secondary key (XTS), etc., derived from the new password. The only item that will be different for each pass will be the salt. This is sufficient to cause each "version" of the header to differ substantially and in a random manner from the versions written during the other passes. */ for (wipePass = 0; wipePass < wipePassCount; wipePass++) { // Prepare new volume header nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, buffer, cryptoInfo->ea, cryptoInfo->mode, newPassword, cryptoInfo->pkcs5, pim, cryptoInfo->master_keydata, &ci, cryptoInfo->VolumeSize.Value, (volumeType == TC_VOLUME_TYPE_HIDDEN) ? cryptoInfo->hiddenVolumeSize : 0, cryptoInfo->EncryptedAreaStart.Value, cryptoInfo->EncryptedAreaLength.Value, truecryptMode? 0 : cryptoInfo->RequiredProgramVersion, cryptoInfo->HeaderFlags, cryptoInfo->SectorSize, wipePass < wipePassCount - 1); if (ci != NULL) crypto_close (ci); if (nStatus != 0) goto error; if (!SetFilePointerEx ((HANDLE) dev, headerOffset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto error; } if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer)) { nStatus = ERR_OS_ERROR; goto error; } if (bDevice && !cryptoInfo->LegacyVolume && !cryptoInfo->hiddenVolume && cryptoInfo->HeaderVersion == 4 && (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) != 0 && (cryptoInfo->HeaderFlags & ~TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0) { nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, cryptoInfo->VolumeSize.Value, !backupHeader, backupHeader); if (nStatus != ERR_SUCCESS) goto error; } FlushFileBuffers (dev); } if (backupHeader || cryptoInfo->LegacyVolume) break; backupHeader = TRUE; headerOffset.QuadPart += hostSize - TC_VOLUME_HEADER_GROUP_SIZE; } /* Password successfully changed */ nStatus = 0; error: dwError = GetLastError (); burn (buffer, sizeof (buffer)); if (cryptoInfo != NULL) crypto_close (cryptoInfo); if (bTimeStampValid) SetFileTime (dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime); if (dev != INVALID_HANDLE_VALUE) CloseHandle ((HANDLE) dev); if (nDosLinkCreated == 0) RemoveFakeDosName (szDiskFile, szDosDevice); RandStop (FALSE); NormalCursor (); SetLastError (dwError); if (nStatus == ERR_OS_ERROR && dwError == ERROR_ACCESS_DENIED && bDevice && !UacElevated && IsUacSupported ()) return nStatus; if (nStatus != 0) handleError (hwndDlg, nStatus, SRC_POS); return nStatus; }
/** * Destroy the host and all connected peers. */ void destroy (void) { host_destroy(); crypto_close(); }