VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Crypto/GostCipher.c
blob: ddd649cdb274b3982472f0f519ac71ff5094fc37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/** @file
GOST89 implementation

Copyright (c) 2016. Disk Cryptography Services for EFI (DCS), Alex Kolotnikov

This program and the accompanying materials
are licensed and made available under the terms and conditions
of the Apache License, Version 2.0.  
The full text of the license may be found at
https://opensource.org/licenses/Apache-2.0

Dynamic SBOX idea is from GostCrypt project. Copyright (c) 2008-2011 TrueCrypt Developers Association
**/



#include "GostCipher.h"
#include "Streebog.h"
#include "cpu.h"

#if defined(CIPHER_GOST89)

// Crypto Pro
byte S_CryptoPro[8][16] = {
	{0x1,0x3,0xA,0x9,0x5,0xB,0x4,0xF,0x8,0x6,0x7,0xE,0xD,0x0,0x2,0xC},
	{0xD,0xE,0x4,0x1,0x7,0x0,0x5,0xA,0x3,0xC,0x8,0xF,0x6,0x2,0x9,0xB},
	{0x7,0x6,0x2,0x4,0xD,0x9,0xF,0x0,0xA,0x1,0x5,0xB,0x8,0xE,0xC,0x3},
	{0x7,0x6,0x4,0xB,0x9,0xC,0x2,0xA,0x1,0x8,0x0,0xE,0xF,0xD,0x3,0x5},
	{0x4,0xA,0x7,0xC,0x0,0xF,0x2,0x8,0xE,0x1,0x6,0x5,0xD,0xB,0x9,0x3},
	{0x7,0xF,0xC,0xE,0x9,0x4,0x1,0x0,0x3,0xB,0x5,0x2,0x6,0xA,0x8,0xD},
	{0x5,0xF,0x4,0x0,0x2,0xD,0xB,0x9,0x1,0x7,0x6,0x3,0xC,0xE,0xA,0x8},
	{0xA,0x4,0x5,0x6,0x8,0x1,0x3,0x7,0xD,0xC,0xE,0x0,0x9,0x2,0xB,0xF}
	};

// TC26
byte S_TC26[8][16] =
{
	{ 0xc, 0x4, 0x6, 0x2, 0xa, 0x5, 0xb, 0x9, 0xe, 0x8, 0xd, 0x7, 0x0, 0x3, 0xf, 0x1 },
	{ 0x6, 0x8, 0x2, 0x3, 0x9, 0xa, 0x5, 0xc, 0x1, 0xe, 0x4, 0x7, 0xb, 0xd, 0x0, 0xf },
	{ 0xb, 0x3, 0x5, 0x8, 0x2, 0xf, 0xa, 0xd, 0xe, 0x1, 0x7, 0x4, 0xc, 0x9, 0x6, 0x0 },
	{ 0xc, 0x8, 0x2, 0x1, 0xd, 0x4, 0xf, 0x6, 0x7, 0x0, 0xa, 0x5, 0x3, 0xe, 0x9, 0xb },
	{ 0x7, 0xf, 0x5, 0xa, 0x8, 0x1, 0x6, 0xd, 0x0, 0x9, 0x3, 0xe, 0xb, 0x4, 0x2, 0xc },
	{ 0x5, 0xd, 0xf, 0x6, 0x9, 0x2, 0xc, 0xa, 0xb, 0x7, 0x8, 0x1, 0x4, 0x3, 0xe, 0x0 },
	{ 0x8, 0xe, 0x2, 0x5, 0x6, 0x9, 0x1, 0xc, 0xf, 0x4, 0xb, 0x0, 0xd, 0xa, 0x3, 0x7 },
	{ 0x1, 0x7, 0xe, 0xd, 0x0, 0x5, 0x8, 0x3, 0x4, 0xf, 0xa, 0x6, 0x9, 0xc, 0xb, 0x2 },
};

void gost_prepare_kds(gost_kds* kds) {
	uint32 i;
	// Build substitution tables. 
	for (i = 0; i < 256; ++i) {
		uint32 p;
		p = kds->sbox[7][i >> 4] << 4 | kds->sbox[6][i & 15];
		p = p << 24; p = p << 11 | p >> 21;
		kds->sbox_cvt[i] = p; // S87

		p = kds->sbox[5][i >> 4] << 4 | kds->sbox[4][i & 15];
		p = p << 16; p = p << 11 | p >> 21;
		kds->sbox_cvt[256 + i] = p; // S65

		p = kds->sbox[3][i >> 4] << 4 | kds->sbox[2][i & 15];
		p = p << 8; p = p << 11 | p >> 21;
		kds->sbox_cvt[256 * 2 + i] = p; // S43

		p = kds->sbox[1][i >> 4] << 4 | kds->sbox[0][i & 15];
		p = p << 11 | p >> 21;
		kds->sbox_cvt[256 * 3 + i] = p; // S21
	}
}


static void xor_s_box(byte s_box[8][16], byte *seed)
{
   int i;
   for (i = 0; i < 16; i++)
   {
      s_box[0][i] ^= (seed[ (i * 4) + 0 ]   ) & 0xF;
      s_box[1][i] ^= (seed[ (i * 4) + 0 ]>>4) & 0xF;
      s_box[2][i] ^= (seed[ (i * 4) + 1 ]   ) & 0xF;
      s_box[3][i] ^= (seed[ (i * 4) + 1 ]>>4) & 0xF;
      s_box[4][i] ^= (seed[ (i * 4) + 2 ]   ) & 0xF;
      s_box[5][i] ^= (seed[ (i * 4) + 2 ]>>4) & 0xF;
      s_box[6][i] ^= (seed[ (i * 4) + 3 ]   ) & 0xF;
      s_box[7][i] ^= (seed[ (i * 4) + 3 ]>>4) & 0xF;
   }
}

void gost_set_key(const byte *key, gost_kds *ks, int useDynamicSbox)
{
	memcpy(ks->key, key, GOST_KEYSIZE);
	memcpy(ks->sbox, S_TC26, sizeof(ks->sbox));

    if (useDynamicSbox)
    {
	    STREEBOG_CTX sctx;
	    byte sbox_seed[64];
#if defined (DEVICE_DRIVER) && !defined (_WIN64)
	    KFLOATING_SAVE floatingPointState;
	    NTSTATUS saveStatus = STATUS_INVALID_PARAMETER;
	    if (HasSSE2() || HasSSE41())
		    saveStatus = KeSaveFloatingPointState (&floatingPointState);
#endif
	    //Generate pseudorandom data based on the key
	    STREEBOG_init(&sctx);
	    STREEBOG_add(&sctx, ks->key, 32);
	    STREEBOG_finalize(&sctx, sbox_seed);

#if defined (DEVICE_DRIVER) && !defined (_WIN64)
	    if (NT_SUCCESS (saveStatus))
		    KeRestoreFloatingPointState (&floatingPointState);
#endif

	    xor_s_box(ks->sbox, sbox_seed);
    }

	gost_prepare_kds(ks);
}

static uint32 f(uint32 v, uint32* sbox){
   byte* x =(byte*) &v;
   /* Do substitutions */
   return sbox[x[3]] | sbox[256 + x[2]] | sbox[256*2 + x[1]] | sbox[256*3 + x[0]];
}

void gost_encrypt_block(uint64 in_, uint64* out_, gost_kds* kds) {
   uint32* in  = (uint32*)&in_;
   uint32* out = (uint32*)out_;
	uint32* key = (uint32*)kds->key;
	uint32* sbox = kds->sbox_cvt;

   // As named in the GOST
   uint32 n1 = in[0];
   uint32 n2 = in[1];

	n2 ^= f(n1+key[0], sbox);
   n1 ^= f(n2+key[1], sbox);
   n2 ^= f(n1+key[2], sbox);
   n1 ^= f(n2+key[3], sbox);
   n2 ^= f(n1+key[4], sbox);
   n1 ^= f(n2+key[5], sbox);
   n2 ^= f(n1+key[6], sbox);
   n1 ^= f(n2+key[7], sbox);

   n2 ^= f(n1+key[0], sbox);
   n1 ^= f(n2+key[1], sbox);
   n2 ^= f(n1+key[2], sbox);
   n1 ^= f(n2+key[3], sbox);
   n2 ^= f(n1+key[4], sbox);
   n1 ^= f(n2+key[5], sbox);
   n2 ^= f(n1+key[6], sbox);
   n1 ^= f(n2+key[7], sbox);

   n2 ^= f(n1+key[0], sbox);
   n1 ^= f(n2+key[1], sbox);
   n2 ^= f(n1+key[2], sbox);
   n1 ^= f(n2+key[3], sbox);
   n2 ^= f(n1+key[4], sbox);
   n1 ^= f(n2+key[5], sbox);
   n2 ^= f(n1+key[6], sbox);
   n1 ^= f(n2+key[7], sbox);

   n2 ^= f(n1+key[7], sbox);
   n1 ^= f(n2+key[6], sbox);
   n2 ^= f(n1+key[5], sbox);
   n1 ^= f(n2+key[4], sbox);
   n2 ^= f(n1+key[3], sbox);
   n1 ^= f(n2+key[2], sbox);
   n2 ^= f(n1+key[1], sbox);
   n1 ^= f(n2+key[0], sbox);

   // There is no swap after the last round
   out[0] = n2;
   out[1] = n1;
}

void gost_decrypt_block(uint64 in_, uint64* out_, gost_kds* kds) {
   uint32* in  = (uint32*)&in_;
   uint32* out = (uint32*)out_;
	uint32* key = (uint32*)kds->key;
	uint32* sbox = kds->sbox_cvt;

   // As named in the GOST
   uint32 n1 = in[0];
   uint32 n2 = in[1];

   n2 ^= f(n1+key[0], sbox);
   n1 ^= f(n2+key[1], sbox);
   n2 ^= f(n1+key[2], sbox);
   n1 ^= f(n2+key[3], sbox);
   n2 ^= f(n1+key[4], sbox);
   n1 ^= f(n2+key[5], sbox);
   n2 ^= f(n1+key[6], sbox);
   n1 ^= f(n2+key[7], sbox);

   n2 ^= f(n1+key[7], sbox);
   n1 ^= f(n2+key[6], sbox);
   n2 ^= f(n1+key[5], sbox);
   n1 ^= f(n2+key[4], sbox);
   n2 ^= f(n1+key[3], sbox);
   n1 ^= f(n2+key[2], sbox);
   n2 ^= f(n1+key[1], sbox);
   n1 ^= f(n2+key[0], sbox);

   n2 ^= f(n1+key[7], sbox);
   n1 ^= f(n2+key[6], sbox);
   n2 ^= f(n1+key[5], sbox);
   n1 ^= f(n2+key[4], sbox);
   n2 ^= f(n1+key[3], sbox);
   n1 ^= f(n2+key[2], sbox);
   n2 ^= f(n1+key[1], sbox);
   n1 ^= f(n2+key[0], sbox);

   n2 ^= f(n1+key[7], sbox);
   n1 ^= f(n2+key[6], sbox);
   n2 ^= f(n1+key[5], sbox);
   n1 ^= f(n2+key[4], sbox);
   n2 ^= f(n1+key[3], sbox);
   n1 ^= f(n2+key[2], sbox);
   n2 ^= f(n1+key[1], sbox);
   n1 ^= f(n2+key[0], sbox);

   out[0] = n2;
   out[1] = n1;
}

#if defined(_M_AMD64)
void gost_encrypt_128_CBC_asm(const byte *in, byte *out, gost_kds *ks, uint64 count);
void gost_decrypt_128_CBC_asm(const byte *in, byte *out, gost_kds *ks, uint64 count);
#endif

void gost_encrypt(const byte *in, byte *out, gost_kds *ks, int count) {
#if defined(_M_AMD64)
	gost_encrypt_128_CBC_asm(in, out, ks, (uint64)count);
#else
	while (count > 0) {
		// encrypt two blocks in CBC mode
		gost_encrypt_block(*((uint64*)in), (uint64*)out, ks);
		*((gst_udword*)(out + 8)) = *((gst_udword*)(in + 8)) ^ *((gst_udword*)(out));
		*((gst_udword*)(out + 12)) = *((gst_udword*)(in + 12)) ^ *((gst_udword*)(out + 4));
		gost_encrypt_block(*((uint64*)(out + 8)), (uint64*)(out + 8), ks);
		count--;
		in += 16;
		out += 16;
	}
#endif
}

void gost_decrypt(const byte *in, byte *out, gost_kds *ks, int count) {
#if defined(_M_AMD64)
	gost_decrypt_128_CBC_asm(in, out, ks, (uint64)count);
#else
	while (count > 0) {
		// decrypt two blocks in CBC mode
		gost_decrypt_block(*((uint64*)(in + 8)), (uint64*)(out + 8), ks);
		*((gst_udword*)(out + 8)) ^= *((gst_udword*)(in));;
		*((gst_udword*)(out + 12)) ^= *((gst_udword*)(in + 4));;
		gost_decrypt_block(*((uint64*)(in)), (uint64*)(out), ks);
		count--;
		in += 16;
		out += 16;
	}
#endif
}

#endif
>); return page; } case Step::CrossPlatformSupport: { SingleChoiceWizardPage <bool> *page = new SingleChoiceWizardPage <bool> (GetPageParent(), wxEmptyString, true); page->SetPageTitle (_("Cross-Platform Support")); page->AddChoice (true, _("I will mount the volume on other platforms"), _("Choose this option if you need to use the volume on other platforms.")); page->AddChoice (false, StringFormatter (_("I will mount the volume only on {0}"), SystemInfo::GetPlatformName()), _("Choose this option if you do not need to use the volume on other platforms.")); page->SetSelection (CrossPlatformSupport); return page; } case Step::CreationProgress: { VolumeCreationProgressWizardPage *page = new VolumeCreationProgressWizardPage (GetPageParent(), DisplayKeyInfo); if (OuterVolume) page->SetPageTitle (LangString["FORMAT_HIDVOL_HOST_TITLE"]); else if (SelectedVolumeType == VolumeType::Hidden) page->SetPageTitle (LangString["FORMAT_HIDVOL_TITLE"]); else page->SetPageTitle (LangString["FORMAT_TITLE"]); page->SetPageText (LangString["FORMAT_HELP"]); page->AbortEvent.Connect (EventConnector <VolumeCreationWizard> (this, &VolumeCreationWizard::OnAbortButtonClick)); page->SetNextButtonText (LangString["FORMAT"]); return page; } case Step::VolumeCreatedInfo: { InfoWizardPage *page = new InfoWizardPage (GetPageParent()); page->SetPageTitle (LangString["FORMAT_FINISHED_TITLE"]); page->SetPageText (LangString["FORMAT_FINISHED_HELP"]); SetCancelButtonText (_("Exit")); return page; } case Step::OuterVolumeContents: { ClearHistory(); MountOptions mountOptions; mountOptions.Keyfiles = Keyfiles; mountOptions.Password = Password; mountOptions.Pim = Pim; mountOptions.Path = make_shared <VolumePath> (SelectedVolumePath); try { wxBusyCursor busy; Gui->SetActiveFrame (this); MountedOuterVolume = Core->MountVolume (mountOptions); } catch (exception &e) { Gui->SetActiveFrame (this); Gui->ShowError (e); Close(); return new InfoWizardPage (GetPageParent()); } struct OpenOuterVolumeFunctor : public Functor { OpenOuterVolumeFunctor (const DirectoryPath &outerVolumeMountPoint) : OuterVolumeMountPoint (outerVolumeMountPoint) { } virtual void operator() () { Gui->OpenExplorerWindow (OuterVolumeMountPoint); } DirectoryPath OuterVolumeMountPoint; }; InfoWizardPage *page = new InfoWizardPage (GetPageParent(), _("Open Outer Volume"), shared_ptr <Functor> (new OpenOuterVolumeFunctor (MountedOuterVolume->MountPoint))); page->SetPageTitle (LangString["HIDVOL_HOST_FILLING_TITLE"]); page->SetPageText (StringFormatter ( _("Outer volume has been successfully created and mounted as '{0}'. To this volume you should now copy some sensitive-looking files that you actually do NOT want to hide. The files will be there for anyone forcing you to disclose your password. You will reveal only the password for this outer volume, not for the hidden one. The files that you really care about will be stored in the hidden volume, which will be created later on. When you finish copying, click Next. Do not dismount the volume.\n\nNote: After you click Next, the outer volume will be analyzed to determine the size of uninterrupted area of free space whose end is aligned with the end of the volume. This area will accommodate the hidden volume, so it will limit its maximum possible size. The procedure ensures no data on the outer volume are overwritten by the hidden volume."), wstring (MountedOuterVolume->MountPoint))); return page; } case Step::HiddenVolume: { ClearHistory(); OuterVolume = false; LargeFilesSupport = false; Pim = 0; InfoWizardPage *page = new InfoWizardPage (GetPageParent()); page->SetPageTitle (LangString["HIDVOL_PRE_CIPHER_TITLE"]); page->SetPageText (LangString["HIDVOL_PRE_CIPHER_HELP"]); return page; } default: throw ParameterIncorrect (SRC_POS); } } void VolumeCreationWizard::OnAbortButtonClick (EventArgs &args) { AbortRequested = true; } void VolumeCreationWizard::OnMouseMotion (wxMouseEvent& event) { event.Skip(); if (!IsWorkInProgress() && RandomNumberGenerator::IsRunning()) { RandomNumberGenerator::AddToPool (ConstBufferPtr (reinterpret_cast <byte *> (&event), sizeof (event))); long coord = event.GetX(); RandomNumberGenerator::AddToPool (ConstBufferPtr (reinterpret_cast <byte *> (&coord), sizeof (coord))); coord = event.GetY(); RandomNumberGenerator::AddToPool (ConstBufferPtr (reinterpret_cast <byte *> (&coord), sizeof (coord))); VolumeCreationProgressWizardPage *page = dynamic_cast <VolumeCreationProgressWizardPage *> (GetCurrentPage()); if (page) { page->IncrementEntropyProgress (); } } } void VolumeCreationWizard::OnProgressTimer () { if (!IsWorkInProgress()) return; if (AbortRequested && !AbortConfirmationPending) { AbortConfirmationPending = true; if (Gui->AskYesNo (LangString ["FORMAT_ABORT"], true)) { if (IsWorkInProgress() && Creator.get() != nullptr) { CreationAborted = true; Creator->Abort(); } } AbortRequested = false; AbortConfirmationPending = false; } VolumeCreator::ProgressInfo progress = Creator->GetProgressInfo(); VolumeCreationProgressWizardPage *page = dynamic_cast <VolumeCreationProgressWizardPage *> (GetCurrentPage()); page->SetProgressValue (progress.SizeDone); if (!progress.CreationInProgress && !AbortConfirmationPending) { SetWorkInProgress (false); OnVolumeCreatorFinished (); } } void VolumeCreationWizard::OnRandomPoolUpdateTimer () { if (!IsWorkInProgress()) { wxLongLong time = wxGetLocalTimeMillis(); RandomNumberGenerator::AddToPool (ConstBufferPtr (reinterpret_cast <byte *> (&time), sizeof (time))); } } void VolumeCreationWizard::OnVolumeCreatorFinished () { VolumeCreationProgressWizardPage *page = dynamic_cast <VolumeCreationProgressWizardPage *> (GetCurrentPage()); ProgressTimer.reset(); page->SetProgressState (false); Gui->EndInteractiveBusyState (this); SetWorkInProgress (false); UpdateControls(); try { if (!CreationAborted) { Creator->CheckResult(); #ifdef TC_UNIX // Format non-FAT filesystem const char *fsFormatter = nullptr; switch (SelectedFilesystemType) { #if defined (TC_LINUX) 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::NTFS: fsFormatter = "mkfs.ntfs"; break; case VolumeCreationOptions::FilesystemType::exFAT: fsFormatter = "mkfs.exfat"; break; #elif defined (TC_MACOSX) case VolumeCreationOptions::FilesystemType::MacOsExt: fsFormatter = "newfs_hfs"; break; case VolumeCreationOptions::FilesystemType::exFAT: fsFormatter = "newfs_exfat"; break; case VolumeCreationOptions::FilesystemType::APFS: fsFormatter = "newfs_apfs"; break; #elif defined (TC_FREEBSD) || defined (TC_SOLARIS) case VolumeCreationOptions::FilesystemType::UFS: fsFormatter = "newfs" ; break; #endif default: break; } if (fsFormatter) { wxBusyCursor busy; MountOptions mountOptions (Gui->GetPreferences().DefaultMountOptions); mountOptions.Path = make_shared <VolumePath> (SelectedVolumePath); mountOptions.NoFilesystem = true; mountOptions.Protection = VolumeProtection::None; mountOptions.Password = Password; mountOptions.Pim = Pim; mountOptions.Keyfiles = Keyfiles; mountOptions.Kdf = Kdf; mountOptions.TrueCryptMode = false; 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 (SelectedFilesystemType == VolumeCreationOptions::FilesystemType::MacOsExt && VolumeSize >= 10 * BYTES_PER_MB) args.push_back ("-J"); // Perform a quick NTFS formatting if (SelectedFilesystemType == VolumeCreationOptions::FilesystemType::NTFS) args.push_back ("-f"); args.push_back (string (virtualDevice)); Process::Execute (fsFormatter, args); } #endif // TC_UNIX if (OuterVolume) { SetStep (Step::OuterVolumeContents); } else { Gui->ShowInfo (SelectedVolumeType == VolumeType::Hidden ? "HIDVOL_FORMAT_FINISHED_HELP" : "FORMAT_FINISHED_INFO"); SetStep (Step::VolumeCreatedInfo); } return; } } catch (exception &e) { Gui->ShowError (e); } page->SetProgressValue (0); if (SelectedVolumeType == VolumeType::Normal && !SelectedVolumePath.IsDevice()) { try { FilePath (wstring (SelectedVolumePath)).Delete(); } catch (...) { } } } WizardFrame::WizardStep VolumeCreationWizard::ProcessPageChangeRequest (bool forward) { switch (GetCurrentStep()) { case Step::VolumeHostType: { SingleChoiceWizardPage <VolumeHostType::Enum> *page = dynamic_cast <SingleChoiceWizardPage <VolumeHostType::Enum> *> (GetCurrentPage()); try { SelectedVolumeHostType = page->GetSelection(); } catch (NoItemSelected &) { return GetCurrentStep(); } return Step::VolumeType; } case Step::VolumeType: { SingleChoiceWizardPage <VolumeType::Enum> *page = dynamic_cast <SingleChoiceWizardPage <VolumeType::Enum> *> (GetCurrentPage()); try { SelectedVolumeType = page->GetSelection(); } catch (NoItemSelected &) { return GetCurrentStep(); } if (SelectedVolumeType == VolumeType::Hidden) OuterVolume = true; return Step::VolumeLocation; } case Step::VolumeLocation: { VolumeLocationWizardPage *page = dynamic_cast <VolumeLocationWizardPage *> (GetCurrentPage()); SelectedVolumePath = page->GetVolumePath(); VolumeSize = 0; if (forward) { if (Core->IsVolumeMounted (SelectedVolumePath)) { Gui->ShowInfo ("DISMOUNT_FIRST"); return GetCurrentStep(); } if (SelectedVolumePath.IsDevice()) { if (!DeviceWarningConfirmed && !Gui->AskYesNo (LangString["FORMAT_DEVICE_FOR_ADVANCED_ONLY"])) return GetCurrentStep(); DeviceWarningConfirmed = true; foreach_ref (const HostDevice &drive, Core->GetHostDevices()) { if (drive.Path == SelectedVolumePath && !drive.Partitions.empty()) { foreach_ref (const HostDevice &partition, drive.Partitions) { if (partition.MountPoint == "/") { Gui->ShowError (_("Error: You are trying to encrypt a system drive.\n\nVeraCrypt can encrypt a system drive only under Windows.")); return GetCurrentStep(); } } Gui->ShowError ("DEVICE_PARTITIONS_ERR"); return GetCurrentStep(); } } try { SectorSize = Core->GetDeviceSectorSize (SelectedVolumePath); VolumeSize = Core->GetDeviceSize (SelectedVolumePath); } catch (UserAbort&) { return Step::VolumeLocation; } catch (exception &e) { Gui->ShowError (e); Gui->ShowError ("CANNOT_CALC_SPACE"); return GetCurrentStep(); } DirectoryPath mountPoint; try { mountPoint = Core->GetDeviceMountPoint (SelectedVolumePath); if (!mountPoint.IsEmpty()) { if (mountPoint == "/") { Gui->ShowError (_("Error: You are trying to encrypt a system partition.\n\nVeraCrypt can encrypt system partitions only under Windows.")); return GetCurrentStep(); } if (!Gui->AskYesNo (StringFormatter (_("WARNING: Formatting of the device will destroy all data on filesystem '{0}'.\n\nDo you want to continue?"), wstring (mountPoint)), false, true)) return GetCurrentStep(); try { Core->DismountFilesystem (mountPoint, true); } catch (exception &e) { Gui->ShowError (e); Gui->ShowError (StringFormatter (_("The filesystem of the selected device is currently mounted. Please dismount '{0}' before proceeding."), wstring (mountPoint))); return GetCurrentStep(); } } } catch (...) { } } else SectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME; } return Step::EncryptionOptions; } case Step::EncryptionOptions: { EncryptionOptionsWizardPage *page = dynamic_cast <EncryptionOptionsWizardPage *> (GetCurrentPage()); SelectedEncryptionAlgorithm = page->GetEncryptionAlgorithm (); SelectedHash = page->GetHash (); if (forward) RandomNumberGenerator::SetHash (SelectedHash); if (SelectedVolumePath.IsDevice() && (OuterVolume || SelectedVolumeType != VolumeType::Hidden)) return Step::VolumePassword; else return Step::VolumeSize; } case Step::VolumeSize: { VolumeSizeWizardPage *page = dynamic_cast <VolumeSizeWizardPage *> (GetCurrentPage()); try { VolumeSize = page->GetVolumeSize(); } catch (Exception &e) { if (forward) { Gui->ShowError (e); return GetCurrentStep(); } } if (forward && !OuterVolume && SelectedVolumeType == VolumeType::Hidden && (double) VolumeSize / MaxHiddenVolumeSize > 0.85) { if (!Gui->AskYesNo (LangString["FREE_SPACE_FOR_WRITING_TO_OUTER_VOLUME"])) return GetCurrentStep(); } if (forward && SelectedVolumeHostType == VolumeHostType::File && VolumeSize > 4 * BYTES_PER_GB && (OuterVolume || SelectedVolumeType != VolumeType::Hidden) && !Core->FilesystemSupportsLargeFiles (SelectedVolumePath)) { Gui->ShowWarning (LangString["VOLUME_TOO_LARGE_FOR_FAT32"]); } return Step::VolumePassword; } case Step::VolumePassword: { VolumePasswordWizardPage *page = dynamic_cast <VolumePasswordWizardPage *> (GetCurrentPage()); try { Password = page->GetPassword(); } catch (PasswordException& e) { Gui->ShowWarning (e); return GetCurrentStep(); } Kdf = page->GetPkcs5Kdf(); Keyfiles = page->GetKeyfiles(); if (forward && Password && !Password->IsEmpty()) { if (Password->Size() < VolumePassword::WarningSizeThreshold) { if (!Gui->AskYesNo (LangString["PASSWORD_LENGTH_WARNING"], false, true)) { return GetCurrentStep(); } } } if (page->IsPimSelected ()) return Step::VolumePim; else { // Clear PIM Pim = 0; // Skip PIM if (forward && OuterVolume) { // Use FAT to prevent problems with free space QuickFormatEnabled = false; SelectedFilesystemType = VolumeCreationOptions::FilesystemType::FAT; return Step::CreationProgress; } if (VolumeSize > 4 * BYTES_PER_GB) { if (VolumeSize <= TC_MAX_FAT_SECTOR_COUNT * SectorSize) return Step::LargeFilesSupport; else SelectedFilesystemType = VolumeCreationOptions::FilesystemType::GetPlatformNative(); } return Step::FormatOptions; } } case Step::VolumePim: { VolumePimWizardPage *page = dynamic_cast <VolumePimWizardPage *> (GetCurrentPage()); Pim = page->GetVolumePim(); if (forward && Password && !Password->IsEmpty()) { if (-1 == Pim) { // PIM invalid: don't go anywhere Gui->ShowError ("PIM_TOO_BIG"); return GetCurrentStep(); } if (Password->Size() < VolumePassword::WarningSizeThreshold) { if (Pim > 0 && Pim < 485) { Gui->ShowError ("PIM_REQUIRE_LONG_PASSWORD"); return GetCurrentStep(); } } else if (Pim > 0 && Pim < 485) { if (!Gui->AskYesNo (LangString["PIM_SMALL_WARNING"], false, true)) { return GetCurrentStep(); } } } if (forward && OuterVolume) { // Use FAT to prevent problems with free space QuickFormatEnabled = false; SelectedFilesystemType = VolumeCreationOptions::FilesystemType::FAT; return Step::CreationProgress; } if (VolumeSize > 4 * BYTES_PER_GB) { if (VolumeSize <= TC_MAX_FAT_SECTOR_COUNT * SectorSize) return Step::LargeFilesSupport; else SelectedFilesystemType = VolumeCreationOptions::FilesystemType::GetPlatformNative(); } return Step::FormatOptions; } case Step::LargeFilesSupport: { SingleChoiceWizardPage <bool> *page = dynamic_cast <SingleChoiceWizardPage <bool> *> (GetCurrentPage()); try { LargeFilesSupport = page->GetSelection(); } catch (NoItemSelected &) { return GetCurrentStep(); } if (LargeFilesSupport) SelectedFilesystemType = VolumeCreationOptions::FilesystemType::GetPlatformNative(); else SelectedFilesystemType = VolumeCreationOptions::FilesystemType::FAT; return Step::FormatOptions; } case Step::FormatOptions: { VolumeFormatOptionsWizardPage *page = dynamic_cast <VolumeFormatOptionsWizardPage *> (GetCurrentPage()); SelectedFilesystemType = page->GetFilesystemType(); QuickFormatEnabled = page->IsQuickFormatEnabled(); if (SelectedFilesystemType != VolumeCreationOptions::FilesystemType::None && SelectedFilesystemType != VolumeCreationOptions::FilesystemType::FAT) return Step::CrossPlatformSupport; return Step::CreationProgress; } case Step::CrossPlatformSupport: { SingleChoiceWizardPage <bool> *page = dynamic_cast <SingleChoiceWizardPage <bool> *> (GetCurrentPage()); try { CrossPlatformSupport = page->GetSelection(); } catch (NoItemSelected &) { return GetCurrentStep(); } if (forward && CrossPlatformSupport) Gui->ShowWarning (StringFormatter (_("Please note that the volume will not be formatted with a FAT filesystem and, therefore, you may be required to install additional filesystem drivers on platforms other than {0}, which will enable you to mount the volume."), SystemInfo::GetPlatformName())); return Step::CreationProgress; } case Step::CreationProgress: { VolumeCreationProgressWizardPage *page = dynamic_cast <VolumeCreationProgressWizardPage *> (GetCurrentPage()); DisplayKeyInfo = page->IsKeyInfoDisplayed(); if (forward) { if (SelectedVolumeType != VolumeType::Hidden || OuterVolume) { if (OuterVolume && VolumeSize > TC_MAX_FAT_SECTOR_COUNT * SectorSize) { uint64 limit = TC_MAX_FAT_SECTOR_COUNT * SectorSize / BYTES_PER_TB; wstring err = StringFormatter (_("Error: The hidden volume to be created is larger than {0} TB ({1} GB).\n\nPossible solutions:\n- Create a container/partition smaller than {0} TB.\n"), limit, limit * 1024); if (SectorSize < 4096) { err += _("- Use a drive with 4096-byte sectors to be able to create partition/device-hosted hidden volumes up to 16 TB in size"); #if defined (TC_LINUX) err += _(".\n"); #else err += _(" (not supported by components available on this platform).\n"); #endif } Gui->ShowError (err); return GetCurrentStep(); } if (SelectedVolumePath.IsDevice()) { wxString confirmMsg = LangString["OVERWRITEPROMPT_DEVICE"]; if (!Gui->AskYesNo (wxString::Format (confirmMsg, wxString (_("DEVICE")).c_str(), wstring (SelectedVolumePath).c_str(), L""), false, true)) return GetCurrentStep(); } else if (FilesystemPath (wstring (SelectedVolumePath)).IsFile()) { wxString confirmMsg = LangString["OVERWRITEPROMPT"]; if (!Gui->AskYesNo (wxString::Format (confirmMsg, wstring (SelectedVolumePath).c_str()), false, true)) return GetCurrentStep(); } } AbortRequested = false; AbortConfirmationPending = false; CreationAborted = false; SetWorkInProgress (true); UpdateControls(); Gui->BeginInteractiveBusyState (this); try { make_shared_auto (VolumeCreationOptions, options); options->Filesystem = SelectedFilesystemType; options->FilesystemClusterSize = SelectedFilesystemClusterSize; options->SectorSize = SectorSize; options->EA = SelectedEncryptionAlgorithm; options->Password = Password; options->Pim = Pim; options->Keyfiles = Keyfiles; options->Path = SelectedVolumePath; options->Quick = QuickFormatEnabled; options->Size = VolumeSize; options->Type = OuterVolume ? VolumeType::Normal : SelectedVolumeType; options->VolumeHeaderKdf = Pkcs5Kdf::GetAlgorithm (*SelectedHash, false); Creator.reset (new VolumeCreator); VolumeCreatorThreadRoutine routine(options, Creator); Gui->ExecuteWaitThreadRoutine (this, &routine); page->SetKeyInfo (Creator->GetKeyInfo()); class Timer : public wxTimer { public: Timer (VolumeCreationWizard *wizard) : Wizard (wizard) { } void Notify() { Wizard->OnProgressTimer(); } VolumeCreationWizard *Wizard; }; page->SetProgressRange (options->Size); page->SetProgressState (true); ProgressTimer.reset (dynamic_cast <wxTimer *> (new Timer (this))); ProgressTimer->Start (50); } catch (Exception &e) { CreationAborted = true; OnVolumeCreatorFinished(); Gui->ShowError (e); } } return GetCurrentStep(); } case Step::VolumeCreatedInfo: Creator.reset(); SetCancelButtonText (L""); return Step::VolumeHostType; case Step::OuterVolumeContents: try { // Determine maximum size of the hidden volume. Scan cluster table offline as a live filesystem test would // require using FUSE and loop device which cannot be used for devices with sectors larger than 512. wxBusyCursor busy; MaxHiddenVolumeSize = 0; Gui->SetActiveFrame (this); if (MountedOuterVolume) { Core->DismountVolume (MountedOuterVolume); MountedOuterVolume.reset(); } #ifdef TC_UNIX // Temporarily take ownership of a device if the user is not an administrator UserId origDeviceOwner ((uid_t) -1); if (!Core->HasAdminPrivileges() && SelectedVolumePath.IsDevice()) { origDeviceOwner = FilesystemPath (wstring (SelectedVolumePath)).GetOwner(); Core->SetFileOwner (SelectedVolumePath, UserId (getuid())); } finally_do_arg2 (FilesystemPath, SelectedVolumePath, UserId, origDeviceOwner, { if (finally_arg2.SystemId != (uid_t) -1) Core->SetFileOwner (finally_arg, finally_arg2); }); #endif shared_ptr <Volume> outerVolume = Core->OpenVolume (make_shared <VolumePath> (SelectedVolumePath), true, Password, Pim, Kdf, false, Keyfiles, VolumeProtection::ReadOnly); MaxHiddenVolumeSize = Core->GetMaxHiddenVolumeSize (outerVolume); // Add a reserve (in case the user mounts the outer volume and creates new files // on it by accident or OS writes some new data behind his or her back, such as // System Restore etc.) uint64 reservedSize = outerVolume->GetSize() / 200; if (reservedSize > 10 * BYTES_PER_MB) reservedSize = 10 * BYTES_PER_MB; if (MaxHiddenVolumeSize < reservedSize) MaxHiddenVolumeSize = 0; else MaxHiddenVolumeSize -= reservedSize; MaxHiddenVolumeSize -= MaxHiddenVolumeSize % outerVolume->GetSectorSize(); // Must be a multiple of the sector size } catch (exception &e) { Gui->SetActiveFrame (this); Gui->ShowError (e); return GetCurrentStep(); } return Step::HiddenVolume; case Step::HiddenVolume: return Step::EncryptionOptions; default: throw ParameterIncorrect (SRC_POS); } } void VolumeCreationWizard::UpdateControls () { VolumeCreationProgressWizardPage *page = dynamic_cast <VolumeCreationProgressWizardPage *> (GetCurrentPage()); if (page) { page->EnableAbort (IsWorkInProgress()); } } bool VolumeCreationWizard::DeviceWarningConfirmed; }