GUI: recover hidden protection without replacing outer credentials

Handle protection failures before outer-password retries, retain cached and
explicit outer credentials without adding default keyfiles, and focus the
hidden-protection controls. Initialize hidden PIM and KDF independently,
and retain an accepted backup header during protection recovery.

Retry embedded backup headers after repeated hidden-protection failures
without advancing the outer-password retry count. Restore the accepted
primary-header selection if backup authentication fails, and report
automatic backup use only when the selected header remains a backup.

Keep primary-header recovery available after an automatic backup attempt
fails with EIO, while reporting the error. Preserve cancellation and
other error handling.

Pass known protection failures from favorite mounts directly into GUI
recovery, including keyfile-only credentials with a null password. Skip
redundant authentication attempts and preserve the text interface's
existing retry path.

Initialize the backup-header checkbox with the other validated controls,
so an initial backup-header request survives the first password prompt.

Conceal the accepted outer password and PIM before disabling their
controls during protection recovery.
This commit is contained in:
Mounir IDRASSI
2026-09-25 16:43:25 +02:00
parent bc9f7d2899
commit bee7d4f1c8
8 changed files with 159 additions and 40 deletions
+47 -23
View File
@@ -33,7 +33,7 @@ namespace VeraCrypt
#ifdef __WXGTK__ // GTK apparently needs wxRESIZE_BORDER to support dynamic resizing
, wxDefaultPosition, wxSize (-1,-1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER
#endif
), Options (options)
), Options (options), ProtectionRecovery (false)
#ifdef TC_UNIX
, m_showRedBorder(false)
#endif
@@ -79,6 +79,7 @@ namespace VeraCrypt
NoFilesystemCheckBox->SetValidator (wxGenericValidator (&Options.NoFilesystem));
RemovableCheckBox->SetValidator (wxGenericValidator (&Options.Removable));
PartitionInSystemEncryptionScopeCheckBox->SetValidator (wxGenericValidator (&Options.PartitionInSystemEncryptionScope));
BackupHeaderCheckBox->SetValidator (wxGenericValidator (&Options.UseBackupHeaders));
TransferDataToWindow();
@@ -88,14 +89,16 @@ namespace VeraCrypt
FilesystemOptionsTextCtrl->SetValue (Options.FilesystemOptions);
ReadOnlyCheckBox->SetValue (Options.Protection == VolumeProtection::ReadOnly);
BackupHeaderCheckBox->SetValidator (wxGenericValidator (&Options.UseBackupHeaders));
ProtectionCheckBox->SetValue (Options.Protection == VolumeProtection::HiddenVolumeReadOnly);
OptionsButtonLabel = OptionsButton->GetLabel();
OptionsButton->SetLabel (OptionsButtonLabel + L" >");
OptionsPanel->Show (false);
ProtectionPasswordPanel = new VolumePasswordPanel (ProtectionSizer->GetStaticBox(), &options, options.ProtectionPassword, options.ProtectionKeyfiles, false, true, true, false, true, true, LangString["IDT_HIDDEN_PROT_PASSWD"]);
MountOptions protectionOptions;
protectionOptions.Pim = options.ProtectionPim;
protectionOptions.Kdf = options.ProtectionKdf;
ProtectionPasswordPanel = new VolumePasswordPanel (ProtectionSizer->GetStaticBox(), &protectionOptions, options.ProtectionPassword, options.ProtectionKeyfiles, false, true, true, false, true, true, LangString["IDT_HIDDEN_PROT_PASSWD"]);
ProtectionPasswordPanel->TopOwnerParent = this;
ProtectionPasswordSizer->Add (ProtectionPasswordPanel, 1, wxALL | wxEXPAND);
@@ -105,7 +108,25 @@ namespace VeraCrypt
void MountOptionsDialog::OnInitDialog (wxInitDialogEvent& event)
{
PasswordPanel->SetFocusToPasswordTextCtrl();
if (ProtectionRecovery)
ProtectionPasswordPanel->SetFocusToPasswordTextCtrl();
else
PasswordPanel->SetFocusToPasswordTextCtrl();
}
void MountOptionsDialog::SetProtectionRecovery (bool recovery)
{
if (recovery && !ProtectionRecovery)
PasswordPanel->SetPasswordVisible (false);
ProtectionRecovery = recovery;
PasswordPanel->Enable (!recovery);
if (recovery)
{
BackupHeaderCheckBox->SetValue (Options.UseBackupHeaders);
OptionsPanel->Show (true);
OptionsButton->SetLabel (OptionsButtonLabel + L" <");
UpdateDialog();
}
}
void MountOptionsDialog::OnMountPointButtonClick (wxCommandEvent& event)
@@ -118,12 +139,12 @@ namespace VeraCrypt
void MountOptionsDialog::OnOKButtonClick (wxCommandEvent& event)
{
/* verify that PIM values are valid before continuing*/
int Pim = PasswordPanel->GetVolumePim();
int Pim = ProtectionRecovery ? Options.Pim : PasswordPanel->GetVolumePim();
int ProtectionPim = (!ReadOnlyCheckBox->IsChecked() && ProtectionCheckBox->IsChecked())?
ProtectionPasswordPanel->GetVolumePim() : 0;
/* invalid PIM: set focus to PIM field and stop processing */
if (-1 == Pim || (PartitionInSystemEncryptionScopeCheckBox->IsChecked() && Pim > MAX_BOOT_PIM_VALUE))
if (!ProtectionRecovery && (-1 == Pim || (PartitionInSystemEncryptionScopeCheckBox->IsChecked() && Pim > MAX_BOOT_PIM_VALUE)))
{
PasswordPanel->SetFocusToPimTextCtrl();
return;
@@ -137,25 +158,28 @@ namespace VeraCrypt
TransferDataFromWindow();
try
if (!ProtectionRecovery)
{
Options.Password = PasswordPanel->GetPassword(Options.PartitionInSystemEncryptionScope);
try
{
Options.Password = PasswordPanel->GetPassword(Options.PartitionInSystemEncryptionScope);
}
catch (PasswordException& e)
{
Gui->ShowWarning (e);
return;
}
if (Options.PartitionInSystemEncryptionScope && Options.Password->Size() > VolumePassword::MaxLegacySize)
{
Gui->ShowWarning (StringFormatter (LangString["LINUX_SYSTEM_ENC_PW_LENGTH_NOTE"], (int) VolumePassword::MaxLegacySize));
return;
}
Options.Pim = Pim;
Options.Kdf = PasswordPanel->GetPkcs5Kdf();
Options.Keyfiles = PasswordPanel->GetKeyfiles();
}
catch (PasswordException& e)
{
Gui->ShowWarning (e);
return;
}
if (Options.PartitionInSystemEncryptionScope && Options.Password->Size() > VolumePassword::MaxLegacySize)
{
Gui->ShowWarning (StringFormatter (LangString["LINUX_SYSTEM_ENC_PW_LENGTH_NOTE"], (int) VolumePassword::MaxLegacySize));
return;
}
Options.Pim = Pim;
Options.Kdf = PasswordPanel->GetPkcs5Kdf();
Options.Keyfiles = PasswordPanel->GetKeyfiles();
if (ReadOnlyCheckBox->IsChecked())
{
+2
View File
@@ -24,6 +24,7 @@ namespace VeraCrypt
public:
MountOptionsDialog (wxWindow* parent, MountOptions &options, const wxString &title = wxEmptyString, bool disableMountOptions = false);
void OnShow ();
void SetProtectionRecovery (bool recovery);
#ifdef TC_MACOSX
virtual bool ProcessEvent(wxEvent& event);
@@ -47,6 +48,7 @@ namespace VeraCrypt
#endif
MountOptions &Options;
bool ProtectionRecovery;
#ifdef TC_UNIX
bool m_showRedBorder;
#endif
+6 -4
View File
@@ -401,16 +401,18 @@ namespace VeraCrypt
}
}
void VolumePasswordPanel::OnDisplayPasswordCheckBoxClick (wxCommandEvent& event)
void VolumePasswordPanel::SetPasswordVisible (bool visible)
{
DisplayPasswordCheckBox->SetValue (visible);
if (PasswordTextCtrl->IsShown())
DisplayPassword (event.IsChecked(), &PasswordTextCtrl, 1);
DisplayPassword (visible, &PasswordTextCtrl, 1);
if (ConfirmPasswordTextCtrl->IsShown())
DisplayPassword (event.IsChecked(), &ConfirmPasswordTextCtrl, 2);
DisplayPassword (visible, &ConfirmPasswordTextCtrl, 2);
if (VolumePimTextCtrl->IsShown())
DisplayPassword (event.IsChecked(), &VolumePimTextCtrl, 3);
DisplayPassword (visible, &VolumePimTextCtrl, 3);
OnUpdate();
}
+2 -1
View File
@@ -37,6 +37,7 @@ namespace VeraCrypt
void SetFocusToPimCheckBox () { PimCheckBox->SetFocus(); }
void SetFocusToPimTextCtrl () { VolumePimTextCtrl->SetSelection (-1, -1); VolumePimTextCtrl->SetFocus(); }
void ResetVolumePimToDefault ();
void SetPasswordVisible (bool visible);
void SetVolumePim (int pim);
bool PasswordsMatch () const;
void EnableUsePim (bool pimOnlyDisplay = false);
@@ -54,7 +55,7 @@ namespace VeraCrypt
void OnAddKeyfileDirMenuItemSelected (wxCommandEvent& event);
void OnAddKeyfilesMenuItemSelected (wxCommandEvent& event);
void OnAddSecurityTokenSignatureMenuItemSelected (wxCommandEvent& event);
void OnDisplayPasswordCheckBoxClick (wxCommandEvent& event);
void OnDisplayPasswordCheckBoxClick (wxCommandEvent& event) { SetPasswordVisible (event.IsChecked()); }
void OnKeyfilesButtonClick (wxCommandEvent& event);
void OnKeyfilesButtonRightClick (wxMouseEvent& event);
void OnKeyfilesButtonRightDown (wxMouseEvent& event);
+91 -9
View File
@@ -15,6 +15,7 @@
#ifdef TC_UNIX
#include <wx/mimetype.h>
#include <wx/sckipc.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
@@ -905,6 +906,17 @@ namespace VeraCrypt
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolume (MountOptions &options, bool tryCachedPasswords) const
{
return MountVolumeInternal (options, tryCachedPasswords, false);
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolumeWithProtectionRecovery (MountOptions &options, const PasswordException &protectionError) const
{
ShowWarning (protectionError);
return MountVolumeInternal (options, false, true);
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolumeInternal (MountOptions &options, bool tryCachedPasswords, bool protectionRecovery) const
{
CheckRequirementsForMountingVolume();
@@ -937,7 +949,32 @@ namespace VeraCrypt
try
{
if (tryCachedPasswords
bool protectionError = protectionRecovery;
auto mountWithProtectionRecovery = [&] () -> shared_ptr <VolumeInfo>
{
protectionError = false;
try
{
return UserInterface::MountVolume (options);
}
catch (ProtectionPasswordIncorrect &e)
{
ShowWarning (e);
}
catch (ProtectionPasswordKeyfilesIncorrect &e)
{
ShowWarning (e);
}
protectionError = true;
// Keep the accepted outer credential source, including the password
// cache. An explicit empty list prevents default keyfiles being added.
if (!options.Keyfiles)
options.Keyfiles = make_shared <KeyfileList>();
return shared_ptr <VolumeInfo>();
};
if (!protectionError && tryCachedPasswords
&& (!options.Password || options.Password->IsEmpty())
&& (!options.Keyfiles || options.Keyfiles->empty())
&& !Core->IsPasswordCacheEmpty())
@@ -946,21 +983,25 @@ namespace VeraCrypt
try
{
wxBusyCursor busy;
return UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (volume)
return volume;
}
catch (PasswordException&) { }
}
if (!options.Keyfiles && GetPreferences().UseKeyfiles && !GetPreferences().DefaultKeyfiles.empty())
if (!protectionError && !options.Keyfiles && GetPreferences().UseKeyfiles && !GetPreferences().DefaultKeyfiles.empty())
options.Keyfiles = make_shared <KeyfileList> (GetPreferences().DefaultKeyfiles);
if ((options.Password && !options.Password->IsEmpty())
|| (options.Keyfiles && !options.Keyfiles->empty() && options.Password))
if (!protectionError && ((options.Password && !options.Password->IsEmpty())
|| (options.Keyfiles && !options.Keyfiles->empty() && options.Password)))
{
try
{
wxBusyCursor busy;
return UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (volume)
return volume;
}
catch (PasswordException&) { }
}
@@ -970,17 +1011,54 @@ namespace VeraCrypt
MountOptionsDialog dialog (GetTopWindow(), options);
int incorrectPasswordCount = 0;
int incorrectProtectionPasswordCount = 0;
bool autoBackupHeaderUsed = false;
while (!volume)
{
dialog.Hide();
dialog.SetProtectionRecovery (protectionError);
if (dialog.ShowModal() != wxID_OK)
return volume;
try
{
wxBusyCursor busy;
volume = UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (!volume && protectionError && !options.UseBackupHeaders
&& ++incorrectProtectionPasswordCount > 2)
{
// The outer primary header was accepted, but the hidden primary
// header may be damaged. Try the backups once for this attempt.
options.UseBackupHeaders = true;
try
{
volume = UserInterface::MountVolume (options);
autoBackupHeaderUsed = true;
}
catch (PasswordException&)
{
// Keep recovering against the accepted primary outer header.
options.UseBackupHeaders = false;
}
#ifdef TC_UNIX
catch (SystemException &e)
{
options.UseBackupHeaders = false;
if (e.GetErrorCode() != EIO)
throw;
// An unreadable backup must not prevent another primary-header attempt.
ShowWarning (e);
}
#endif
catch (...)
{
options.UseBackupHeaders = false;
throw;
}
}
}
catch (PasswordIncorrect &e)
{
@@ -988,15 +1066,16 @@ namespace VeraCrypt
{
// Try to mount the volume using the backup header
options.UseBackupHeaders = true;
autoBackupHeaderUsed = true;
try
{
volume = UserInterface::MountVolume (options);
ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
volume = mountWithProtectionRecovery();
}
catch (...)
{
options.UseBackupHeaders = false;
autoBackupHeaderUsed = false;
ShowWarning (e);
}
}
@@ -1008,6 +1087,9 @@ namespace VeraCrypt
ShowWarning (e);
}
}
if (autoBackupHeaderUsed && options.UseBackupHeaders)
ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
}
catch (exception &e)
{
+2
View File
@@ -120,6 +120,7 @@ namespace VeraCrypt
Event OpenVolumeSystemRequestEvent;
protected:
virtual shared_ptr <VolumeInfo> MountVolumeWithProtectionRecovery (MountOptions &options, const PasswordException &protectionError) const;
virtual void OnEndSession (wxCloseEvent& event) { OnLogOff(); }
#ifdef wxHAS_POWER_EVENTS
virtual void OnPowerSuspending (wxPowerEvent& event);
@@ -145,6 +146,7 @@ public:
#endif
private:
shared_ptr <VolumeInfo> MountVolumeInternal (MountOptions &options, bool tryCachedPasswords, bool protectionRecovery) const;
GraphicUserInterface (const GraphicUserInterface &);
GraphicUserInterface &operator= (const GraphicUserInterface &);
};
+8 -3
View File
@@ -851,11 +851,16 @@ namespace VeraCrypt
{
CloseSecurityTokenSessionsAfterMountScope closeTokenSessionsScope (Preferences.CloseSecurityTokenSessionsAfterMount);
// A protection failure accepted the outer password. Let the UI recover
// using that cache; only skip a cache sweep that failed outer authentication.
// A protection failure accepted the outer credentials. Preserve that
// state for UI recovery; otherwise skip the failed cache sweep.
bool protectionError = dynamic_cast <ProtectionPasswordIncorrect *> (&e)
|| dynamic_cast <ProtectionPasswordKeyfilesIncorrect *> (&e);
shared_ptr <VolumeInfo> volume = MountVolume (favoriteOptions, protectionError);
// Do not add default outer keyfiles to credentials already accepted.
if (protectionError && !favoriteOptions.Keyfiles)
favoriteOptions.Keyfiles = make_shared <KeyfileList>();
shared_ptr <VolumeInfo> volume = protectionError
? MountVolumeWithProtectionRecovery (favoriteOptions, e)
: MountVolume (favoriteOptions, false);
if (!volume)
break;
+1
View File
@@ -102,6 +102,7 @@ namespace VeraCrypt
protected:
UserInterface ();
virtual shared_ptr <VolumeInfo> MountVolumeWithProtectionRecovery (MountOptions &options, const PasswordException &protectionError) const { return MountVolume (options); }
virtual bool OnExceptionInMainLoop () { throw; }
virtual void OnUnhandledException ();
virtual void OnVolumeMounted (EventArgs &args);