// Returns the largest key needed by any EA for any mode int EAGetLargestKey () { int ea, key = 0; for (ea = EAGetFirst (); ea != 0; ea = EAGetNext (ea)) { if (EAGetKeySize (ea) >= key) key = EAGetKeySize (ea); } return key; }
// Returns the largest key size needed by an EA for the specified mode of operation int EAGetLargestKeyForMode (int mode) { int ea, key = 0; for (ea = EAGetFirst (); ea != 0; ea = EAGetNext (ea)) { if (!EAIsModeSupported (ea, mode)) continue; if (EAGetKeySize (ea) >= key) key = EAGetKeySize (ea); } return key; }
int FormatNoFs (HWND hwndDlg, unsigned __int64 startSector, __int64 num_sectors, void * dev, PCRYPTO_INFO cryptoInfo, BOOL quickFormat) { int write_buf_cnt = 0; char sector[TC_MAX_VOLUME_SECTOR_SIZE], *write_buf; unsigned __int64 nSecNo = startSector; int retVal = 0; DWORD err; char temporaryKey[MASTER_KEYDATA_SIZE]; char originalK2[MASTER_KEYDATA_SIZE]; LARGE_INTEGER startOffset; LARGE_INTEGER newOffset; // Seek to start sector startOffset.QuadPart = startSector * FormatSectorSize; if (!SetFilePointerEx ((HANDLE) dev, startOffset, &newOffset, FILE_BEGIN) || newOffset.QuadPart != startOffset.QuadPart) { return ERR_OS_ERROR; } write_buf = (char *)TCalloc (FormatWriteBufferSize); if (!write_buf) return ERR_OUTOFMEMORY; VirtualLock (temporaryKey, sizeof (temporaryKey)); VirtualLock (originalK2, sizeof (originalK2)); memset (sector, 0, sizeof (sector)); // Remember the original secondary key (XTS mode) before generating a temporary one memcpy (originalK2, cryptoInfo->k2, sizeof (cryptoInfo->k2)); /* Fill the rest of the data area with random data */ if(!quickFormat) { /* Generate a random temporary key set to be used for "dummy" encryption that will fill the free disk space (data area) with random data. This is necessary for plausible deniability of hidden volumes. */ // Temporary master key if (!RandgetBytes (hwndDlg, temporaryKey, EAGetKeySize (cryptoInfo->ea), FALSE)) goto fail; // Temporary secondary key (XTS mode) if (!RandgetBytes (hwndDlg, cryptoInfo->k2, sizeof cryptoInfo->k2, FALSE)) goto fail; retVal = EAInit (cryptoInfo->ea, temporaryKey, cryptoInfo->ks); if (retVal != ERR_SUCCESS) goto fail; if (!EAInitMode (cryptoInfo)) { retVal = ERR_MODE_INIT_FAILED; goto fail; } while (num_sectors--) { if (WriteSector (dev, sector, write_buf, &write_buf_cnt, &nSecNo, cryptoInfo) == FALSE) goto fail; } if (!FlushFormatWriteBuffer (dev, write_buf, &write_buf_cnt, &nSecNo, cryptoInfo)) goto fail; } else nSecNo = num_sectors; UpdateProgressBar (nSecNo * FormatSectorSize); // Restore the original secondary key (XTS mode) in case NTFS format fails and the user wants to try FAT immediately memcpy (cryptoInfo->k2, originalK2, sizeof (cryptoInfo->k2)); // Reinitialize the encryption algorithm and mode in case NTFS format fails and the user wants to try FAT immediately retVal = EAInit (cryptoInfo->ea, cryptoInfo->master_keydata, cryptoInfo->ks); if (retVal != ERR_SUCCESS) goto fail; if (!EAInitMode (cryptoInfo)) { retVal = ERR_MODE_INIT_FAILED; goto fail; } burn (temporaryKey, sizeof(temporaryKey)); burn (originalK2, sizeof(originalK2)); VirtualUnlock (temporaryKey, sizeof (temporaryKey)); VirtualUnlock (originalK2, sizeof (originalK2)); TCfree (write_buf); return 0; fail: err = GetLastError(); burn (temporaryKey, sizeof(temporaryKey)); burn (originalK2, sizeof(originalK2)); VirtualUnlock (temporaryKey, sizeof (temporaryKey)); VirtualUnlock (originalK2, sizeof (originalK2)); TCfree (write_buf); SetLastError (err); return (retVal ? retVal : ERR_OS_ERROR); }
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; }
// Creates a volume header in memory int CreateVolumeHeaderInMemory (BOOL bBoot, char *header, int ea, int mode, Password *password, int pkcs5_prf, char *masterKeydata, PCRYPTO_INFO *retInfo, unsigned __int64 volumeSize, unsigned __int64 hiddenVolumeSize, unsigned __int64 encryptedAreaStart, unsigned __int64 encryptedAreaLength, uint16 requiredProgramVersion, uint32 headerFlags, uint32 sectorSize, BOOL bWipeMode) { unsigned char *p = (unsigned char *) header; static KEY_INFO keyInfo; int nUserKeyLen = password->Length; PCRYPTO_INFO cryptoInfo = crypto_open (); static char dk[MASTER_KEYDATA_SIZE]; int x; int retVal = 0; int primaryKeyOffset; if (cryptoInfo == NULL) return ERR_OUTOFMEMORY; memset (header, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); VirtualLock (&keyInfo, sizeof (keyInfo)); VirtualLock (&dk, sizeof (dk)); /* Encryption setup */ if (masterKeydata == NULL) { // We have no master key data (creating a new volume) so we'll use the TrueCrypt RNG to generate them int bytesNeeded; switch (mode) { case LRW: case CBC: case INNER_CBC: case OUTER_CBC: // Deprecated/legacy modes of operation bytesNeeded = LEGACY_VOL_IV_SIZE + EAGetKeySize (ea); // In fact, this should never be the case since volumes being newly created are not // supposed to use any deprecated mode of operation. TC_THROW_FATAL_EXCEPTION; break; default: bytesNeeded = EAGetKeySize (ea) * 2; // Size of primary + secondary key(s) } if (!RandgetBytes (keyInfo.master_keydata, bytesNeeded, TRUE)) return ERR_CIPHER_INIT_WEAK_KEY; } else { // We already have existing master key data (the header is being re-encrypted) memcpy (keyInfo.master_keydata, masterKeydata, MASTER_KEYDATA_SIZE); } // User key memcpy (keyInfo.userKey, password->Text, nUserKeyLen); keyInfo.keyLength = nUserKeyLen; keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, bBoot); // User selected encryption algorithm cryptoInfo->ea = ea; // Mode of operation cryptoInfo->mode = mode; // Salt for header key derivation if (!RandgetBytes (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: 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 RIPEMD160: derive_key_ripemd160 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; case WHIRLPOOL: derive_key_whirlpool (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } /* Header setup */ // Salt mputBytes (p, keyInfo.salt, PKCS5_SALT_SIZE); // Magic mputLong (p, 0x54525545); // Header version mputWord (p, VOLUME_HEADER_VERSION); cryptoInfo->HeaderVersion = VOLUME_HEADER_VERSION; // Required program version to handle this volume switch (mode) { case LRW: // Deprecated/legacy mputWord (p, 0x0410); break; case OUTER_CBC: case INNER_CBC: // Deprecated/legacy mputWord (p, 0x0300); break; case CBC: // Deprecated/legacy mputWord (p, hiddenVolumeSize > 0 ? 0x0300 : 0x0100); break; default: mputWord (p, requiredProgramVersion != 0 ? requiredProgramVersion : TC_VOLUME_MIN_REQUIRED_PROGRAM_VERSION); } // CRC of the master key data x = GetCrc32(keyInfo.master_keydata, MASTER_KEYDATA_SIZE); mputLong (p, x); // Reserved fields p += 2 * 8; // Size of hidden volume (if any) cryptoInfo->hiddenVolumeSize = hiddenVolumeSize; mputInt64 (p, cryptoInfo->hiddenVolumeSize); cryptoInfo->hiddenVolume = cryptoInfo->hiddenVolumeSize != 0; // Volume size cryptoInfo->VolumeSize.Value = volumeSize; mputInt64 (p, volumeSize); // Encrypted area start cryptoInfo->EncryptedAreaStart.Value = encryptedAreaStart; mputInt64 (p, encryptedAreaStart); // Encrypted area size cryptoInfo->EncryptedAreaLength.Value = encryptedAreaLength; mputInt64 (p, encryptedAreaLength); // Flags cryptoInfo->HeaderFlags = headerFlags; mputLong (p, headerFlags); // Sector size if (sectorSize < TC_MIN_VOLUME_SECTOR_SIZE || sectorSize > TC_MAX_VOLUME_SECTOR_SIZE || sectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) { TC_THROW_FATAL_EXCEPTION; } cryptoInfo->SectorSize = sectorSize; mputLong (p, sectorSize); // CRC of the header fields x = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); p = header + TC_HEADER_OFFSET_HEADER_CRC; mputLong (p, x); // The master key data memcpy (header + HEADER_MASTER_KEYDATA_OFFSET, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); /* Header encryption */ switch (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, dk, LEGACY_VOL_IV_SIZE); primaryKeyOffset = LEGACY_VOL_IV_SIZE; break; default: // The secondary key (if cascade, multiple concatenated) memcpy (cryptoInfo->k2, dk + EAGetKeySize (cryptoInfo->ea), EAGetKeySize (cryptoInfo->ea)); primaryKeyOffset = 0; } retVal = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) return retVal; // Mode of operation if (!EAInitMode (cryptoInfo)) return ERR_OUTOFMEMORY; // Encrypt the entire header (except the salt) EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo); /* cryptoInfo setup for further use (disk format) */ // Init with the master key(s) retVal = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) return retVal; memcpy (cryptoInfo->master_keydata, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); 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)); } // Mode of operation if (!EAInitMode (cryptoInfo)) return ERR_OUTOFMEMORY; #ifdef VOLFORMAT if (showKeys && !bInPlaceEncNonSys) { BOOL dots3 = FALSE; int i, j; j = EAGetKeySize (ea); if (j > NBR_KEY_BYTES_TO_DISPLAY) { dots3 = TRUE; j = NBR_KEY_BYTES_TO_DISPLAY; } MasterKeyGUIView[0] = 0; for (i = 0; i < j; i++) { char tmp2[8] = {0}; sprintf (tmp2, "%02X", (int) (unsigned char) keyInfo.master_keydata[i + primaryKeyOffset]); strcat (MasterKeyGUIView, tmp2); } HeaderKeyGUIView[0] = 0; for (i = 0; i < NBR_KEY_BYTES_TO_DISPLAY; i++) { char tmp2[8]; sprintf (tmp2, "%02X", (int) (unsigned char) dk[primaryKeyOffset + i]); strcat (HeaderKeyGUIView, tmp2); } if (dots3) { DisplayPortionsOfKeys (hHeaderKey, hMasterKey, HeaderKeyGUIView, MasterKeyGUIView, !showKeys); } else { SendMessage (hMasterKey, WM_SETTEXT, 0, (LPARAM) MasterKeyGUIView); SendMessage (hHeaderKey, WM_SETTEXT, 0, (LPARAM) HeaderKeyGUIView); } } #endif // #ifdef VOLFORMAT burn (dk, sizeof(dk)); burn (&keyInfo, sizeof (keyInfo)); *retInfo = cryptoInfo; return 0; }
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; }
// Writes randomly generated data to unused/reserved header areas. // When bPrimaryOnly is TRUE, then only the primary header area (not the backup header area) is filled with random data. // When bBackupOnly is TRUE, only the backup header area (not the primary header area) is filled with random data. int WriteRandomDataToReservedHeaderAreas (HANDLE dev, CRYPTO_INFO *cryptoInfo, uint64 dataAreaSize, BOOL bPrimaryOnly, BOOL bBackupOnly) { char temporaryKey[MASTER_KEYDATA_SIZE]; char originalK2[MASTER_KEYDATA_SIZE]; byte buf[TC_VOLUME_HEADER_GROUP_SIZE]; LARGE_INTEGER offset; int nStatus = ERR_SUCCESS; DWORD dwError; DWORD bytesDone; BOOL backupHeaders = bBackupOnly; if (bPrimaryOnly && bBackupOnly) TC_THROW_FATAL_EXCEPTION; memcpy (originalK2, cryptoInfo->k2, sizeof (cryptoInfo->k2)); while (TRUE) { // Temporary keys if (!RandgetBytes (temporaryKey, EAGetKeySize (cryptoInfo->ea), FALSE) || !RandgetBytes (cryptoInfo->k2, sizeof (cryptoInfo->k2), FALSE)) { nStatus = ERR_PARAMETER_INCORRECT; goto final_seq; } nStatus = EAInit (cryptoInfo->ea, temporaryKey, cryptoInfo->ks); if (nStatus != ERR_SUCCESS) goto final_seq; if (!EAInitMode (cryptoInfo)) { nStatus = ERR_MODE_INIT_FAILED; goto final_seq; } offset.QuadPart = backupHeaders ? dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE : TC_VOLUME_HEADER_OFFSET; if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto final_seq; } if (!ReadFile (dev, buf, sizeof (buf), &bytesDone, NULL)) { nStatus = ERR_OS_ERROR; goto final_seq; } if (bytesDone < TC_VOLUME_HEADER_EFFECTIVE_SIZE) { SetLastError (ERROR_INVALID_PARAMETER); nStatus = ERR_OS_ERROR; goto final_seq; } EncryptBuffer (buf + TC_VOLUME_HEADER_EFFECTIVE_SIZE, sizeof (buf) - TC_VOLUME_HEADER_EFFECTIVE_SIZE, cryptoInfo); if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) { nStatus = ERR_OS_ERROR; goto final_seq; } if (!WriteFile (dev, buf, sizeof (buf), &bytesDone, NULL)) { nStatus = ERR_OS_ERROR; goto final_seq; } if (bytesDone != sizeof (buf)) { nStatus = ERR_PARAMETER_INCORRECT; goto final_seq; } if (backupHeaders || bPrimaryOnly) break; backupHeaders = TRUE; } memcpy (cryptoInfo->k2, originalK2, sizeof (cryptoInfo->k2)); nStatus = EAInit (cryptoInfo->ea, cryptoInfo->master_keydata, cryptoInfo->ks); if (nStatus != ERR_SUCCESS) goto final_seq; if (!EAInitMode (cryptoInfo)) { nStatus = ERR_MODE_INIT_FAILED; goto final_seq; } final_seq: dwError = GetLastError(); burn (temporaryKey, sizeof (temporaryKey)); burn (originalK2, sizeof (originalK2)); if (nStatus != ERR_SUCCESS) SetLastError (dwError); return nStatus; }
/// /// Note: if there are Keyfiles, these must be applied already to the password! /// int __declspec(dllexport) __stdcall CheckVolumeHeaderPassword (BOOL bBoot, char *encryptedHeader, Password *password) int __declspec(dllexport) __cdecl CheckVolumeHeaderPassword (BOOL bBoot, char *encryptedHeader, Password *password) { 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; cryptoInfo = 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; 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; } } VirtualLock (&keyInfo, sizeof (keyInfo)); VirtualLock (&dk, sizeof (dk)); 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){ status = ERR_SUCCESS; goto ret; } } } } status = ERR_PASSWORD_WRONG; err: ret: burn (&keyInfo, sizeof (keyInfo)); burn (dk, sizeof(dk)); VirtualUnlock (&keyInfo, sizeof (keyInfo)); VirtualUnlock (&dk, sizeof (dk)); if (encryptionThreadCount > 1) { // TC_WAIT_EVENT (noOutstandingWorkItemEvent); burn (keyDerivationWorkItems, sizeof (KeyDerivationWorkItem) * pkcs5PrfCount); TCfree (keyDerivationWorkItems); CloseHandle (keyDerivationCompletedEvent); CloseHandle (noOutstandingWorkItemEvent); } 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; }