VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Main/TextUserInterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Main/TextUserInterface.cpp')
-rw-r--r--src/Main/TextUserInterface.cpp1552
1 files changed, 1552 insertions, 0 deletions
diff --git a/src/Main/TextUserInterface.cpp b/src/Main/TextUserInterface.cpp
new file mode 100644
index 00000000..d9e93e6f
--- /dev/null
+++ b/src/Main/TextUserInterface.cpp
@@ -0,0 +1,1552 @@
+/*
+ Copyright (c) 2008-2010 TrueCrypt Developers Association. All rights reserved.
+
+ Governed by the TrueCrypt License 3.0 the full text of which is contained in
+ the file License.txt included in TrueCrypt binary and source code distribution
+ packages.
+*/
+
+#include "System.h"
+#ifdef TC_UNIX
+#include <signal.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "Platform/Unix/Process.h"
+#endif
+
+#include "Common/SecurityToken.h"
+#include "Core/RandomNumberGenerator.h"
+#include "Application.h"
+#include "TextUserInterface.h"
+
+namespace TrueCrypt
+{
+ TextUserInterface::TextUserInterface ()
+ {
+#ifdef TC_UNIX
+ signal (SIGHUP, OnSignal);
+ signal (SIGINT, OnSignal);
+ signal (SIGQUIT, OnSignal);
+ signal (SIGTERM, OnSignal);
+
+ struct stat statBuf;
+ if (fstat (0, &statBuf) != -1)
+#endif
+ {
+ FInputStream.reset (new wxFFileInputStream (stdin));
+ TextInputStream.reset (new wxTextInputStream (*FInputStream));
+ }
+ }
+
+ TextUserInterface::~TextUserInterface ()
+ {
+ try
+ {
+ if (RandomNumberGenerator::IsRunning())
+ RandomNumberGenerator::Stop();
+ }
+ catch (...) { }
+
+#ifdef TC_UNIX
+ signal (SIGHUP, SIG_DFL);
+ signal (SIGINT, SIG_DFL);
+ signal (SIGQUIT, SIG_DFL);
+ signal (SIGTERM, SIG_DFL);
+#endif
+ }
+
+ FilePath TextUserInterface::AskFilePath (const wxString &message) const
+ {
+ return AskString (!message.empty() ? message : wxString (_("Enter filename: ")));
+ }
+
+ shared_ptr <KeyfileList> TextUserInterface::AskKeyfiles (const wxString &message) const
+ {
+ wxString msg = _("Enter keyfile");
+ if (!message.empty())
+ msg = message;
+
+ make_shared_auto (KeyfileList, keyfiles);
+
+ wxString s;
+ wxString m = msg + L" [" + _("none") + L"]: ";
+ while (!(s = AskString (m)).empty())
+ {
+ keyfiles->push_back (make_shared <Keyfile> (wstring (s)));
+ m = msg + L" [" + _("finish") + L"]: ";
+ }
+
+ return keyfiles;
+ }
+
+ shared_ptr <VolumePassword> TextUserInterface::AskPassword (const wxString &message, bool verify) const
+ {
+ wxString msg = LangString["ENTER_PASSWORD"] + L": ";
+ if (!message.empty())
+ msg = message + L": ";
+
+ SetTerminalEcho (false);
+ finally_do ({ TextUserInterface::SetTerminalEcho (true); });
+
+ wchar_t passwordBuf[4096];
+ finally_do_arg (BufferPtr, BufferPtr (reinterpret_cast <byte *> (passwordBuf), sizeof (passwordBuf)), { finally_arg.Erase(); });
+
+ make_shared_auto (VolumePassword, password);
+
+ bool verPhase = false;
+ while (true)
+ {
+ ShowString (verPhase ? wxString (_("Re-enter password: ")) : msg);
+
+ wxString passwordStr;
+ ReadInputStreamLine (passwordStr);
+
+ size_t length = passwordStr.size();
+
+ ShowString (L"\n");
+
+ if (!verPhase && length < 1)
+ {
+ password->Set (passwordBuf, 0);
+ return password;
+ }
+
+ for (size_t i = 0; i < length && i < VolumePassword::MaxSize; ++i)
+ {
+ passwordBuf[i] = (wchar_t) passwordStr[i];
+ const_cast <wchar_t *> (passwordStr.c_str())[i] = L'X';
+ }
+
+ if (verify && verPhase)
+ {
+ make_shared_auto (VolumePassword, verPassword);
+ verPassword->Set (passwordBuf, length);
+
+ if (*password != *verPassword)
+ {
+ ShowInfo (_("Passwords do not match."));
+ ShowString (L"\n");
+ verPhase = false;
+ continue;
+ }
+ }
+
+ password->Set (passwordBuf, length);
+
+ if (!verPhase)
+ {
+ try
+ {
+ password->CheckPortability();
+ }
+ catch (UnportablePassword &e)
+ {
+ if (verify)
+ {
+ ShowError (e);
+ verPhase = false;
+ continue;
+ }
+
+ ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
+ }
+
+ if (verify)
+ {
+ if (password->Size() < VolumePassword::WarningSizeThreshold)
+ {
+ SetTerminalEcho (true);
+ finally_do ({ TextUserInterface::SetTerminalEcho (false); });
+
+ if (!AskYesNo (LangString ["PASSWORD_LENGTH_WARNING"], false, true))
+ {
+ ShowString (L"\n");
+ continue;
+ }
+ ShowString (L"\n");
+ }
+ }
+ }
+
+ if (!verify || verPhase)
+ return password;
+
+ if (!verPhase)
+ verPhase = true;
+ }
+
+ return password;
+ }
+
+ ssize_t TextUserInterface::AskSelection (ssize_t optionCount, ssize_t defaultOption) const
+ {
+ while (true)
+ {
+ wstring selectionStr = AskString (defaultOption == -1 ? wxString (_("Select: ")) : wxString (wstring (StringFormatter (_("Select [{0}]: "), (uint32) defaultOption))));
+ ssize_t selection;
+
+ if (selectionStr.empty() && defaultOption != -1)
+ return defaultOption;
+
+ try
+ {
+ selection = StringConverter::ToUInt32 (selectionStr);
+ }
+ catch (...)
+ {
+ continue;
+ }
+
+ if (selection > 0 && selection <= optionCount)
+ return selection;
+ }
+ }
+
+ wstring TextUserInterface::AskString (const wxString &message) const
+ {
+ ShowString (message);
+ return wstring (ReadInputStreamLine());
+ }
+
+ bool TextUserInterface::AskYesNo (const wxString &message, bool defaultYes, bool warning) const
+ {
+ while (true)
+ {
+ wxString s = AskString (StringFormatter (L"{0} (y={1}/n={2}) [{3}]: ",
+ message, LangString["YES"], LangString["NO"], LangString[defaultYes ? "YES" : "NO"]));
+
+ if (s.IsSameAs (L'n', false) || s.IsSameAs (L"no", false) || (!defaultYes && s.empty()))
+ return false;
+
+ if (s.IsSameAs (L'y', false) || s.IsSameAs (L"yes", false) || (defaultYes && s.empty()))
+ return true;
+ };
+ }
+
+ shared_ptr <VolumePath> TextUserInterface::AskVolumePath (const wxString &message) const
+ {
+ return make_shared <VolumePath> (AskString (message.empty() ? wxString (_("Enter volume path: ")) : message));
+ }
+
+ void TextUserInterface::BackupVolumeHeaders (shared_ptr <VolumePath> volumePath) const
+ {
+ if (!volumePath)
+ volumePath = AskVolumePath();
+
+ if (!volumePath)
+ throw UserAbort (SRC_POS);
+
+#ifdef TC_WINDOWS
+ if (Core->IsVolumeMounted (*volumePath))
+ throw_err (LangString["DISMOUNT_FIRST"]);
+#endif
+
+ ShowInfo ("EXTERNAL_VOL_HEADER_BAK_FIRST_INFO");
+
+ shared_ptr <Volume> normalVolume;
+ shared_ptr <Volume> hiddenVolume;
+
+ MountOptions normalVolumeMountOptions;
+ MountOptions hiddenVolumeMountOptions;
+
+ normalVolumeMountOptions.Path = volumePath;
+ hiddenVolumeMountOptions.Path = volumePath;
+
+ VolumeType::Enum volumeType = VolumeType::Normal;
+
+ // Open both types of volumes
+ while (true)
+ {
+ shared_ptr <Volume> volume;
+ MountOptions *options = (volumeType == VolumeType::Hidden ? &hiddenVolumeMountOptions : &normalVolumeMountOptions);
+
+ while (!volume)
+ {
+ ShowString (L"\n");
+ options->Password = AskPassword (LangString[volumeType == VolumeType::Hidden ? "ENTER_HIDDEN_VOL_PASSWORD" : "ENTER_NORMAL_VOL_PASSWORD"]);
+ options->Keyfiles = AskKeyfiles();
+
+ try
+ {
+ volume = Core->OpenVolume (
+ options->Path,
+ options->PreserveTimestamps,
+ options->Password,
+ options->Keyfiles,
+ options->Protection,
+ options->ProtectionPassword,
+ options->ProtectionKeyfiles,
+ true,
+ volumeType,
+ options->UseBackupHeaders
+ );
+ }
+ catch (PasswordException &e)
+ {
+ ShowInfo (e);
+ }
+ }
+
+ if (volumeType == VolumeType::Hidden)
+ hiddenVolume = volume;
+ else
+ normalVolume = volume;
+
+ // Ask whether a hidden volume is present
+ if (volumeType == VolumeType::Normal && AskYesNo (L"\n" + LangString["DOES_VOLUME_CONTAIN_HIDDEN"]))
+ {
+ volumeType = VolumeType::Hidden;
+ continue;
+ }
+
+ break;
+ }
+
+ if (hiddenVolume)
+ {
+ if (typeid (*normalVolume->GetLayout()) == typeid (VolumeLayoutV1Normal) && typeid (*hiddenVolume->GetLayout()) != typeid (VolumeLayoutV1Hidden))
+ throw ParameterIncorrect (SRC_POS);
+
+ if (typeid (*normalVolume->GetLayout()) == typeid (VolumeLayoutV2Normal) && typeid (*hiddenVolume->GetLayout()) != typeid (VolumeLayoutV2Hidden))
+ throw ParameterIncorrect (SRC_POS);
+ }
+
+ // Ask user to select backup file path
+ wxString confirmMsg = L"\n" + LangString["CONFIRM_VOL_HEADER_BAK"] + L"\n";
+ confirmMsg.Replace (L"%hs", L"%s");
+
+ if (!AskYesNo (wxString::Format (confirmMsg, wstring (*volumePath).c_str()), true))
+ return;
+
+ ShowString (L"\n");
+
+ FilePath filePath = AskFilePath();
+ if (filePath.IsEmpty())
+ throw UserAbort (SRC_POS);
+
+ File backupFile;
+ backupFile.Open (filePath, File::CreateWrite);
+
+ RandomNumberGenerator::Start();
+ UserEnrichRandomPool();
+
+ // Re-encrypt volume header
+ SecureBuffer newHeaderBuffer (normalVolume->GetLayout()->GetHeaderSize());
+ Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, normalVolume->GetHeader(), normalVolumeMountOptions.Password, normalVolumeMountOptions.Keyfiles);
+
+ backupFile.Write (newHeaderBuffer);
+
+ if (hiddenVolume)
+ {
+ // Re-encrypt hidden volume header
+ Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, hiddenVolume->GetHeader(), hiddenVolumeMountOptions.Password, hiddenVolumeMountOptions.Keyfiles);
+ }
+ else
+ {
+ // Store random data in place of hidden volume header
+ shared_ptr <EncryptionAlgorithm> ea = normalVolume->GetEncryptionAlgorithm();
+ Core->RandomizeEncryptionAlgorithmKey (ea);
+ ea->Encrypt (newHeaderBuffer);
+ }
+
+ backupFile.Write (newHeaderBuffer);
+
+ ShowString (L"\n");
+ ShowInfo ("VOL_HEADER_BACKED_UP");
+ }
+
+ void TextUserInterface::ChangePassword (shared_ptr <VolumePath> volumePath, shared_ptr <VolumePassword> password, shared_ptr <KeyfileList> keyfiles, shared_ptr <VolumePassword> newPassword, shared_ptr <KeyfileList> newKeyfiles, shared_ptr <Hash> newHash) const
+ {
+ shared_ptr <Volume> volume;
+
+ // Volume path
+ if (!volumePath.get())
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ volumePath = AskVolumePath ();
+ }
+
+ if (volumePath->IsEmpty())
+ throw UserAbort (SRC_POS);
+
+ bool passwordInteractive = !password.get();
+ bool keyfilesInteractive = !keyfiles.get();
+
+ while (true)
+ {
+ // Current password
+ if (!passwordInteractive)
+ {
+ try
+ {
+ password->CheckPortability();
+ }
+ catch (UnportablePassword &)
+ {
+ ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
+ }
+ }
+ else if (!Preferences.NonInteractive)
+ {
+ password = AskPassword ();
+ }
+
+ // Current keyfiles
+ try
+ {
+ if (keyfilesInteractive)
+ {
+ // Ask for keyfiles only if required
+ try
+ {
+ keyfiles.reset (new KeyfileList);
+ volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, keyfiles);
+ }
+ catch (PasswordException&)
+ {
+ if (!Preferences.NonInteractive)
+ keyfiles = AskKeyfiles ();
+ }
+ }
+
+ if (!volume.get())
+ volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, keyfiles);
+ }
+ catch (PasswordException &e)
+ {
+ if (Preferences.NonInteractive || !passwordInteractive || !keyfilesInteractive)
+ throw;
+
+ ShowInfo (e);
+ continue;
+ }
+
+ break;
+ }
+
+ // New password
+ if (newPassword.get())
+ newPassword->CheckPortability();
+ else if (!Preferences.NonInteractive)
+ newPassword = AskPassword (_("Enter new password"), true);
+
+ // New keyfiles
+ if (!newKeyfiles.get() && !Preferences.NonInteractive)
+ {
+ if (keyfiles.get() && keyfiles->size() > 0 && AskYesNo (_("Keep current keyfiles?"), true))
+ newKeyfiles = keyfiles;
+ else
+ newKeyfiles = AskKeyfiles (_("Enter new keyfile"));
+ }
+
+ UserEnrichRandomPool();
+
+ Core->ChangePassword (volume, newPassword, newKeyfiles,
+ newHash ? Pkcs5Kdf::GetAlgorithm (*newHash) : shared_ptr <Pkcs5Kdf>());
+
+ ShowInfo ("PASSWORD_CHANGED");
+ }
+
+ void TextUserInterface::CreateKeyfile (shared_ptr <FilePath> keyfilePath) const
+ {
+ FilePath path;
+
+ RandomNumberGenerator::Start();
+ UserEnrichRandomPool();
+
+ if (keyfilePath)
+ {
+ Core->CreateKeyfile (*keyfilePath);
+ }
+ else
+ {
+ wstring fileName = AskFilePath();
+ if (fileName.empty())
+ return;
+
+ Core->CreateKeyfile (fileName);
+ }
+
+ ShowInfo ("KEYFILE_CREATED");
+ }
+
+ void TextUserInterface::CreateVolume (shared_ptr <VolumeCreationOptions> options) const
+ {
+ // Volume type
+ if (options->Type == VolumeType::Unknown)
+ {
+ if (Preferences.NonInteractive)
+ {
+ options->Type = VolumeType::Normal;
+ }
+ else
+ {
+ ShowString (_("Volume type:\n 1) Normal\n 2) Hidden\n"));
+
+ switch (AskSelection (2, 1))
+ {
+ case 1:
+ options->Type = VolumeType::Normal;
+ break;
+
+ case 2:
+ options->Type = VolumeType::Hidden;
+ break;
+ }
+ }
+ }
+
+ shared_ptr <VolumeLayout> layout;
+ if (options->Type == VolumeType::Hidden)
+ layout.reset (new VolumeLayoutV2Hidden);
+ else
+ layout.reset (new VolumeLayoutV2Normal);
+
+ if (!Preferences.NonInteractive && options->Type == VolumeType::Hidden)
+ ShowInfo (_("\nIMPORTANT: Inexperienced users should use the graphical user interface to create a hidden volume. When using the text interface, the procedure described in the command line help must be followed to create a hidden volume."));
+
+ // Volume path
+ if (options->Path.IsEmpty())
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ do
+ {
+ ShowString (L"\n");
+ options->Path = VolumePath (*AskVolumePath());
+ } while (options->Path.IsEmpty());
+ }
+
+ // Sector size
+ if (options->Path.IsDevice())
+ options->SectorSize = Core->GetDeviceSectorSize (options->Path);
+ else
+ options->SectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME;
+
+ // Volume size
+ uint64 hostSize = 0;
+
+ if (options->Type == VolumeType::Hidden)
+ {
+ FilesystemPath fsPath (wstring (options->Path));
+
+ if (fsPath.IsFile())
+ {
+ File file;
+ file.Open (fsPath);
+ hostSize = file.Length();
+ }
+ else if (fsPath.IsDevice())
+ {
+ hostSize = Core->GetDeviceSize (fsPath);
+ }
+ else
+ {
+ throw_err (_("Hidden volume can be created only in an existing file or device."));
+ }
+
+ if (hostSize < TC_MIN_HIDDEN_VOLUME_HOST_SIZE)
+ throw_err (StringFormatter (_("Minimum outer volume size is {0}."), SizeToString (TC_MIN_HIDDEN_VOLUME_HOST_SIZE)));
+ }
+
+ uint64 minVolumeSize = options->Type == VolumeType::Hidden ? TC_MIN_HIDDEN_VOLUME_SIZE : TC_MIN_VOLUME_SIZE;
+ uint64 maxVolumeSize = options->Type == VolumeType::Hidden ? VolumeLayoutV2Normal().GetMaxDataSize (hostSize) - TC_MIN_FAT_FS_SIZE : TC_MAX_VOLUME_SIZE_GENERAL;
+
+ if (options->Path.IsDevice() && options->Type != VolumeType::Hidden)
+ {
+ if (options->Size != 0)
+ throw_err (_("Volume size cannot be changed for device-hosted volumes."));
+
+ options->Size = Core->GetDeviceSize (options->Path);
+ }
+ else
+ {
+ options->Quick = false;
+
+ uint32 sectorSizeRem = options->Size % options->SectorSize;
+ if (sectorSizeRem != 0)
+ options->Size += options->SectorSize - sectorSizeRem;
+
+ while (options->Size == 0)
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ wstring sizeStr = AskString (options->Type == VolumeType::Hidden ? _("\nEnter hidden volume size (sizeK/size[M]/sizeG): ") : _("\nEnter volume size (sizeK/size[M]/sizeG): "));
+ int multiplier = 1024 * 1024;
+
+ if (sizeStr.find (L"K") != string::npos)
+ {
+ multiplier = 1024;
+ sizeStr.resize (sizeStr.size() - 1);
+ }
+ else if (sizeStr.find (L"M") != string::npos)
+ {
+ sizeStr.resize (sizeStr.size() - 1);
+ }
+ else if (sizeStr.find (L"G") != string::npos)
+ {
+ multiplier = 1024 * 1024 * 1024;
+ sizeStr.resize (sizeStr.size() - 1);
+ }
+
+ try
+ {
+ options->Size = StringConverter::ToUInt64 (sizeStr);
+ options->Size *= multiplier;
+
+ sectorSizeRem = options->Size % options->SectorSize;
+ if (sectorSizeRem != 0)
+ options->Size += options->SectorSize - sectorSizeRem;
+ }
+ catch (...)
+ {
+ options->Size = 0;
+ continue;
+ }
+
+ if (options->Size < minVolumeSize)
+ {
+ ShowError (StringFormatter (_("Minimum volume size is {0}."), SizeToString (minVolumeSize)));
+ options->Size = 0;
+ }
+
+ if (options->Size > maxVolumeSize)
+ {
+ ShowError (StringFormatter (_("Maximum volume size is {0}."), SizeToString (maxVolumeSize)));
+ options->Size = 0;
+ }
+ }
+ }
+
+ if (options->Size < minVolumeSize || options->Size > maxVolumeSize)
+ throw_err (_("Incorrect volume size"));
+
+ if (options->Type == VolumeType::Hidden)
+ options->Quick = true;
+
+ // Encryption algorithm
+ if (!options->EA)
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ ShowInfo (wxString (L"\n") + LangString["ENCRYPTION_ALGORITHM_LV"] + L":");
+
+ vector < shared_ptr <EncryptionAlgorithm> > encryptionAlgorithms;
+ foreach (shared_ptr <EncryptionAlgorithm> ea, EncryptionAlgorithm::GetAvailableAlgorithms())
+ {
+ if (!ea->IsDeprecated())
+ {
+ ShowString (StringFormatter (L" {0}) {1}\n", (uint32) encryptionAlgorithms.size() + 1, ea->GetName()));
+ encryptionAlgorithms.push_back (ea);
+ }
+ }
+
+
+ options->EA = encryptionAlgorithms[AskSelection (encryptionAlgorithms.size(), 1) - 1];
+ }
+
+ // Hash algorithm
+ if (!options->VolumeHeaderKdf)
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ ShowInfo (_("\nHash algorithm:"));
+
+ vector < shared_ptr <Hash> > hashes;
+ foreach (shared_ptr <Hash> hash, Hash::GetAvailableAlgorithms())
+ {
+ if (!hash->IsDeprecated())
+ {
+ ShowString (StringFormatter (L" {0}) {1}\n", (uint32) hashes.size() + 1, hash->GetName()));
+ hashes.push_back (hash);
+ }
+ }
+
+ shared_ptr <Hash> selectedHash = hashes[AskSelection (hashes.size(), 1) - 1];
+ RandomNumberGenerator::SetHash (selectedHash);
+ options->VolumeHeaderKdf = Pkcs5Kdf::GetAlgorithm (*selectedHash);
+
+ }
+
+ // Filesystem
+ options->FilesystemClusterSize = 0;
+
+ if (options->Filesystem == VolumeCreationOptions::FilesystemType::Unknown)
+ {
+ if (Preferences.NonInteractive)
+ {
+ options->Filesystem = VolumeCreationOptions::FilesystemType::GetPlatformNative();
+ }
+ else
+ {
+ ShowInfo (_("\nFilesystem:"));
+
+ vector <VolumeCreationOptions::FilesystemType::Enum> filesystems;
+
+ ShowInfo (L" 1) " + LangString["NONE"]); filesystems.push_back (VolumeCreationOptions::FilesystemType::None);
+ ShowInfo (L" 2) FAT"); filesystems.push_back (VolumeCreationOptions::FilesystemType::FAT);
+
+#if defined (TC_LINUX)
+ ShowInfo (L" 3) Linux Ext2"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext2);
+ ShowInfo (L" 4) Linux Ext3"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext3);
+ ShowInfo (L" 5) Linux Ext4"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext4);
+#elif defined (TC_MACOSX)
+ ShowInfo (L" 3) Mac OS Extended"); filesystems.push_back (VolumeCreationOptions::FilesystemType::MacOsExt);
+#elif defined (TC_FREEBSD) || defined (TC_SOLARIS)
+ ShowInfo (L" 3) UFS"); filesystems.push_back (VolumeCreationOptions::FilesystemType::UFS);
+#endif
+
+ options->Filesystem = filesystems[AskSelection (filesystems.size(), 2) - 1];
+ }
+ }
+
+ uint64 filesystemSize = layout->GetMaxDataSize (options->Size);
+
+ if (options->Filesystem == VolumeCreationOptions::FilesystemType::FAT
+ && (filesystemSize < TC_MIN_FAT_FS_SIZE || filesystemSize > TC_MAX_FAT_SECTOR_COUNT * options->SectorSize))
+ {
+ throw_err (_("Specified volume size cannot be used with FAT filesystem."));
+ }
+
+ // Password
+ if (!options->Password && !Preferences.NonInteractive)
+ {
+ ShowString (L"\n");
+ options->Password = AskPassword (_("Enter password"), true);
+ }
+
+ if (options->Password)
+ options->Password->CheckPortability();
+
+ // Keyfiles
+ if (!options->Keyfiles && !Preferences.NonInteractive)
+ {
+ ShowString (L"\n");
+ options->Keyfiles = AskKeyfiles (_("Enter keyfile path"));
+ }
+
+ if ((!options->Keyfiles || options->Keyfiles->empty())
+ && (!options->Password || options->Password->IsEmpty()))
+ {
+ throw_err (_("Password cannot be empty when no keyfile is specified"));
+ }
+
+ // Random data
+ RandomNumberGenerator::Start();
+ UserEnrichRandomPool();
+
+ ShowString (L"\n");
+ wxLongLong startTime = wxGetLocalTimeMillis();
+
+ VolumeCreator creator;
+ creator.CreateVolume (options);
+
+ bool volumeCreated = false;
+ while (!volumeCreated)
+ {
+ VolumeCreator::ProgressInfo progress = creator.GetProgressInfo();
+
+ wxLongLong timeDiff = wxGetLocalTimeMillis() - startTime;
+ if (timeDiff.GetValue() > 0)
+ {
+ uint64 speed = progress.SizeDone * 1000 / timeDiff.GetValue();
+
+ volumeCreated = !progress.CreationInProgress;
+
+ ShowString (wxString::Format (L"\rDone: %7.3f%% Speed: %9s Left: %s ",
+ 100.0 - double (options->Size - progress.SizeDone) / (double (options->Size) / 100.0),
+ speed > 0 ? SpeedToString (speed).c_str() : L" ",
+ speed > 0 ? TimeSpanToString ((options->Size - progress.SizeDone) / speed).c_str() : L""));
+ }
+
+ Thread::Sleep (100);
+ }
+
+ ShowString (L"\n\n");
+ creator.CheckResult();
+
+#ifdef TC_UNIX
+ if (options->Filesystem != VolumeCreationOptions::FilesystemType::None
+ && options->Filesystem != VolumeCreationOptions::FilesystemType::FAT)
+ {
+ const char *fsFormatter = nullptr;
+
+ switch (options->Filesystem)
+ {
+ case VolumeCreationOptions::FilesystemType::Ext2: fsFormatter = "mkfs.ext2"; break;
+ case VolumeCreationOptions::FilesystemType::Ext3: fsFormatter = "mkfs.ext3"; break;
+ case VolumeCreationOptions::FilesystemType::Ext4: fsFormatter = "mkfs.ext4"; break;
+ case VolumeCreationOptions::FilesystemType::MacOsExt: fsFormatter = "newfs_hfs"; break;
+ case VolumeCreationOptions::FilesystemType::UFS: fsFormatter = "newfs" ; break;
+ default: throw ParameterIncorrect (SRC_POS);
+ }
+
+ MountOptions mountOptions (GetPreferences().DefaultMountOptions);
+ mountOptions.Path = make_shared <VolumePath> (options->Path);
+ mountOptions.NoFilesystem = true;
+ mountOptions.Protection = VolumeProtection::None;
+ mountOptions.Password = options->Password;
+ mountOptions.Keyfiles = options->Keyfiles;
+
+ shared_ptr <VolumeInfo> volume = Core->MountVolume (mountOptions);
+ finally_do_arg (shared_ptr <VolumeInfo>, volume, { Core->DismountVolume (finally_arg, true); });
+
+ Thread::Sleep (2000); // Try to prevent race conditions caused by OS
+
+ // Temporarily take ownership of the device if the user is not an administrator
+ UserId origDeviceOwner ((uid_t) -1);
+
+ DevicePath virtualDevice = volume->VirtualDevice;
+#ifdef TC_MACOSX
+ string virtualDeviceStr = virtualDevice;
+ if (virtualDeviceStr.find ("/dev/rdisk") != 0)
+ virtualDevice = "/dev/r" + virtualDeviceStr.substr (5);
+#endif
+ try
+ {
+ File file;
+ file.Open (virtualDevice, File::OpenReadWrite);
+ }
+ catch (...)
+ {
+ if (!Core->HasAdminPrivileges())
+ {
+ origDeviceOwner = virtualDevice.GetOwner();
+ Core->SetFileOwner (virtualDevice, UserId (getuid()));
+ }
+ }
+
+ finally_do_arg2 (FilesystemPath, virtualDevice, UserId, origDeviceOwner,
+ {
+ if (finally_arg2.SystemId != (uid_t) -1)
+ Core->SetFileOwner (finally_arg, finally_arg2);
+ });
+
+ // Create filesystem
+ list <string> args;
+
+ if (options->Filesystem == VolumeCreationOptions::FilesystemType::MacOsExt && options->Size >= 10 * BYTES_PER_MB)
+ args.push_back ("-J");
+
+ args.push_back (string (virtualDevice));
+
+ Process::Execute (fsFormatter, args);
+ }
+#endif // TC_UNIX
+
+ ShowInfo (options->Type == VolumeType::Hidden ? "HIDVOL_FORMAT_FINISHED_HELP" : "FORMAT_FINISHED_INFO");
+ }
+
+ void TextUserInterface::DeleteSecurityTokenKeyfiles () const
+ {
+ shared_ptr <KeyfileList> keyfiles = AskKeyfiles();
+ if (keyfiles->empty())
+ throw UserAbort();
+
+ foreach_ref (const Keyfile &keyfile, *keyfiles)
+ {
+ SecurityToken::DeleteKeyfile (SecurityTokenKeyfilePath (FilePath (keyfile)));
+ }
+ }
+
+ void TextUserInterface::DoShowError (const wxString &message) const
+ {
+ wcerr << L"Error: " << static_cast<wstring> (message) << endl;
+ }
+
+ void TextUserInterface::DoShowInfo (const wxString &message) const
+ {
+ wcout << static_cast<wstring> (message) << endl;
+ }
+
+ void TextUserInterface::DoShowString (const wxString &str) const
+ {
+ wcout << str.c_str();
+ }
+
+ void TextUserInterface::DoShowWarning (const wxString &message) const
+ {
+ wcerr << L"Warning: " << static_cast<wstring> (message) << endl;
+ }
+
+ void TextUserInterface::ExportSecurityTokenKeyfile () const
+ {
+ wstring keyfilePath = AskString (_("Enter security token keyfile path: "));
+
+ if (keyfilePath.empty())
+ throw UserAbort (SRC_POS);
+
+ SecurityTokenKeyfile tokenKeyfile (keyfilePath);
+
+ vector <byte> keyfileData;
+ SecurityToken::GetKeyfileData (tokenKeyfile, keyfileData);
+
+ BufferPtr keyfileDataBuf (&keyfileData.front(), keyfileData.size());
+ finally_do_arg (BufferPtr, keyfileDataBuf, { finally_arg.Erase(); });
+
+ FilePath exportFilePath = AskFilePath();
+
+ if (exportFilePath.IsEmpty())
+ throw UserAbort (SRC_POS);
+
+ File keyfile;
+ keyfile.Open (exportFilePath, File::CreateWrite);
+ keyfile.Write (keyfileDataBuf);
+ }
+
+ shared_ptr <GetStringFunctor> TextUserInterface::GetAdminPasswordRequestHandler ()
+ {
+ struct AdminPasswordRequestHandler : public GetStringFunctor
+ {
+ AdminPasswordRequestHandler (TextUserInterface *userInterface) : UI (userInterface) { }
+ virtual void operator() (string &passwordStr)
+ {
+ UI->ShowString (_("Enter your user password or administrator password: "));
+
+ TextUserInterface::SetTerminalEcho (false);
+ finally_do ({ TextUserInterface::SetTerminalEcho (true); });
+
+ wstring wPassword (UI->ReadInputStreamLine());
+ finally_do_arg (wstring *, &wPassword, { StringConverter::Erase (*finally_arg); });
+
+ UI->ShowString (L"\n");
+
+ StringConverter::ToSingle (wPassword, passwordStr);
+ }
+ TextUserInterface *UI;
+ };
+
+ return shared_ptr <GetStringFunctor> (new AdminPasswordRequestHandler (this));
+ }
+
+ void TextUserInterface::ImportSecurityTokenKeyfiles () const
+ {
+ list <SecurityTokenInfo> tokens = SecurityToken::GetAvailableTokens();
+
+ if (tokens.empty())
+ throw_err (LangString ["NO_TOKENS_FOUND"]);
+
+ CK_SLOT_ID slotId;
+
+ if (tokens.size() == 1)
+ {
+ slotId = tokens.front().SlotId;
+ }
+ else
+ {
+ foreach (const SecurityTokenInfo &token, tokens)
+ {
+ wstringstream tokenLabel;
+ tokenLabel << L"[" << token.SlotId << L"] " << LangString["TOKEN_SLOT_ID"].c_str() << L" " << token.SlotId << L" " << token.Label;
+
+ ShowInfo (tokenLabel.str());
+ }
+
+ slotId = (CK_SLOT_ID) AskSelection (tokens.back().SlotId, tokens.front().SlotId);
+ }
+
+ shared_ptr <KeyfileList> keyfiles = AskKeyfiles();
+ if (keyfiles->empty())
+ throw UserAbort();
+
+ foreach_ref (const Keyfile &keyfilePath, *keyfiles)
+ {
+ File keyfile;
+ keyfile.Open (keyfilePath, File::OpenRead, File::ShareReadWrite, File::PreserveTimestamps);
+
+ if (keyfile.Length() > 0)
+ {
+ vector <byte> keyfileData (keyfile.Length());
+ BufferPtr keyfileDataBuf (&keyfileData.front(), keyfileData.size());
+
+ keyfile.ReadCompleteBuffer (keyfileDataBuf);
+ finally_do_arg (BufferPtr, keyfileDataBuf, { finally_arg.Erase(); });
+
+ SecurityToken::CreateKeyfile (slotId, keyfileData, string (FilePath (keyfilePath).ToBaseName()));
+ }
+ else
+ throw InsufficientData (SRC_POS, FilePath (keyfilePath));
+ }
+ }
+
+ void TextUserInterface::InitSecurityTokenLibrary () const
+ {
+ if (Preferences.SecurityTokenModule.IsEmpty())
+ throw_err (LangString ["NO_PKCS11_MODULE_SPECIFIED"]);
+
+ struct PinRequestHandler : public GetPinFunctor
+ {
+ PinRequestHandler (const TextUserInterface *userInterface) : UI (userInterface) { }
+
+ virtual void operator() (string &passwordStr)
+ {
+ if (UI->GetPreferences().NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ UI->ShowString (wxString::Format (LangString["ENTER_TOKEN_PASSWORD"], StringConverter::ToWide (passwordStr).c_str()) + L" ");
+
+ TextUserInterface::SetTerminalEcho (false);
+ finally_do ({ TextUserInterface::SetTerminalEcho (true); });
+
+ wstring wPassword (UI->ReadInputStreamLine());
+ finally_do_arg (wstring *, &wPassword, { StringConverter::Erase (*finally_arg); });
+
+ UI->ShowString (L"\n");
+
+ StringConverter::ToSingle (wPassword, passwordStr);
+ }
+
+ const TextUserInterface *UI;
+ };
+
+ struct WarningHandler : public SendExceptionFunctor
+ {
+ WarningHandler (const TextUserInterface *userInterface) : UI (userInterface) { }
+
+ virtual void operator() (const Exception &e)
+ {
+ UI->ShowError (e);
+ }
+
+ const TextUserInterface *UI;
+ };
+
+ try
+ {
+ SecurityToken::InitLibrary (Preferences.SecurityTokenModule, auto_ptr <GetPinFunctor> (new PinRequestHandler (this)), auto_ptr <SendExceptionFunctor> (new WarningHandler (this)));
+ }
+ catch (Exception &e)
+ {
+ ShowError (e);
+ throw_err (LangString ["PKCS11_MODULE_INIT_FAILED"]);
+ }
+ }
+
+ void TextUserInterface::ListSecurityTokenKeyfiles () const
+ {
+ foreach (const SecurityTokenKeyfile &keyfile, SecurityToken::GetAvailableKeyfiles())
+ {
+ ShowString (wstring (SecurityTokenKeyfilePath (keyfile)));
+ ShowString (L"\n");
+ }
+ }
+
+ VolumeInfoList TextUserInterface::MountAllDeviceHostedVolumes (MountOptions &options) const
+ {
+ while (true)
+ {
+ if (!options.Password)
+ options.Password = AskPassword();
+
+ if (!options.Keyfiles)
+ options.Keyfiles = AskKeyfiles();
+
+ VolumeInfoList mountedVolumes = UserInterface::MountAllDeviceHostedVolumes (options);
+
+ if (!mountedVolumes.empty())
+ return mountedVolumes;
+
+ options.Password.reset();
+ }
+ }
+
+ shared_ptr <VolumeInfo> TextUserInterface::MountVolume (MountOptions &options) const
+ {
+ shared_ptr <VolumeInfo> volume;
+
+ CheckRequirementsForMountingVolume();
+
+ // Volume path
+ while (!options.Path || options.Path->IsEmpty())
+ {
+ if (Preferences.NonInteractive)
+ throw MissingArgument (SRC_POS);
+
+ options.Path = AskVolumePath ();
+ }
+
+ if (Core->IsVolumeMounted (*options.Path))
+ {
+ ShowInfo (StringFormatter (LangString["VOLUME_ALREADY_MOUNTED"], wstring (*options.Path)));
+ return volume;
+ }
+
+ // Mount point
+ if (!options.MountPoint && !options.NoFilesystem)
+ options.MountPoint.reset (new DirectoryPath (AskString (_("Enter mount directory [default]: "))));
+
+ VolumePassword password;
+ KeyfileList keyfiles;
+
+ if ((!options.Password || options.Password->IsEmpty())
+ && (!options.Keyfiles || options.Keyfiles->empty())
+ && !Core->IsPasswordCacheEmpty())
+ {
+ // Cached password
+ try
+ {
+ volume = UserInterface::MountVolume (options);
+ }
+ catch (PasswordException&) { }
+ }
+
+ int incorrectPasswordCount = 0;
+
+ while (!volume)
+ {
+ // Password
+ if (!options.Password)
+ {
+ options.Password = AskPassword (StringFormatter (_("Enter password for {0}"), wstring (*options.Path)));
+ }
+ else
+ {
+ try
+ {
+ if (options.Password)
+ options.Password->CheckPortability();
+ }
+ catch (UnportablePassword &)
+ {
+ ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
+ }
+ }
+
+ // Keyfiles
+ if (!options.Keyfiles)
+ options.Keyfiles = AskKeyfiles();
+
+ // Hidden volume protection
+ if (options.Protection == VolumeProtection::None
+ && !CmdLine->ArgNoHiddenVolumeProtection
+ && AskYesNo (_("Protect hidden volume (if any)?")))
+ options.Protection = VolumeProtection::HiddenVolumeReadOnly;
+
+ if (options.Protection == VolumeProtection::HiddenVolumeReadOnly)
+ {
+ if (!options.ProtectionPassword)
+ options.ProtectionPassword = AskPassword (_("Enter password for hidden volume"));
+ if (!options.ProtectionKeyfiles)
+ options.ProtectionKeyfiles = AskKeyfiles (_("Enter keyfile for hidden volume"));
+ }
+
+ try
+ {
+ volume = UserInterface::MountVolume (options);
+ }
+ catch (ProtectionPasswordIncorrect &e)
+ {
+ ShowInfo (e);
+ options.ProtectionPassword.reset();
+ }
+ catch (PasswordIncorrect &e)
+ {
+ if (++incorrectPasswordCount > 2 && !options.UseBackupHeaders)
+ {
+ // Try to mount the volume using the backup header
+ options.UseBackupHeaders = true;
+
+ try
+ {
+ volume = UserInterface::MountVolume (options);
+ ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
+ }
+ catch (...)
+ {
+ options.UseBackupHeaders = false;
+ ShowInfo (e);
+ options.Password.reset();
+ }
+ }
+ else
+ {
+ ShowInfo (e);
+ options.Password.reset();
+ }
+
+ ShowString (L"\n");
+ }
+ catch (PasswordException &e)
+ {
+ ShowInfo (e);
+ options.Password.reset();
+ }
+ }
+
+#ifdef TC_LINUX
+ if (!Preferences.NonInteractive && !Preferences.DisableKernelEncryptionModeWarning
+ && volume->EncryptionModeName != L"XTS"
+ && (volume->EncryptionModeName != L"LRW" || volume->EncryptionAlgorithmMinBlockSize != 16 || volume->EncryptionAlgorithmKeySize != 32))
+ {
+ ShowWarning (LangString["ENCRYPTION_MODE_NOT_SUPPORTED_BY_KERNEL"]);
+ }
+#endif
+
+ return volume;
+ }
+
+ bool TextUserInterface::OnInit ()
+ {
+ try
+ {
+ DefaultMessageOutput = new wxMessageOutputStderr;
+ wxMessageOutput::Set (DefaultMessageOutput);
+
+ InterfaceType = UserInterfaceType::Text;
+ Init();
+ }
+ catch (exception &e)
+ {
+ ShowError (e);
+ return false;
+ }
+ return true;
+ }
+
+ int TextUserInterface::OnRun()
+ {
+ try
+ {
+ if (ProcessCommandLine ())
+ {
+ Application::SetExitCode (0);
+ return 0;
+ }
+ }
+ catch (exception &e)
+ {
+ ShowError (e);
+ }
+
+ Application::SetExitCode (1);
+ return 1;
+ }
+
+ void TextUserInterface::OnSignal (int signal)
+ {
+#ifdef TC_UNIX
+ try
+ {
+ SetTerminalEcho (true);
+ }
+ catch (...) { }
+ _exit (1);
+#endif
+ }
+
+ void TextUserInterface::ReadInputStreamLine (wxString &line) const
+ {
+ if (!TextInputStream.get() || feof (stdin) || ferror (stdin))
+ throw UserAbort (SRC_POS);
+
+ line = TextInputStream->ReadLine();
+
+ if (ferror (stdin) || (line.empty() && feof (stdin)))
+ throw UserAbort (SRC_POS);
+ }
+
+ wxString TextUserInterface::ReadInputStreamLine () const
+ {
+ wxString line;
+ ReadInputStreamLine (line);
+ return line;
+ }
+
+ void TextUserInterface::RestoreVolumeHeaders (shared_ptr <VolumePath> volumePath) const
+ {
+ if (!volumePath)
+ volumePath = AskVolumePath();
+
+ if (!volumePath)
+ throw UserAbort (SRC_POS);
+
+#ifdef TC_WINDOWS
+ if (Core->IsVolumeMounted (*volumePath))
+ throw_err (LangString["DISMOUNT_FIRST"]);
+#endif
+
+ // Ask whether to restore internal or external backup
+ bool restoreInternalBackup;
+
+ ShowInfo (LangString["HEADER_RESTORE_EXTERNAL_INTERNAL"]);
+ ShowInfo (L"\n1) " + LangString["HEADER_RESTORE_INTERNAL"]);
+ ShowInfo (L"2) " + LangString["HEADER_RESTORE_EXTERNAL"] + L"\n");
+
+ switch (AskSelection (2))
+ {
+ case 1:
+ restoreInternalBackup = true;
+ break;
+
+ case 2:
+ restoreInternalBackup = false;
+ break;
+
+ default:
+ throw UserAbort (SRC_POS);
+ }
+
+ if (restoreInternalBackup)
+ {
+ // Restore header from the internal backup
+ shared_ptr <Volume> volume;
+ MountOptions options;
+ options.Path = volumePath;
+
+ while (!volume)
+ {
+ ShowString (L"\n");
+ options.Password = AskPassword();
+ options.Keyfiles = AskKeyfiles();
+
+ try
+ {
+ volume = Core->OpenVolume (
+ options.Path,
+ options.PreserveTimestamps,
+ options.Password,
+ options.Keyfiles,
+ options.Protection,
+ options.ProtectionPassword,
+ options.ProtectionKeyfiles,
+ options.SharedAccessAllowed,
+ VolumeType::Unknown,
+ true
+ );
+ }
+ catch (PasswordException &e)
+ {
+ ShowInfo (e);
+ }
+ }
+
+ shared_ptr <VolumeLayout> layout = volume->GetLayout();
+ if (typeid (*layout) == typeid (VolumeLayoutV1Normal) || typeid (*layout) == typeid (VolumeLayoutV1Hidden))
+ {
+ throw_err (LangString ["VOLUME_HAS_NO_BACKUP_HEADER"]);
+ }
+
+ RandomNumberGenerator::Start();
+ UserEnrichRandomPool();
+
+ // Re-encrypt volume header
+ SecureBuffer newHeaderBuffer (volume->GetLayout()->GetHeaderSize());
+ Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, volume->GetHeader(), options.Password, options.Keyfiles);
+
+ // Write volume header
+ int headerOffset = volume->GetLayout()->GetHeaderOffset();
+ shared_ptr <File> volumeFile = volume->GetFile();
+
+ if (headerOffset >= 0)
+ volumeFile->SeekAt (headerOffset);
+ else
+ volumeFile->SeekEnd (headerOffset);
+
+ volumeFile->Write (newHeaderBuffer);
+ }
+ else
+ {
+ // Restore header from an external backup
+
+ wxString confirmMsg = L"\n\n" + LangString["CONFIRM_VOL_HEADER_RESTORE"];
+ confirmMsg.Replace (L"%hs", L"%s");
+
+ if (!AskYesNo (wxString::Format (confirmMsg, wstring (*volumePath).c_str()), true, true))
+ return;
+
+ ShowString (L"\n");
+
+ FilePath filePath = AskFilePath();
+ if (filePath.IsEmpty())
+ throw UserAbort (SRC_POS);
+
+ File backupFile;
+ backupFile.Open (filePath, File::OpenRead);
+
+ uint64 headerSize;
+ bool legacyBackup;
+
+ // Determine the format of the backup file
+ switch (backupFile.Length())
+ {
+ case TC_VOLUME_HEADER_GROUP_SIZE:
+ headerSize = TC_VOLUME_HEADER_SIZE;
+ legacyBackup = false;
+ break;
+
+ case TC_VOLUME_HEADER_SIZE_LEGACY * 2:
+ headerSize = TC_VOLUME_HEADER_SIZE_LEGACY;
+ legacyBackup = true;
+ break;
+
+ default:
+ throw_err (LangString ["HEADER_BACKUP_SIZE_INCORRECT"]);
+ }
+
+ // Open the volume header stored in the backup file
+ MountOptions options;
+
+ shared_ptr <VolumeLayout> decryptedLayout;
+
+ while (!decryptedLayout)
+ {
+ options.Password = AskPassword (L"\n" + LangString["ENTER_HEADER_BACKUP_PASSWORD"]);
+ options.Keyfiles = AskKeyfiles();
+
+ try
+ {
+ // Test volume layouts
+ foreach (shared_ptr <VolumeLayout> layout, VolumeLayout::GetAvailableLayouts ())
+ {
+ if (layout->HasDriveHeader())
+ continue;
+
+ if (!legacyBackup && (typeid (*layout) == typeid (VolumeLayoutV1Normal) || typeid (*layout) == typeid (VolumeLayoutV1Hidden)))
+ continue;
+
+ if (legacyBackup && (typeid (*layout) == typeid (VolumeLayoutV2Normal) || typeid (*layout) == typeid (VolumeLayoutV2Hidden)))
+ continue;
+
+ SecureBuffer headerBuffer (layout->GetHeaderSize());
+ backupFile.ReadAt (headerBuffer, layout->GetType() == VolumeType::Hidden ? layout->GetHeaderSize() : 0);
+
+ // Decrypt header
+ shared_ptr <VolumePassword> passwordKey = Keyfile::ApplyListToPassword (options.Keyfiles, options.Password);
+ if (layout->GetHeader()->Decrypt (headerBuffer, *passwordKey, layout->GetSupportedKeyDerivationFunctions(), layout->GetSupportedEncryptionAlgorithms(), layout->GetSupportedEncryptionModes()))
+ {
+ decryptedLayout = layout;
+ break;
+ }
+ }
+
+ if (!decryptedLayout)
+ throw PasswordIncorrect (SRC_POS);
+ }
+ catch (PasswordException &e)
+ {
+ ShowWarning (e);
+ }
+ }
+
+ File volumeFile;
+ volumeFile.Open (*volumePath, File::OpenReadWrite, File::ShareNone, File::PreserveTimestamps);
+
+ RandomNumberGenerator::Start();
+ UserEnrichRandomPool();
+
+ // Re-encrypt volume header
+ SecureBuffer newHeaderBuffer (decryptedLayout->GetHeaderSize());
+ Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, decryptedLayout->GetHeader(), options.Password, options.Keyfiles);
+
+ // Write volume header
+ int headerOffset = decryptedLayout->GetHeaderOffset();
+ if (headerOffset >= 0)
+ volumeFile.SeekAt (headerOffset);
+ else
+ volumeFile.SeekEnd (headerOffset);
+
+ volumeFile.Write (newHeaderBuffer);
+
+ if (decryptedLayout->HasBackupHeader())
+ {
+ // Re-encrypt backup volume header
+ Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, decryptedLayout->GetHeader(), options.Password, options.Keyfiles);
+
+ // Write backup volume header
+ headerOffset = decryptedLayout->GetBackupHeaderOffset();
+ if (headerOffset >= 0)
+ volumeFile.SeekAt (headerOffset);
+ else
+ volumeFile.SeekEnd (headerOffset);
+
+ volumeFile.Write (newHeaderBuffer);
+ }
+ }
+
+ ShowString (L"\n");
+ ShowInfo ("VOL_HEADER_RESTORED");
+ }
+
+ void TextUserInterface::SetTerminalEcho (bool enable)
+ {
+ if (CmdLine->ArgDisplayPassword)
+ return;
+
+#ifdef TC_UNIX
+ struct termios termAttr;
+ if (tcgetattr (0, &termAttr) == 0)
+ {
+ if (!enable)
+ {
+ termAttr.c_lflag &= ~ECHO;
+ throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
+ }
+ else
+ {
+ termAttr.c_lflag |= ECHO;
+ throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
+ }
+ }
+#endif
+ }
+
+ void TextUserInterface::UserEnrichRandomPool () const
+ {
+ RandomNumberGenerator::Start();
+
+ if (RandomNumberGenerator::IsEnrichedByUser())
+ return;
+
+ if (CmdLine->ArgHash)
+ RandomNumberGenerator::SetHash (CmdLine->ArgHash);
+
+ if (!CmdLine->ArgRandomSourcePath.IsEmpty())
+ {
+ SecureBuffer buffer (RandomNumberGenerator::PoolSize);
+ File randSourceFile;
+
+ randSourceFile.Open (CmdLine->ArgRandomSourcePath, File::OpenRead);
+
+ for (size_t i = 0; i < buffer.Size(); ++i)
+ {
+ if (randSourceFile.Read (buffer.GetRange (i, 1)) < 1)
+ break;
+ }
+
+ RandomNumberGenerator::AddToPool (buffer);
+ RandomNumberGenerator::SetEnrichedByUserStatus (true);
+ }
+ else if (!Preferences.NonInteractive)
+ {
+ int randCharsRequired = RandomNumberGenerator::PoolSize;
+ ShowInfo (StringFormatter (_("\nPlease type at least {0} randomly chosen characters and then press Enter:"), randCharsRequired));
+
+ SetTerminalEcho (false);
+ finally_do ({ TextUserInterface::SetTerminalEcho (true); });
+
+ while (randCharsRequired > 0)
+ {
+ wstring randStr = AskString();
+ RandomNumberGenerator::AddToPool (ConstBufferPtr ((byte *) randStr.c_str(), randStr.size() * sizeof (wchar_t)));
+
+ randCharsRequired -= randStr.size();
+
+ if (randCharsRequired > 0)
+ ShowInfo (StringFormatter (_("Characters remaining: {0}"), randCharsRequired));
+ }
+
+ ShowString (L"\n");
+ RandomNumberGenerator::SetEnrichedByUserStatus (true);
+ }
+ }
+
+ wxMessageOutput *DefaultMessageOutput;
+}