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; }
/// /// 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; }