Linux: parallelize header KDF autodetection

Extend the Unix encryption thread pool to run key-derivation work items and use it when mounting volumes without an explicitly selected KDF. This brings Linux/macOS header PRF autodetection closer to the Windows path while keeping selected-KDF mounts unchanged.

Fixes #1610.
This commit is contained in:
Mounir IDRASSI
2026-05-25 21:54:14 +09:00
parent 66ddd29c91
commit a173a11cfe
9 changed files with 357 additions and 57 deletions
+90 -3
View File
@@ -23,9 +23,62 @@
#include "Platform/SystemLog.h"
#include "Common/Crypto.h"
#include "EncryptionThreadPool.h"
#include "Pkcs5Kdf.h"
namespace VeraCrypt
{
EncryptionThreadPool::KeyDerivationWorkItem::KeyDerivationWorkItem (shared_ptr <Pkcs5Kdf> kdf, size_t derivedKeySize)
: Completed (false), DerivedKey (derivedKeySize), Kdf (kdf), Processed (false), Result (0)
{
}
EncryptionThreadPool::KeyDerivationWorkItem::~KeyDerivationWorkItem ()
{
}
void EncryptionThreadPool::BeginKeyDerivation (KeyDerivationWorkItem &keyDerivationWorkItem, const VolumePassword &password, int pim, const ConstBufferPtr &salt, SyncEvent &completionEvent, SyncEvent &noOutstandingWorkItemEvent, SharedVal <size_t> &outstandingWorkItemCount, long volatile *abortFlag)
{
if (!ThreadPoolRunning)
throw NotInitialized (SRC_POS);
ScopeLock lock (EnqueueMutex);
WorkItem *workItem = &WorkItemQueue[EnqueuePosition++];
if (EnqueuePosition >= QueueSize)
EnqueuePosition = 0;
while (workItem->State != WorkItem::State::Free)
{
WorkItemCompletedEvent.Wait();
}
keyDerivationWorkItem.Completed.Set (false);
keyDerivationWorkItem.ItemException.reset();
keyDerivationWorkItem.Processed = false;
keyDerivationWorkItem.Result = 0;
workItem->Type = WorkType::DeriveKey;
workItem->KeyDerivation.AbortFlag = abortFlag;
workItem->KeyDerivation.CompletionEvent = &completionEvent;
workItem->KeyDerivation.NoOutstandingWorkItemEvent = &noOutstandingWorkItemEvent;
workItem->KeyDerivation.OutstandingWorkItemCount = &outstandingWorkItemCount;
workItem->KeyDerivation.Password = &password;
workItem->KeyDerivation.Pim = pim;
workItem->KeyDerivation.Salt = salt.Get();
workItem->KeyDerivation.SaltSize = salt.Size();
workItem->KeyDerivation.WorkItem = &keyDerivationWorkItem;
{
ScopeLock outstandingWorkItemLock (KeyDerivationCompletionMutex);
if (outstandingWorkItemCount.Increment() == 1)
noOutstandingWorkItemEvent.Reset();
}
workItem->State.Set (WorkItem::State::Ready);
WorkItemReadyEvent.Signal();
}
void EncryptionThreadPool::DoWork (WorkType::Enum type, const EncryptionMode *encryptionMode, uint8 *data, uint64 startUnitNo, uint64 unitCount, size_t sectorSize)
{
size_t fragmentCount;
@@ -272,21 +325,54 @@ namespace VeraCrypt
workItem->Encryption.Mode->EncryptSectorsCurrentThread (workItem->Encryption.Data, workItem->Encryption.StartUnitNo, workItem->Encryption.UnitCount, workItem->Encryption.SectorSize);
break;
case WorkType::DeriveKey:
{
KeyDerivationWorkItem *keyDerivationWorkItem = workItem->KeyDerivation.WorkItem;
if (workItem->KeyDerivation.AbortFlag && *workItem->KeyDerivation.AbortFlag)
keyDerivationWorkItem->Result = ERR_USER_ABORT;
else
keyDerivationWorkItem->Result = keyDerivationWorkItem->Kdf->DeriveKey (keyDerivationWorkItem->DerivedKey, *workItem->KeyDerivation.Password, workItem->KeyDerivation.Pim, ConstBufferPtr (workItem->KeyDerivation.Salt, workItem->KeyDerivation.SaltSize), workItem->KeyDerivation.AbortFlag);
}
break;
default:
throw ParameterIncorrect (SRC_POS);
}
}
catch (Exception &e)
{
workItem->FirstFragment->ItemException.reset (e.CloneNew());
if (workItem->Type == WorkType::DeriveKey)
workItem->KeyDerivation.WorkItem->ItemException.reset (e.CloneNew());
else
workItem->FirstFragment->ItemException.reset (e.CloneNew());
}
catch (exception &e)
{
workItem->FirstFragment->ItemException.reset (new ExternalException (SRC_POS, StringConverter::ToExceptionString (e)));
if (workItem->Type == WorkType::DeriveKey)
workItem->KeyDerivation.WorkItem->ItemException.reset (new ExternalException (SRC_POS, StringConverter::ToExceptionString (e)));
else
workItem->FirstFragment->ItemException.reset (new ExternalException (SRC_POS, StringConverter::ToExceptionString (e)));
}
catch (...)
{
workItem->FirstFragment->ItemException.reset (new UnknownException (SRC_POS));
if (workItem->Type == WorkType::DeriveKey)
workItem->KeyDerivation.WorkItem->ItemException.reset (new UnknownException (SRC_POS));
else
workItem->FirstFragment->ItemException.reset (new UnknownException (SRC_POS));
}
if (workItem->Type == WorkType::DeriveKey)
{
workItem->KeyDerivation.WorkItem->Completed.Set (true);
workItem->KeyDerivation.CompletionEvent->Signal();
{
ScopeLock outstandingWorkItemLock (KeyDerivationCompletionMutex);
if (workItem->KeyDerivation.OutstandingWorkItemCount->Decrement() == 0)
workItem->KeyDerivation.NoOutstandingWorkItemEvent->Signal();
}
workItem->State.Set (WorkItem::State::Free);
WorkItemCompletedEvent.Signal();
continue;
}
if (workItem != workItem->FirstFragment)
@@ -321,6 +407,7 @@ namespace VeraCrypt
Mutex EncryptionThreadPool::EnqueueMutex;
Mutex EncryptionThreadPool::DequeueMutex;
Mutex EncryptionThreadPool::KeyDerivationCompletionMutex;
SyncEvent EncryptionThreadPool::WorkItemReadyEvent;
SyncEvent EncryptionThreadPool::WorkItemCompletedEvent;