diff options
Diffstat (limited to 'src/Common')
36 files changed, 12451 insertions, 1008 deletions
diff --git a/src/Common/Apidrvr.h b/src/Common/Apidrvr.h index 9c16b378..36233d76 100644 --- a/src/Common/Apidrvr.h +++ b/src/Common/Apidrvr.h @@ -26,23 +26,77 @@ #define TC_IOCTL(CODE) (CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800 + (CODE), METHOD_BUFFERED, FILE_ANY_ACCESS)) +// IOCTL interface to \\device\veracrypt + +// Gets version of driver +// OUT struct - LONG #define TC_IOCTL_GET_DRIVER_VERSION TC_IOCTL (1) + +// Gets boot loader version +// OUT struct - int16 #define TC_IOCTL_GET_BOOT_LOADER_VERSION TC_IOCTL (2) + +// Mount volume to \\Device\VeraCryptVolume"X" +// IN OUT - MOUNT_STRUCT #define TC_IOCTL_MOUNT_VOLUME TC_IOCTL (3) + +// Dismount volume +// IN OUT - UNMOUNT_STRUCT #define TC_IOCTL_DISMOUNT_VOLUME TC_IOCTL (4) + +// Dismount all volumes +// IN OUT - UNMOUNT_STRUCT #define TC_IOCTL_DISMOUNT_ALL_VOLUMES TC_IOCTL (5) + +// Get list of all mounted volumes +// IN OUT - MOUNT_LIST_STRUCT (only 26 volumes possible) #define TC_IOCTL_GET_MOUNTED_VOLUMES TC_IOCTL (6) + +// Get properties of the volume selected by driveNo +// In OUT - VOLUME_PROPERTIES_STRUCT #define TC_IOCTL_GET_VOLUME_PROPERTIES TC_IOCTL (7) + +// Get reference count to main device object +// OUT - int #define TC_IOCTL_GET_DEVICE_REFCOUNT TC_IOCTL (8) + +// Is it possible to unload driver +// It check file system cache of mounted drives via unmount IOCTL. +// OUT - int #define TC_IOCTL_IS_DRIVER_UNLOAD_DISABLED TC_IOCTL (9) + +// Is there any mounted device +// OUT - int #define TC_IOCTL_IS_ANY_VOLUME_MOUNTED TC_IOCTL (10) + +// Check password cache +// Result in IOCTL result TRUE if there is chached passwords #define TC_IOCTL_GET_PASSWORD_CACHE_STATUS TC_IOCTL (11) + +// Clean password cache #define TC_IOCTL_WIPE_PASSWORD_CACHE TC_IOCTL (12) + +// Check file/drive container +// IN OUT - OPEN_TEST_STRUCT #define TC_IOCTL_OPEN_TEST TC_IOCTL (13) + +// result of IOCTL_DISK_GET_PARTITION_INFO +// IN OUT - DISK_PARTITION_INFO_STRUCT +// TODO: need IOCTL_DISK_GET_PARTITION_INFO_EX to support GPT #define TC_IOCTL_GET_DRIVE_PARTITION_INFO TC_IOCTL (14) + +// result IOCTL_DISK_GET_DRIVE_GEOMETRY +// IN OUT - DISK_GEOMETRY_STRUCT #define TC_IOCTL_GET_DRIVE_GEOMETRY TC_IOCTL (15) + +// result IOCTL_DISK_GET_LENGTH_INFO +// IN OUT - ProbeRealDriveSizeRequest #define TC_IOCTL_PROBE_REAL_DRIVE_SIZE TC_IOCTL (16) + +// result of ZwQuerySymbolicLinkObject +// IN OUT RESOLVE_SYMLINK_STRUCT #define TC_IOCTL_GET_RESOLVED_SYMLINK TC_IOCTL (17) + #define TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS TC_IOCTL (18) #define TC_IOCTL_BOOT_ENCRYPTION_SETUP TC_IOCTL (19) #define TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP TC_IOCTL (20) @@ -287,7 +341,7 @@ typedef struct typedef struct { WipeAlgorithmId WipeAlgorithm; - byte WipeKey[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) byte WipeKey[MASTER_KEYDATA_SIZE]; } WipeDecoySystemRequest; typedef struct diff --git a/src/Common/BaseCom.cpp b/src/Common/BaseCom.cpp index 5905b6a8..26e2650a 100644 --- a/src/Common/BaseCom.cpp +++ b/src/Common/BaseCom.cpp @@ -3,7 +3,7 @@ Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0. - Modifications and additions to the original source code (contained in this file) + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -161,6 +161,63 @@ DWORD BaseCom::ReadWriteFile (BOOL write, BOOL device, BSTR filePath, BSTR *buff return ERROR_SUCCESS; } +DWORD BaseCom::GetFileSize (BSTR filePath, unsigned __int64 *pSize) +{ + if (!pSize) + return ERROR_INVALID_PARAMETER; + + try + { + std::wstring path (filePath); + File file(filePath, true); + file.CheckOpened (SRC_POS); + file.GetFileSize (*pSize); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::DeviceIoControl (BOOL readOnly, BOOL device, BSTR filePath, DWORD dwIoControlCode, BSTR input, BSTR *output) +{ + try + { + auto_ptr <File> file (device ? new Device (filePath, readOnly == TRUE) : new File (filePath, readOnly == TRUE)); + file->CheckOpened (SRC_POS); + if (!file->IoCtl (dwIoControlCode, (BYTE *) input, !(BYTE *) input ? 0 : ((DWORD *) ((BYTE *) input))[-1], + (BYTE *) *output, !(BYTE *) *output ? 0 : ((DWORD *) ((BYTE *) *output))[-1])) + { + return GetLastError(); + } + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} DWORD BaseCom::RegisterFilterDriver (BOOL registerDriver, int filterType) { @@ -244,3 +301,161 @@ DWORD BaseCom::WriteLocalMachineRegistryDwordValue (BSTR keyPath, BSTR valueName return ERROR_SUCCESS; } +DWORD BaseCom::InstallEfiBootLoader (BOOL preserveUserConfig, BOOL hiddenOSCreation, int pim, int hashAlg) +{ + try + { + BootEncryption bootEnc (NULL); + bootEnc.InstallBootLoader (preserveUserConfig? true : false, hiddenOSCreation? true : false, pim, hashAlg); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::BackupEfiSystemLoader () +{ + try + { + BootEncryption bootEnc (NULL); + bootEnc.BackupSystemLoader (); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::RestoreEfiSystemLoader () +{ + try + { + BootEncryption bootEnc (NULL); + bootEnc.RestoreSystemLoader (); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::GetEfiBootDeviceNumber (BSTR* pSdn) +{ + if (!pSdn || !(*pSdn) || ((((DWORD *) ((BYTE *) *pSdn))[-1]) < sizeof (STORAGE_DEVICE_NUMBER))) + return ERROR_INVALID_PARAMETER; + + try + { + BootEncryption bootEnc (NULL); + bootEnc.GetEfiBootDeviceNumber ((PSTORAGE_DEVICE_NUMBER) *pSdn); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::ReadEfiConfig (BSTR* pContent, DWORD *pcbRead) +{ + if (!pContent || !(*pContent)) + return ERROR_INVALID_PARAMETER; + + try + { + DWORD maxSize = ((DWORD *) ((BYTE *) *pContent))[-1]; + BootEncryption bootEnc (NULL); + bootEnc.ReadEfiConfig ((byte*) *pContent, maxSize, pcbRead); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +} + +DWORD BaseCom::WriteEfiBootSectorUserConfig (DWORD userConfig, BSTR customUserMessage, int pim, int hashAlg) +{ + if (!customUserMessage) + return ERROR_INVALID_PARAMETER; + + try + { + DWORD maxSize = ((DWORD *) ((BYTE *) customUserMessage))[-1]; + char* msg = (char*) *customUserMessage; + if (maxSize > 0) + msg [maxSize - 1] = 0; + std::string msgStr = maxSize > 0 ? msg : ""; + BootEncryption bootEnc (NULL); + bootEnc.WriteEfiBootSectorUserConfig ((byte) userConfig, msgStr, pim, hashAlg); + } + catch (SystemException &) + { + return GetLastError(); + } + catch (Exception &e) + { + e.Show (NULL); + return ERROR_EXCEPTION_IN_SERVICE; + } + catch (...) + { + return ERROR_EXCEPTION_IN_SERVICE; + } + + return ERROR_SUCCESS; +}
\ No newline at end of file diff --git a/src/Common/BaseCom.h b/src/Common/BaseCom.h index a5b27473..b103ad59 100644 --- a/src/Common/BaseCom.h +++ b/src/Common/BaseCom.h @@ -3,7 +3,7 @@ Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0. - Modifications and additions to the original source code (contained in this file) + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -20,11 +20,11 @@ class TrueCryptFactory : public IClassFactory { public: - TrueCryptFactory (DWORD messageThreadId) : + TrueCryptFactory (DWORD messageThreadId) : RefCount (1), ServerLockCount (0), MessageThreadId (messageThreadId) { } ~TrueCryptFactory () { } - + virtual ULONG STDMETHODCALLTYPE AddRef () { return InterlockedIncrement (&RefCount) - 1; @@ -53,7 +53,7 @@ public: AddRef (); return S_OK; } - + virtual HRESULT STDMETHODCALLTYPE CreateInstance (IUnknown *pUnkOuter, REFIID riid, void **ppvObject) { if (pUnkOuter != NULL) @@ -110,6 +110,14 @@ public: static DWORD RegisterSystemFavoritesService (BOOL registerService); static DWORD SetDriverServiceStartType (DWORD startType); static DWORD WriteLocalMachineRegistryDwordValue (BSTR keyPath, BSTR valueName, DWORD value); + static DWORD GetFileSize (BSTR filePath, unsigned __int64 *pSize); + static DWORD DeviceIoControl (BOOL readOnly, BOOL device, BSTR filePath, DWORD dwIoControlCode, BSTR input, BSTR *output); + static DWORD InstallEfiBootLoader (BOOL preserveUserConfig, BOOL hiddenOSCreation, int pim, int hashAlg); + static DWORD BackupEfiSystemLoader (); + static DWORD RestoreEfiSystemLoader (); + static DWORD GetEfiBootDeviceNumber (BSTR* pSdn); + static DWORD ReadEfiConfig (BSTR* pContent, DWORD *pcbRead); + static DWORD WriteEfiBootSectorUserConfig (DWORD userConfig, BSTR customUserMessage, int pim, int hashAlg); }; diff --git a/src/Common/BootEncryption.cpp b/src/Common/BootEncryption.cpp index 7148ca24..0146c7a5 100644 --- a/src/Common/BootEncryption.cpp +++ b/src/Common/BootEncryption.cpp @@ -28,6 +28,9 @@ #include "Random.h" #include "Registry.h" #include "Volumes.h" +#include "Xml.h" +#include "XZip.h" +#include "XUnzip.h" #ifdef VOLFORMAT #include "Format/FormatCom.h" @@ -44,7 +47,7 @@ namespace VeraCrypt class Elevator { public: - + static void AddReference () { ++ReferenceCount; @@ -160,6 +163,75 @@ namespace VeraCrypt memcpy (buffer, (BYTE *) bufferBstr.m_str, size); } + static void GetFileSize (const wstring &filePath, unsigned __int64* pSize) + { + Elevate(); + + DWORD result; + CComBSTR fileBstr; + BSTR bstr = W2BSTR(filePath.c_str()); + if (bstr) + { + fileBstr.Attach (bstr); + result = ElevatedComInstance->GetFileSize (fileBstr, pSize); + } + else + { + result = ERROR_OUTOFMEMORY; + } + + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static BOOL DeviceIoControl (BOOL readOnly, BOOL device, const wstring &filePath, DWORD dwIoControlCode, LPVOID input, DWORD inputSize, + LPVOID output, DWORD outputSize) + { + Elevate(); + + DWORD result; + + BSTR bstr = W2BSTR(filePath.c_str()); + if (bstr) + { + CComBSTR inputBstr; + if (input && inputBstr.AppendBytes ((const char *) input, inputSize) != S_OK) + { + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + + CComBSTR outputBstr; + if (output && outputBstr.AppendBytes ((const char *) output, outputSize) != S_OK) + { + SetLastError (ERROR_INVALID_PARAMETER); + return FALSE; + } + + CComBSTR fileBstr; + fileBstr.Attach (bstr); + result = ElevatedComInstance->DeviceIoControl (readOnly, device, fileBstr, dwIoControlCode, inputBstr, &outputBstr); + + if (output) + memcpy (output, *(void **) &outputBstr, outputSize); + } + else + { + result = ERROR_OUTOFMEMORY; + } + + if (result != ERROR_SUCCESS) + { + SetLastError (result); + return FALSE; + } + else + return TRUE; + } + static BOOL IsPagingFileActive (BOOL checkNonWindowsPartitionsOnly) { Elevate(); @@ -225,6 +297,112 @@ namespace VeraCrypt } } + static void InstallEfiBootLoader (bool preserveUserConfig, bool hiddenOSCreation, int pim, int hashAlg) + { + Elevate(); + + DWORD result = ElevatedComInstance->InstallEfiBootLoader (preserveUserConfig ? TRUE : FALSE, hiddenOSCreation ? TRUE : FALSE, pim, hashAlg); + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static void BackupEfiSystemLoader () + { + Elevate(); + + DWORD result = ElevatedComInstance->BackupEfiSystemLoader (); + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static void RestoreEfiSystemLoader () + { + Elevate(); + + DWORD result = ElevatedComInstance->RestoreEfiSystemLoader (); + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static void GetEfiBootDeviceNumber (PSTORAGE_DEVICE_NUMBER pSdn) + { + Elevate(); + + CComBSTR outputBstr; + if (pSdn && outputBstr.AppendBytes ((const char *) pSdn, sizeof (STORAGE_DEVICE_NUMBER)) != S_OK) + { + SetLastError (ERROR_INVALID_PARAMETER); + throw SystemException(SRC_POS); + } + + DWORD result = ElevatedComInstance->GetEfiBootDeviceNumber (&outputBstr); + + if (pSdn) + memcpy (pSdn, *(void **) &outputBstr, sizeof (STORAGE_DEVICE_NUMBER)); + + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static void ReadEfiConfig (byte* confContent, DWORD maxSize, DWORD* pcbRead) + { + Elevate(); + + CComBSTR outputBstr; + if (confContent && outputBstr.AppendBytes ((const char *) confContent, maxSize) != S_OK) + { + SetLastError (ERROR_INVALID_PARAMETER); + throw SystemException(SRC_POS); + } + + DWORD result = ElevatedComInstance->ReadEfiConfig (&outputBstr, pcbRead); + + if (confContent) + memcpy (confContent, *(void **) &outputBstr, maxSize); + + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + + static void WriteEfiBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg) + { + Elevate(); + + DWORD result; + CComBSTR customUserMessageBstr; + BSTR bstr = A2BSTR(customUserMessage.c_str()); + if (bstr) + { + customUserMessageBstr.Attach (bstr); + result = ElevatedComInstance->WriteEfiBootSectorUserConfig ((DWORD) userConfig, customUserMessageBstr, pim, hashAlg); + } + else + { + result = ERROR_OUTOFMEMORY; + } + + if (result != ERROR_SUCCESS) + { + SetLastError (result); + throw SystemException(SRC_POS); + } + } + static void Release () { if (--ReferenceCount == 0 && ElevatedComInstance) @@ -292,12 +470,19 @@ namespace VeraCrypt static void RegisterFilterDriver (bool registerDriver, BootEncryption::FilterType filterType) { throw ParameterIncorrect (SRC_POS); } static void Release () { } static void SetDriverServiceStartType (DWORD startType) { throw ParameterIncorrect (SRC_POS); } + static void GetFileSize (const wstring &filePath, unsigned __int64 *pSize) { throw ParameterIncorrect (SRC_POS); } + static BOOL DeviceIoControl (BOOL readOnly, BOOL device, const wstring &filePath, DWORD dwIoControlCode, LPVOID input, DWORD inputSize, LPVOID output, DWORD outputSize) { throw ParameterIncorrect (SRC_POS); } + static void InstallEfiBootLoader (bool preserveUserConfig, bool hiddenOSCreation, int pim, int hashAlg) { throw ParameterIncorrect (SRC_POS); } + static void BackupEfiSystemLoader () { throw ParameterIncorrect (SRC_POS); } + static void RestoreEfiSystemLoader () { throw ParameterIncorrect (SRC_POS); } + static void GetEfiBootDeviceNumber (PSTORAGE_DEVICE_NUMBER pSdn) { throw ParameterIncorrect (SRC_POS); } + static void ReadEfiConfig (byte* confContent, DWORD maxSize, DWORD* pcbRead) { throw ParameterIncorrect (SRC_POS); } + static void WriteEfiBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg) { throw ParameterIncorrect (SRC_POS); } }; #endif // SETUP - - File::File (wstring path, bool readOnly, bool create) : Elevated (false), FileOpen (false), LastError(0) + File::File (wstring path, bool readOnly, bool create) : Elevated (false), FileOpen (false), ReadOnly (readOnly), LastError(0) { Handle = CreateFile (path.c_str(), readOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, @@ -375,6 +560,34 @@ namespace VeraCrypt } } + void File::GetFileSize (unsigned __int64& size) + { + if (!FileOpen) + { + SetLastError (LastError); + throw SystemException (SRC_POS); + } + + if (Elevated) + { + Elevator::GetFileSize (Path, &size); + } + else + { + LARGE_INTEGER lSize; + lSize.QuadPart = 0; + throw_sys_if (!GetFileSizeEx (Handle, &lSize)); + size = (unsigned __int64) lSize.QuadPart; + } + } + + void File::GetFileSize (DWORD& dwSize) + { + unsigned __int64 size64; + GetFileSize (size64); + dwSize = (DWORD) size64; + } + void File::Write (byte *buffer, DWORD size) { DWORD bytesWritten; @@ -416,6 +629,25 @@ namespace VeraCrypt } } + bool File::IoCtl(DWORD code, void* inBuf, DWORD inBufSize, void* outBuf, DWORD outBufSize) + { + if (!FileOpen) + { + SetLastError (LastError); + throw SystemException (SRC_POS); + } + + if (Elevated) + { + return TRUE == Elevator::DeviceIoControl (ReadOnly, IsDevice, Path, code, inBuf, inBufSize, outBuf, outBufSize); + } + else + { + DWORD bytesReturned = 0; + return TRUE == DeviceIoControl(Handle, code, inBuf, inBufSize, outBuf, outBufSize, &bytesReturned, NULL); + } + } + void Show (HWND parent, const wstring &str) { MessageBox (parent, str.c_str(), NULL, 0); @@ -449,14 +681,18 @@ namespace VeraCrypt FilePointerPosition = 0; IsDevice = true; Path = path; + ReadOnly = readOnly; } + static EfiBoot EfiBootInst; BootEncryption::BootEncryption (HWND parent) : DriveConfigValid (false), ParentWindow (parent), RealSystemDriveSizeValid (false), RescueIsoImage (nullptr), + RescueZipData (nullptr), + RescueZipSize (0), RescueVolumeHeaderValid (false), SelectedEncryptionAlgorithmId (0), SelectedPrfAlgorithmId (0), @@ -476,7 +712,15 @@ namespace VeraCrypt BootEncryption::~BootEncryption () { if (RescueIsoImage) + { + burn (RescueIsoImage, RescueIsoImageSize); delete[] RescueIsoImage; + } + if (RescueZipData) + { + burn (RescueZipData, RescueZipSize); + delete [] RescueZipData; + } Elevator::Release(); } @@ -513,50 +757,54 @@ namespace VeraCrypt bool activePartitionFound = false; bool candidateForHiddenOSFound = false; - if (config.SystemPartition.IsGPT) - throw ParameterIncorrect (SRC_POS); // It is assumed that CheckRequirements() had been called - - // Find the first active partition on the system drive - foreach (const Partition &partition, config.Partitions) + if (!config.SystemPartition.IsGPT) { - if (partition.Info.BootIndicator) +// throw ParameterIncorrect (SRC_POS); // It is assumed that CheckRequirements() had been called + + // Find the first active partition on the system drive + foreach (const Partition &partition, config.Partitions) { - if (partition.Info.PartitionNumber != config.SystemPartition.Number) + if (partition.Info.BootIndicator) { - // If there is an extra boot partition, the system partition must be located right behind it - if (IsOSAtLeast (WIN_7) && config.ExtraBootPartitionPresent) + if (partition.Info.PartitionNumber != config.SystemPartition.Number) { - int64 minOffsetFound = config.DrivePartition.Info.PartitionLength.QuadPart; - Partition bootPartition = partition; - Partition partitionBehindBoot; - - foreach (const Partition &partition, config.Partitions) + // If there is an extra boot partition, the system partition must be located right behind it + if (IsOSAtLeast (WIN_7) && config.ExtraBootPartitionPresent) { - if (partition.Info.StartingOffset.QuadPart > bootPartition.Info.StartingOffset.QuadPart - && partition.Info.StartingOffset.QuadPart < minOffsetFound) + int64 minOffsetFound = config.DrivePartition.Info.PartitionLength.QuadPart; + Partition bootPartition = partition; + Partition partitionBehindBoot; + + foreach (const Partition &partition, config.Partitions) { - minOffsetFound = partition.Info.StartingOffset.QuadPart; - partitionBehindBoot = partition; + if (partition.Info.StartingOffset.QuadPart > bootPartition.Info.StartingOffset.QuadPart + && partition.Info.StartingOffset.QuadPart < minOffsetFound) + { + minOffsetFound = partition.Info.StartingOffset.QuadPart; + partitionBehindBoot = partition; + } } - } - if (minOffsetFound != config.DrivePartition.Info.PartitionLength.QuadPart - && partitionBehindBoot.Number == config.SystemPartition.Number) - { - activePartitionFound = true; - break; + if (minOffsetFound != config.DrivePartition.Info.PartitionLength.QuadPart + && partitionBehindBoot.Number == config.SystemPartition.Number) + { + activePartitionFound = true; + break; + } } + + throw ErrorException (wstring (GetString ("SYSTEM_PARTITION_NOT_ACTIVE")) + + GetRemarksOnHiddenOS(), SRC_POS); } - throw ErrorException (wstring (GetString ("SYSTEM_PARTITION_NOT_ACTIVE")) - + GetRemarksOnHiddenOS(), SRC_POS); + activePartitionFound = true; + break; } - - activePartitionFound = true; - break; } + } else { + // For GPT + activePartitionFound = true; } - /* WARNING: Note that the partition number at the end of a device path (\Device\HarddiskY\PartitionX) must NOT be used to find the first partition physically located behind the active one. The reason is that the user may have deleted and created partitions during this session and e.g. the second partition could have @@ -1065,6 +1313,7 @@ namespace VeraCrypt int ea = 0; int pkcs5_prf = 0; + BOOL bIsGPT = GetSystemDriveConfiguration().SystemPartition.IsGPT; if (GetStatus().DriveMounted) { try @@ -1082,11 +1331,23 @@ namespace VeraCrypt ea = SERPENT; else if (_stricmp (request.BootEncryptionAlgorithmName, "Twofish") == 0) ea = TWOFISH; + else if (_stricmp (request.BootEncryptionAlgorithmName, "Camellia") == 0) + ea = CAMELLIA; +#if defined(CIPHER_GOST89) + else if (_stricmp (request.BootEncryptionAlgorithmName, "GOST89") == 0) + ea = GOST89; +#endif if (_stricmp(request.BootPrfAlgorithmName, "SHA-256") == 0) pkcs5_prf = SHA256; else if (_stricmp(request.BootPrfAlgorithmName, "RIPEMD-160") == 0) pkcs5_prf = RIPEMD160; + else if (_stricmp(request.BootPrfAlgorithmName, "SHA-512") == 0) + pkcs5_prf = SHA512; + else if (_stricmp(request.BootPrfAlgorithmName, "Whirlpool") == 0) + pkcs5_prf = WHIRLPOOL; + else if (_stricmp(request.BootPrfAlgorithmName, "Streebog") == 0) + pkcs5_prf = STREEBOG; else if (strlen(request.BootPrfAlgorithmName) == 0) // case of version < 1.0f pkcs5_prf = RIPEMD160; } @@ -1101,6 +1362,9 @@ namespace VeraCrypt } catch (...) { } } + + if (pkcs5_prf == 0) + throw ParameterIncorrect (SRC_POS); } else { @@ -1111,8 +1375,8 @@ namespace VeraCrypt pkcs5_prf = SelectedPrfAlgorithmId; } - // Only RIPEMD160 and SHA-256 are supported for boot loader - if (pkcs5_prf != RIPEMD160 && pkcs5_prf != SHA256) + // Only RIPEMD160 and SHA-256 are supported for MBR boot loader + if (!bIsGPT && pkcs5_prf != RIPEMD160 && pkcs5_prf != SHA256) throw ParameterIncorrect (SRC_POS); int bootSectorId = 0; @@ -1169,6 +1433,19 @@ namespace VeraCrypt bootLoaderId = rescueDisk ? IDR_RESCUE_LOADER_TWOFISH : IDR_BOOT_LOADER_TWOFISH; } break; + + case CAMELLIA: + if (pkcs5_prf == SHA256) + { + bootSectorId = rescueDisk ? IDR_RESCUE_BOOT_SECTOR_CAMELLIA_SHA2 : IDR_BOOT_SECTOR_CAMELLIA_SHA2; + bootLoaderId = rescueDisk ? IDR_RESCUE_LOADER_CAMELLIA_SHA2 : IDR_BOOT_LOADER_CAMELLIA_SHA2; + } + else + { + bootSectorId = rescueDisk ? IDR_RESCUE_BOOT_SECTOR_CAMELLIA : IDR_BOOT_SECTOR_CAMELLIA; + bootLoaderId = rescueDisk ? IDR_RESCUE_LOADER_CAMELLIA : IDR_BOOT_LOADER_CAMELLIA; + } + break; } // Boot sector @@ -1233,47 +1510,128 @@ namespace VeraCrypt } } - - void BootEncryption::ReadBootSectorConfig (byte *config, size_t bufLength, byte *userConfig, string *customUserMessage, uint16 *bootLoaderVersion) + void BootEncryption::ReadEfiConfig (byte* confContent, DWORD maxSize, DWORD* pcbRead) { - if (config && bufLength < TC_BOOT_CFG_FLAG_AREA_SIZE) + if (!pcbRead) throw ParameterIncorrect (SRC_POS); - GetSystemDriveConfigurationRequest request; - StringCchCopyW (request.DevicePath, ARRAYSIZE (request.DevicePath), GetSystemDriveConfiguration().DeviceKernelPath.c_str()); + if (!IsAdmin() && IsUacSupported()) + { + Elevator::ReadEfiConfig (confContent, maxSize, pcbRead); + } + else + { + unsigned __int64 ui64Size = 0; + + finally_do ({ EfiBootInst.DismountBootPartition(); }); + EfiBootInst.MountBootPartition(0); + + EfiBootInst.GetFileSize(L"\\EFI\\VeraCrypt\\DcsProp", ui64Size); + + *pcbRead = (DWORD) ui64Size; + + if (*pcbRead > maxSize) + throw ParameterIncorrect (SRC_POS); + EfiBootInst.ReadFile (L"\\EFI\\VeraCrypt\\DcsProp", confContent, *pcbRead); + } + } + + // return false when the user cancel an elevation request + bool BootEncryption::ReadBootSectorConfig (byte *config, size_t bufLength, byte *userConfig, string *customUserMessage, uint16 *bootLoaderVersion) + { + bool bCanceled = false, bExceptionOccured = false; try { - CallDriver (TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG, &request, sizeof (request), &request, sizeof (request)); - if (config) - *config = request.Configuration; + if (GetSystemDriveConfiguration().SystemPartition.IsGPT) + { + byte confContent[4096]; + DWORD dwSize; - if (userConfig) - *userConfig = request.UserConfiguration; + // for now, we don't support any boot config flags, like hidden OS one + if (config) + memset (config, 0, bufLength); - if (customUserMessage) - { - request.CustomUserMessage[TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH] = 0; - *customUserMessage = request.CustomUserMessage; + // call ReadEfiConfig only when needed since it requires elevation + if (userConfig || customUserMessage || bootLoaderVersion) + { + ReadEfiConfig (confContent, sizeof (confContent) - 1, &dwSize); + + confContent[dwSize] = 0; + + EfiBootConf conf; + conf.Load ((char*) confContent); + + if (userConfig) + { + *userConfig = 0; + if (!conf.requestPim) + *userConfig |= TC_BOOT_USER_CFG_FLAG_DISABLE_PIM; + if (!conf.requestHash) + *userConfig |= TC_BOOT_USER_CFG_FLAG_STORE_HASH; + + } + + if (customUserMessage) + customUserMessage->clear(); + + if (bootLoaderVersion) + { + *bootLoaderVersion = GetStatus().BootLoaderVersion; + } + } } + else + { + if (config && bufLength < TC_BOOT_CFG_FLAG_AREA_SIZE) + throw ParameterIncorrect (SRC_POS); - if (bootLoaderVersion) - *bootLoaderVersion = request.BootLoaderVersion; + GetSystemDriveConfigurationRequest request; + StringCchCopyW (request.DevicePath, ARRAYSIZE (request.DevicePath), GetSystemDriveConfiguration().DeviceKernelPath.c_str()); + + CallDriver (TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG, &request, sizeof (request), &request, sizeof (request)); + if (config) + *config = request.Configuration; + + if (userConfig) + *userConfig = request.UserConfiguration; + + if (customUserMessage) + { + request.CustomUserMessage[TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH] = 0; + *customUserMessage = request.CustomUserMessage; + } + + if (bootLoaderVersion) + *bootLoaderVersion = request.BootLoaderVersion; + } + } + catch (UserAbort&) + { + bCanceled = true; + bExceptionOccured= true; } catch (...) { + bExceptionOccured = true; + } + + if (bExceptionOccured) + { if (config) *config = 0; if (userConfig) *userConfig = 0; - + if (customUserMessage) customUserMessage->clear(); if (bootLoaderVersion) *bootLoaderVersion = 0; } + + return !bCanceled; } @@ -1299,52 +1657,78 @@ namespace VeraCrypt throw ErrorException ("ERROR_MBR_PROTECTED", SRC_POS); } - - void BootEncryption::WriteBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim) + void BootEncryption::WriteEfiBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg) { - Device device (GetSystemDriveConfiguration().DevicePath); - device.CheckOpened (SRC_POS); - byte mbr[TC_SECTOR_SIZE_BIOS]; - - device.SeekAt (0); - device.Read (mbr, sizeof (mbr)); - - if (!BufferContainsString (mbr, sizeof (mbr), TC_APP_NAME) - || BE16 (*(uint16 *) (mbr + TC_BOOT_SECTOR_VERSION_OFFSET)) != VERSION_NUM) + if (!IsAdmin() && IsUacSupported()) { - return; + Elevator::WriteEfiBootSectorUserConfig (userConfig, customUserMessage, pim, hashAlg); } - - mbr[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] = userConfig; - - memset (mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, 0, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); - - if (!customUserMessage.empty()) + else { - if (customUserMessage.size() > TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH) - throw ParameterIncorrect (SRC_POS); + finally_do ({ EfiBootInst.DismountBootPartition(); }); + EfiBootInst.MountBootPartition(0); - memcpy (mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, customUserMessage.c_str(), customUserMessage.size()); + if (! (userConfig & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM)) + pim = -1; + if (! (userConfig & TC_BOOT_USER_CFG_FLAG_STORE_HASH)) + hashAlg = -1; + + EfiBootInst.UpdateConfig (L"\\EFI\\VeraCrypt\\DcsProp", pim, hashAlg, ParentWindow); } + } - if (userConfig & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) + void BootEncryption::WriteBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg) + { + if (GetSystemDriveConfiguration().SystemPartition.IsGPT) { - // PIM for pre-boot authentication can be encoded on two bytes since its maximum - // value is 65535 (0xFFFF) - memcpy (mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &pim, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + WriteEfiBootSectorUserConfig (userConfig, customUserMessage, pim, hashAlg); } else - memset (mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, 0, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + { + Device device (GetSystemDriveConfiguration().DevicePath); + device.CheckOpened (SRC_POS); + byte mbr[TC_SECTOR_SIZE_BIOS]; - device.SeekAt (0); - device.Write (mbr, sizeof (mbr)); + device.SeekAt (0); + device.Read (mbr, sizeof (mbr)); - byte mbrVerificationBuf[TC_SECTOR_SIZE_BIOS]; - device.SeekAt (0); - device.Read (mbrVerificationBuf, sizeof (mbr)); + if (!BufferContainsString (mbr, sizeof (mbr), TC_APP_NAME) + || BE16 (*(uint16 *) (mbr + TC_BOOT_SECTOR_VERSION_OFFSET)) != VERSION_NUM) + { + return; + } - if (memcmp (mbr, mbrVerificationBuf, sizeof (mbr)) != 0) - throw ErrorException ("ERROR_MBR_PROTECTED", SRC_POS); + mbr[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] = userConfig; + + memset (mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, 0, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); + + if (!customUserMessage.empty()) + { + if (customUserMessage.size() > TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH) + throw ParameterIncorrect (SRC_POS); + + memcpy (mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, customUserMessage.c_str(), customUserMessage.size()); + } + + if (userConfig & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) + { + // PIM for pre-boot authentication can be encoded on two bytes since its maximum + // value is 65535 (0xFFFF) + memcpy (mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &pim, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + } + else + memset (mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, 0, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + + device.SeekAt (0); + device.Write (mbr, sizeof (mbr)); + + byte mbrVerificationBuf[TC_SECTOR_SIZE_BIOS]; + device.SeekAt (0); + device.Read (mbrVerificationBuf, sizeof (mbr)); + + if (memcmp (mbr, mbrVerificationBuf, sizeof (mbr)) != 0) + throw ErrorException ("ERROR_MBR_PROTECTED", SRC_POS); + } } @@ -1386,7 +1770,7 @@ namespace VeraCrypt ZeroMemory (&request, sizeof (request)); request.WipeAlgorithm = wipeAlgorithm; - + if (Randinit() != ERR_SUCCESS) { if (CryptoAPILastError == ERROR_SUCCESS) @@ -1414,7 +1798,7 @@ namespace VeraCrypt CallDriver (TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE); } - + DecoySystemWipeStatus BootEncryption::GetDecoyOSWipeStatus () { DecoySystemWipeStatus status; @@ -1448,7 +1832,7 @@ namespace VeraCrypt device.SeekAt (0); device.Read (mbr, sizeof (mbr)); - + finally_do_arg (BootEncryption *, this, { try @@ -1494,66 +1878,767 @@ namespace VeraCrypt #endif // !SETUP + NtQuerySystemInformationFn NtQuerySystemInformationPtr = NULL; + + EfiBootConf::EfiBootConf() : passwordType (0), + passwordMsg ("Enter Password: "), + passwordPicture ("login.bmp"), + hashMsg ("(0) TEST ALL (1) SHA512 (2) WHIRLPOOL (3) SHA256 (4) RIPEMD160 (5) STREEBOG\nHash: "), + hashAlgo (0), + requestHash (0), + pimMsg ("PIM (Leave empty for default): "), + pim (0), + requestPim (1), + authorizeVisible (0), + authorizeRetry (10) + { + + } + + BOOL EfiBootConf::ReadConfigValue (char* configContent, const char *configKey, char *configValue, int maxValueSize) + { + char *xml; + + xml = configContent; + if (xml != NULL) + { + xml = XmlFindElementByAttributeValue (xml, "config", "key", configKey); + if (xml != NULL) + { + XmlGetNodeText (xml, configValue, maxValueSize); + return TRUE; + } + } + + return FALSE; + } + + + int EfiBootConf::ReadConfigInteger (char* configContent, const char *configKey, int defaultValue) + { + char s[32]; + int iRet; + if (ReadConfigValue (configContent, configKey, s, sizeof (s))) + iRet = atoi (s); + else + iRet = defaultValue; + burn (s, sizeof (s)); + return iRet; + } + + + char* EfiBootConf::ReadConfigString (char* configContent, const char *configKey, char *defaultValue, char *str, int maxLen) + { + if (ReadConfigValue (configContent, configKey, str, maxLen)) + return str; + else + { + StringCbCopyA (str, maxLen, defaultValue); + return defaultValue; + } + } + + BOOL EfiBootConf::WriteConfigString (FILE* configFile, char* configContent, const char *configKey, const char *configValue) + { + + BOOL bRet = FALSE; + if (configFile) + { + char *c; + // Mark previous config value as updated + if (configContent != NULL) + { + c = XmlFindElementByAttributeValue (configContent, "config", "key", configKey); + if (c != NULL) + c[1] = '!'; + } + + if ( 0 != fwprintf ( + configFile, L"\n\t\t<config key=\"%hs\">%hs</config>", + configKey, configValue)) + { + bRet = TRUE; + } + } + return bRet; + } + + BOOL EfiBootConf::WriteConfigInteger (FILE* configFile, char* configContent, const char *configKey, int configValue) + { + BOOL bRet = FALSE; + if (configFile) + { + char val[32]; + StringCbPrintfA (val, sizeof(val), "%d", configValue); + bRet = WriteConfigString (configFile, configContent, configKey, val); + burn (val, sizeof (val)); + } + return bRet; + } + + BOOL EfiBootConf::Load (const wchar_t* fileName) + { + DWORD size = 0; + char* configContent = LoadFile (fileName, &size); + if (configContent) + { + Load (configContent); + burn (configContent, size); + free (configContent); + return TRUE; + } + else + return FALSE; + } + + void EfiBootConf::Load (char* configContent) + { + char buffer[1024]; + + passwordType = ReadConfigInteger (configContent, "PasswordType", 0); + passwordMsg = ReadConfigString (configContent, "PasswordMsg", "Enter password: ", buffer, sizeof (buffer)); + passwordPicture = ReadConfigString (configContent, "PasswordPicture", "\\EFI\\VeraCrypt\\login.bmp", buffer, sizeof (buffer)); + //hashMsg = ReadConfigString (configContent, "HashMsg", "(0) TEST ALL (1) SHA512 (2) WHIRLPOOL (3) SHA256 (4) RIPEMD160 (5) STREEBOG\nHash: ", buffer, sizeof (buffer)); + hashAlgo = ReadConfigInteger (configContent, "Hash", 0); + requestHash = ReadConfigInteger (configContent, "HashRqt", 1); + pimMsg = ReadConfigString (configContent, "PimMsg", "PIM: ", buffer, sizeof (buffer)); + pim = ReadConfigInteger (configContent, "Pim", 0); + requestPim = ReadConfigInteger (configContent, "PimRqt", 1); + authorizeVisible = ReadConfigInteger (configContent, "AuthorizeVisible", 0); + authorizeRetry = ReadConfigInteger (configContent, "AuthorizeRetry", 0); + + burn (buffer, sizeof (buffer)); + } + + BOOL EfiBootConf::Save (const wchar_t* fileName, HWND hwnd) + { + FILE *configFile = _wfopen (fileName, L"w,ccs=UTF-8"); + if (configFile == NULL) + return FALSE; + + BOOL bRet = FALSE; + DWORD size = 0; + char* configContent = LoadFile (fileName, &size); + + + XmlWriteHeader (configFile); + fputws (L"\n\t<configuration>", configFile); + + WriteConfigInteger (configFile, configContent, "PasswordType", passwordType); + WriteConfigString (configFile, configContent, "PasswordMsg", passwordMsg.c_str()); + WriteConfigString (configFile, configContent, "PasswordPicture", passwordPicture.c_str()); + WriteConfigString (configFile, configContent, "HashMsg", hashMsg.c_str()); + WriteConfigInteger (configFile, configContent, "Hash", hashAlgo); + WriteConfigInteger (configFile, configContent, "HashRqt", requestHash); + WriteConfigString (configFile, configContent, "PimMsg", pimMsg.c_str()); + WriteConfigInteger (configFile, configContent, "Pim", pim); + WriteConfigInteger (configFile, configContent, "PimRqt", requestPim); + WriteConfigInteger (configFile, configContent, "AuthorizeVisible", authorizeVisible); + WriteConfigInteger (configFile, configContent, "AuthorizeRetry", authorizeRetry); + + // Write unmodified values + char* xml = configContent; + char key[128], value[2048]; + while (xml && (xml = XmlFindElement (xml, "config"))) + { + XmlGetAttributeText (xml, "key", key, sizeof (key)); + XmlGetNodeText (xml, value, sizeof (value)); + + fwprintf (configFile, L"\n\t\t<config key=\"%hs\">%hs</config>", key, value); + xml++; + } + + fputws (L"\n\t</configuration>", configFile); + XmlWriteFooter (configFile); + + TCFlushFile (configFile); + + bRet = CheckFileStreamWriteErrors (hwnd, configFile, fileName); + + fclose (configFile); + + if (configContent != NULL) + { + burn (configContent, size); + free (configContent); + } + + return bRet; + } + + static const wchar_t* EfiVarGuid = L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"; + + EfiBoot::EfiBoot() { + ZeroMemory(EfiBootPartPath, sizeof(EfiBootPartPath)); + ZeroMemory (systemPartitionPath, sizeof (systemPartitionPath)); + m_bMounted = false; + } + + void EfiBoot::MountBootPartition(WCHAR letter) { + NTSTATUS res; + ULONG len; + memset(tempBuf, 0, sizeof(tempBuf)); + + // Load NtQuerySystemInformation function point + if (!NtQuerySystemInformationPtr) + { + NtQuerySystemInformationPtr = (NtQuerySystemInformationFn) GetProcAddress (GetModuleHandle (L"ntdll.dll"), "NtQuerySystemInformation"); + if (!NtQuerySystemInformationPtr) + throw SystemException (SRC_POS); + } + + res = NtQuerySystemInformationPtr((SYSTEM_INFORMATION_CLASS)SYSPARTITIONINFORMATION, tempBuf, sizeof(tempBuf), &len); + if (res != S_OK) + { + SetLastError (res); + throw SystemException (SRC_POS); + } + + PUNICODE_STRING pStr = (PUNICODE_STRING) tempBuf; + memcpy (systemPartitionPath, pStr->Buffer, min (pStr->Length, (sizeof (systemPartitionPath) - 2))); + + if (!letter) { + if (!GetFreeDriveLetter(&EfiBootPartPath[0])) { + throw ErrorException(L"No free letter to mount EFI boot partition", SRC_POS); + } + } else { + EfiBootPartPath[0] = letter; + } + EfiBootPartPath[1] = ':'; + EfiBootPartPath[2] = 0; + throw_sys_if(!DefineDosDevice(DDD_RAW_TARGET_PATH, EfiBootPartPath, systemPartitionPath)); + + Device dev(EfiBootPartPath, TRUE); + + try + { + dev.CheckOpened(SRC_POS); + } + catch (...) + { + DefineDosDevice(DDD_REMOVE_DEFINITION, EfiBootPartPath, NULL); + throw; + } + + bool bSuccess = dev.IoCtl(IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn)) + && dev.IoCtl(IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &partInfo, sizeof(partInfo)); + DWORD dwLastError = GetLastError (); + dev.Close(); + if (!bSuccess) + { + DefineDosDevice(DDD_REMOVE_DEFINITION, EfiBootPartPath, NULL); + SetLastError (dwLastError); + throw SystemException(SRC_POS); + } + + m_bMounted = true; + } + + void EfiBoot::DismountBootPartition() { + if (m_bMounted) + { + DefineDosDevice(DDD_REMOVE_DEFINITION, EfiBootPartPath, NULL); + m_bMounted = false; + } + } + + bool EfiBoot::IsEfiBoot() { + DWORD BootOrderLen; + BootOrderLen = GetFirmwareEnvironmentVariable(L"BootOrder", EfiVarGuid, tempBuf, sizeof(tempBuf)); + return BootOrderLen != 0; + } + + void EfiBoot::DeleteStartExec(uint16 statrtOrderNum, wchar_t* type) { + RaisePrivileges(); + // Check EFI + if (!IsEfiBoot()) { + throw ErrorException(L"can not detect EFI environment", SRC_POS); + } + wchar_t varName[256]; + StringCchPrintfW(varName, ARRAYSIZE (varName), L"%s%04X", type == NULL ? L"Boot" : type, statrtOrderNum); + SetFirmwareEnvironmentVariable(varName, EfiVarGuid, NULL, 0); + + wstring order = L"Order"; + order.insert(0, type == NULL ? L"Boot" : type); + uint32 startOrderLen = GetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, tempBuf, sizeof(tempBuf)); + uint32 startOrderNumPos = UINT_MAX; + bool startOrderUpdate = false; + uint16* startOrder = (uint16*)tempBuf; + for (uint32 i = 0; i < startOrderLen / 2; i++) { + if (startOrder[i] == statrtOrderNum) { + startOrderNumPos = i; + break; + } + } + + // delete entry if present + if (startOrderNumPos != UINT_MAX) { + for (uint32 i = startOrderNumPos; i < ((startOrderLen / 2) - 1); ++i) { + startOrder[i] = startOrder[i + 1]; + } + startOrderLen -= 2; + startOrderUpdate = true; + } + + if (startOrderUpdate) { + SetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, startOrder, startOrderLen); + + // remove ourselves from BootNext value + uint16 bootNextValue = 0; + wstring next = L"Next"; + next.insert(0, type == NULL ? L"Boot" : type); + + if ( (GetFirmwareEnvironmentVariable(next.c_str(), EfiVarGuid, &bootNextValue, 2) == 2) + && (bootNextValue == statrtOrderNum) + ) + { + SetFirmwareEnvironmentVariable(next.c_str(), EfiVarGuid, startOrder, 0); + } + } + } + + void EfiBoot::SetStartExec(wstring description, wstring execPath, uint16 statrtOrderNum , wchar_t* type, uint32 attr) { + RaisePrivileges(); + // Check EFI + if (!IsEfiBoot()) { + throw ErrorException(L"can not detect EFI environment", SRC_POS); + } + + uint32 varSize = 56; + varSize += ((uint32) description.length()) * 2 + 2; + varSize += ((uint32) execPath.length()) * 2 + 2; + byte *startVar = new byte[varSize]; + byte *pVar = startVar; + + // Attributes (1b Active, 1000b - Hidden) + *(uint32 *)pVar = attr; + pVar += sizeof(uint32); + + // Size Of device path + file path + *(uint16 *)pVar = (uint16)(50 + execPath.length() * 2 + 2); + pVar += sizeof(uint16); + + // description + for (uint32 i = 0; i < description.length(); i++) { + *(uint16 *)pVar = description[i]; + pVar += sizeof(uint16); + } + *(uint16 *)pVar = 0; + pVar += sizeof(uint16); + + /* EFI_DEVICE_PATH_PROTOCOL (HARDDRIVE_DEVICE_PATH \ FILE_PATH \ END) */ + + // Type + *(byte *)pVar = 0x04; + pVar += sizeof(byte); + + // SubType + *(byte *)pVar = 0x01; + pVar += sizeof(byte); + + // HDD dev path length + *(uint16 *)pVar = 0x2A; // 42 + pVar += sizeof(uint16); + + // PartitionNumber + *(uint32 *)pVar = (uint32)partInfo.PartitionNumber; + pVar += sizeof(uint32); + + // PartitionStart + *(uint64 *)pVar = partInfo.StartingOffset.QuadPart >> 9; + pVar += sizeof(uint64); + + // PartitiontSize + *(uint64 *)pVar = partInfo.PartitionLength.QuadPart >> 9; + pVar += sizeof(uint64); + + // GptGuid + memcpy(pVar, &partInfo.Gpt.PartitionId, 16); + pVar += 16; + + // MbrType + *(byte *)pVar = 0x02; + pVar += sizeof(byte); + + // SigType + *(byte *)pVar = 0x02; + pVar += sizeof(byte); + + // Type and sub type 04 04 (file path) + *(uint16 *)pVar = 0x0404; + pVar += sizeof(uint16); + + // SizeOfFilePath ((CHAR16)FullPath.length + sizeof(EndOfrecord marker) ) + *(uint16 *)pVar = (uint16)(execPath.length() * 2 + 2 + sizeof(uint32)); + pVar += sizeof(uint16); + + // FilePath + for (uint32 i = 0; i < execPath.length(); i++) { + *(uint16 *)pVar = execPath[i]; + pVar += sizeof(uint16); + } + *(uint16 *)pVar = 0; + pVar += sizeof(uint16); + + // EndOfrecord + *(uint32 *)pVar = 0x04ff7f; + pVar += sizeof(uint32); + + // Set variable + wchar_t varName[256]; + StringCchPrintfW(varName, ARRAYSIZE (varName), L"%s%04X", type == NULL ? L"Boot" : type, statrtOrderNum); + SetFirmwareEnvironmentVariable(varName, EfiVarGuid, startVar, varSize); + delete startVar; + + // Update order + wstring order = L"Order"; + order.insert(0, type == NULL ? L"Boot" : type); + + uint32 startOrderLen = GetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, tempBuf, sizeof(tempBuf)); + uint32 startOrderNumPos = UINT_MAX; + bool startOrderUpdate = false; + uint16* startOrder = (uint16*)tempBuf; + for (uint32 i = 0; i < startOrderLen / 2; i++) { + if (startOrder[i] == statrtOrderNum) { + startOrderNumPos = i; + break; + } + } + + // Create new entry if absent + if (startOrderNumPos == UINT_MAX) { + for (uint32 i = startOrderLen / 2; i > 0; --i) { + startOrder[i] = startOrder[i - 1]; + } + startOrder[0] = statrtOrderNum; + startOrderLen += 2; + startOrderUpdate = true; + } else if (startOrderNumPos > 0) { + for (uint32 i = startOrderNumPos; i > 0; --i) { + startOrder[i] = startOrder[i - 1]; + } + startOrder[0] = statrtOrderNum; + startOrderUpdate = true; + } + + if (startOrderUpdate) { + SetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, startOrder, startOrderLen); + } + + // set BootNext value + wstring next = L"Next"; + next.insert(0, type == NULL ? L"Boot" : type); + + SetFirmwareEnvironmentVariable(next.c_str(), EfiVarGuid, &statrtOrderNum, 2); - void BootEncryption::InstallBootLoader (bool preserveUserConfig, bool hiddenOSCreation) + } + + void EfiBoot::SaveFile(wchar_t* name, byte* data, DWORD size) { + wstring path = EfiBootPartPath; + path += name; + File f(path, false, true); + f.Write(data, size); + f.Close(); + } + + void EfiBoot::GetFileSize(const wchar_t* name, unsigned __int64& size) { + wstring path = EfiBootPartPath; + path += name; + File f(path, true); + f.GetFileSize(size); + f.Close(); + } + + void EfiBoot::ReadFile(const wchar_t* name, byte* data, DWORD size) { + wstring path = EfiBootPartPath; + path += name; + File f(path, true); + f.Read(data, size); + f.Close(); + } + + void EfiBoot::CopyFile(const wchar_t* name, const wchar_t* targetName) { + wstring path = EfiBootPartPath; + path += name; + wstring targetPath; + if (targetName[0] == L'\\') + { + targetPath = EfiBootPartPath; + targetPath += targetName; + } + else + targetPath = targetName; + throw_sys_if (!::CopyFileW (path.c_str(), targetPath.c_str(), FALSE)); + } + + BOOL EfiBoot::RenameFile(wchar_t* name, wchar_t* nameNew, BOOL bForce) { + wstring path = EfiBootPartPath; + path += name; + wstring pathNew = EfiBootPartPath; + pathNew += nameNew; + return MoveFileExW(path.c_str(), pathNew.c_str(), bForce? MOVEFILE_REPLACE_EXISTING : 0); + } + + BOOL EfiBoot::DelFile(wchar_t* name) { + wstring path = EfiBootPartPath; + path += name; + return DeleteFile(path.c_str()); + } + + BOOL EfiBoot::MkDir(wchar_t* name, bool& bAlreadyExists) { + wstring path = EfiBootPartPath; + path += name; + bAlreadyExists = false; + BOOL bRet = CreateDirectory(path.c_str(), NULL); + if (!bRet && (GetLastError () == ERROR_ALREADY_EXISTS)) + { + bRet = TRUE; + bAlreadyExists = true; + } + return bRet; + } + + BOOL EfiBoot::ReadConfig (wchar_t* name, EfiBootConf& conf) + { + wstring path = EfiBootPartPath; + path += name; + + return conf.Load (path.c_str()); + } + + BOOL EfiBoot::UpdateConfig (wchar_t* name, int pim, int hashAlgo, HWND hwndDlg) + { + BOOL bRet = FALSE; + EfiBootConf conf; + wstring path = EfiBootPartPath; + path += name; + + if (conf.Load (path.c_str())) + { + if (pim >= 0) + { + conf.pim = pim; + conf.requestPim = 0; + } + else + { + conf.pim = 0; + conf.requestPim = 1; + } + + if (hashAlgo >= 0) + { + conf.hashAlgo = hashAlgo; + conf.requestHash = 0; + } + else + { + conf.hashAlgo = 0; + conf.requestHash = 1; + } + + return conf.Save (path.c_str(), hwndDlg); + } + + return bRet; + } + + BOOL EfiBoot::WriteConfig (wchar_t* name, bool preserveUserConfig, int pim, int hashAlgo, const char* passPromptMsg, HWND hwndDlg) + { + EfiBootConf conf; + wstring path = EfiBootPartPath; + path += name; + + if (preserveUserConfig) + { + conf.Load (path.c_str()); + if (pim >= 0 && (conf.requestPim == 0)) + { + conf.pim = pim; + } + if (hashAlgo >= 0 && (conf.requestHash == 0)) + { + conf.hashAlgo = hashAlgo; + } + } + else + { + if (pim >= 0) + { + conf.pim = pim; + conf.requestPim = 0; + } + else + { + conf.pim = 0; + conf.requestPim = 1; + } + + if (hashAlgo >= 0) + { + conf.hashAlgo = hashAlgo; + conf.requestHash = 0; + } + else + { + conf.hashAlgo = 0; + conf.requestHash = 1; + } + } + + if (passPromptMsg && strlen (passPromptMsg)) + { + conf.passwordMsg = passPromptMsg; + } + + return conf.Save (path.c_str(), hwndDlg); + } + + void BootEncryption::InstallBootLoader (bool preserveUserConfig, bool hiddenOSCreation, int pim, int hashAlg) { Device device (GetSystemDriveConfiguration().DevicePath); device.CheckOpened (SRC_POS); - InstallBootLoader (device, preserveUserConfig, hiddenOSCreation); + InstallBootLoader (device, preserveUserConfig, hiddenOSCreation, pim, hashAlg); } - void BootEncryption::InstallBootLoader (Device& device, bool preserveUserConfig, bool hiddenOSCreation, int pim) + void BootEncryption::InstallBootLoader (Device& device, bool preserveUserConfig, bool hiddenOSCreation, int pim, int hashAlg) { - byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SIZE - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE] = {0}; - CreateBootLoaderInMemory (bootLoaderBuf, sizeof (bootLoaderBuf), false, hiddenOSCreation); + SystemDriveConfiguration config = GetSystemDriveConfiguration(); - // Write MBR - byte mbr[TC_SECTOR_SIZE_BIOS]; + if (config.SystemPartition.IsGPT) { + if (!IsAdmin()) { + if (IsUacSupported()) + { + Elevator::InstallEfiBootLoader (preserveUserConfig, hiddenOSCreation, pim, hashAlg); + return; + } + else + { + Warning ("ADMIN_PRIVILEGES_WARN_DEVICES", ParentWindow); + } + } + DWORD sizeDcsBoot; + byte *dcsBootImg = MapResource(L"BIN", IDR_EFI_DCSBOOT, &sizeDcsBoot); + if (!dcsBootImg) + throw ErrorException(L"Out of resource DcsBoot", SRC_POS); + DWORD sizeDcsInt; + byte *dcsIntImg = MapResource(L"BIN", IDR_EFI_DCSINT, &sizeDcsInt); + if (!dcsIntImg) + throw ErrorException(L"Out of resource DcsInt", SRC_POS); + DWORD sizeDcsCfg; + byte *dcsCfgImg = MapResource(L"BIN", IDR_EFI_DCSCFG, &sizeDcsCfg); + if (!dcsCfgImg) + throw ErrorException(L"Out of resource DcsCfg", SRC_POS); + DWORD sizeLegacySpeaker; + byte *LegacySpeakerImg = MapResource(L"BIN", IDR_EFI_LEGACYSPEAKER, &sizeLegacySpeaker); + if (!LegacySpeakerImg) + throw ErrorException(L"Out of resource LegacySpeaker", SRC_POS); + DWORD sizeBootMenuLocker; + byte *BootMenuLockerImg = MapResource(L"BIN", IDR_EFI_DCSBML, &sizeBootMenuLocker); + if (!BootMenuLockerImg) + throw ErrorException(L"Out of resource DcsBml", SRC_POS); + + finally_do ({ EfiBootInst.DismountBootPartition(); }); + EfiBootInst.MountBootPartition(0); - device.SeekAt (0); - device.Read (mbr, sizeof (mbr)); + try + { + // Save modules + bool bAlreadyExist; + + EfiBootInst.MkDir(L"\\EFI\\VeraCrypt", bAlreadyExist); + EfiBootInst.SaveFile(L"\\EFI\\VeraCrypt\\DcsBoot.efi", dcsBootImg, sizeDcsBoot); + EfiBootInst.SaveFile(L"\\EFI\\Boot\\bootx64.efi", dcsBootImg, sizeDcsBoot); + EfiBootInst.SaveFile(L"\\EFI\\VeraCrypt\\DcsInt.dcs", dcsIntImg, sizeDcsInt); + EfiBootInst.SaveFile(L"\\EFI\\VeraCrypt\\DcsCfg.dcs", dcsCfgImg, sizeDcsCfg); + EfiBootInst.SaveFile(L"\\EFI\\VeraCrypt\\LegacySpeaker.dcs", LegacySpeakerImg, sizeLegacySpeaker); + EfiBootInst.SaveFile(L"\\EFI\\VeraCrypt\\DcsBml.dcs", BootMenuLockerImg, sizeBootMenuLocker); + EfiBootInst.SetStartExec(L"VeraCrypt BootLoader (DcsBoot)", L"\\EFI\\VeraCrypt\\DcsBoot.efi"); + + // move configuration file from old location (if it exists) to new location + // we don't force the move operation if the new location already exists + EfiBootInst.RenameFile (L"\\DcsProp", L"\\EFI\\VeraCrypt\\DcsProp", FALSE); + EfiBootInst.RenameFile (L"\\DcsBoot", L"\\EFI\\VeraCrypt\\DcsBoot", FALSE); + + // move the original bootloader backup from old location (if it exists) to new location + // we don't force the move operation if the new location already exists + EfiBootInst.RenameFile (L"\\EFI\\Boot\\original_bootx64_vc_backup.efi", L"\\EFI\\Boot\\original_bootx64.vc_backup", FALSE); + + // Clean beta9 + EfiBootInst.DelFile(L"\\DcsBoot.efi"); + EfiBootInst.DelFile(L"\\DcsInt.efi"); + EfiBootInst.DelFile(L"\\DcsCfg.efi"); + EfiBootInst.DelFile(L"\\LegacySpeaker.efi"); + EfiBootInst.DelFile(L"\\DcsBoot"); + EfiBootInst.DelFile(L"\\DcsProp"); + } + catch (...) + { + throw; + } - if (preserveUserConfig && BufferContainsString (mbr, sizeof (mbr), TC_APP_NAME)) + EfiBootInst.WriteConfig (L"\\EFI\\VeraCrypt\\DcsProp", preserveUserConfig, pim, hashAlg, NULL, ParentWindow); + } + else { - uint16 version = BE16 (*(uint16 *) (mbr + TC_BOOT_SECTOR_VERSION_OFFSET)); - if (version != 0) - { - bootLoaderBuf[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] = mbr[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; - memcpy (bootLoaderBuf + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); + byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SIZE - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE] = {0}; + CreateBootLoaderInMemory (bootLoaderBuf, sizeof (bootLoaderBuf), false, hiddenOSCreation); + + // Write MBR + byte mbr[TC_SECTOR_SIZE_BIOS]; - if (bootLoaderBuf[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) + device.SeekAt (0); + device.Read (mbr, sizeof (mbr)); + + if (preserveUserConfig && BufferContainsString (mbr, sizeof (mbr), TC_APP_NAME)) + { + uint16 version = BE16 (*(uint16 *) (mbr + TC_BOOT_SECTOR_VERSION_OFFSET)); + if (version != 0) { - if (pim >= 0) + bootLoaderBuf[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] = mbr[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; + memcpy (bootLoaderBuf + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, mbr + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); + + if (bootLoaderBuf[TC_BOOT_SECTOR_USER_CONFIG_OFFSET] & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) { - memcpy (bootLoaderBuf + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &pim, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + if (pim >= 0) + { + memcpy (bootLoaderBuf + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &pim, TC_BOOT_SECTOR_PIM_VALUE_SIZE); + } + else + memcpy (bootLoaderBuf + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, TC_BOOT_SECTOR_PIM_VALUE_SIZE); } - else - memcpy (bootLoaderBuf + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, mbr + TC_BOOT_SECTOR_PIM_VALUE_OFFSET, TC_BOOT_SECTOR_PIM_VALUE_SIZE); } } - } - memcpy (mbr, bootLoaderBuf, TC_MAX_MBR_BOOT_CODE_SIZE); + memcpy (mbr, bootLoaderBuf, TC_MAX_MBR_BOOT_CODE_SIZE); - device.SeekAt (0); - device.Write (mbr, sizeof (mbr)); + device.SeekAt (0); + device.Write (mbr, sizeof (mbr)); - byte mbrVerificationBuf[TC_SECTOR_SIZE_BIOS]; - device.SeekAt (0); - device.Read (mbrVerificationBuf, sizeof (mbr)); + byte mbrVerificationBuf[TC_SECTOR_SIZE_BIOS]; + device.SeekAt (0); + device.Read (mbrVerificationBuf, sizeof (mbr)); - if (memcmp (mbr, mbrVerificationBuf, sizeof (mbr)) != 0) - throw ErrorException ("ERROR_MBR_PROTECTED", SRC_POS); + if (memcmp (mbr, mbrVerificationBuf, sizeof (mbr)) != 0) + throw ErrorException ("ERROR_MBR_PROTECTED", SRC_POS); - // Write boot loader - device.SeekAt (TC_SECTOR_SIZE_BIOS); - device.Write (bootLoaderBuf + TC_SECTOR_SIZE_BIOS, sizeof (bootLoaderBuf) - TC_SECTOR_SIZE_BIOS); + // Write boot loader + device.SeekAt (TC_SECTOR_SIZE_BIOS); + device.Write (bootLoaderBuf + TC_SECTOR_SIZE_BIOS, sizeof (bootLoaderBuf) - TC_SECTOR_SIZE_BIOS); + } } #ifndef SETUP bool BootEncryption::CheckBootloaderFingerprint (bool bSilent) { + SystemDriveConfiguration config = GetSystemDriveConfiguration(); + + // return true for now when EFI system encryption is used until we implement + // a dedicated EFI fingerprinting mechanism in VeraCrypt driver + if (config.SystemPartition.IsGPT) + return true; + byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SIZE - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE] = {0}; byte fingerprint[WHIRLPOOL_DIGESTSIZE + SHA512_DIGESTSIZE]; byte expectedFingerprint[WHIRLPOOL_DIGESTSIZE + SHA512_DIGESTSIZE]; @@ -1594,7 +2679,7 @@ namespace VeraCrypt WCHAR pathBuf[MAX_PATH]; throw_sys_if (!SUCCEEDED (SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, pathBuf))); - + wstring path = wstring (pathBuf) + L"\\" _T(TC_APP_NAME); CreateDirectory (path.c_str(), NULL); @@ -1622,126 +2707,255 @@ namespace VeraCrypt BootEncryptionStatus encStatus = GetStatus(); if (encStatus.SetupInProgress) throw ParameterIncorrect (SRC_POS); + BOOL bIsGPT = GetSystemDriveConfiguration().SystemPartition.IsGPT; + if (bIsGPT) + { + // create EFI disk structure + DWORD sizeDcsBoot; + byte *dcsBootImg = MapResource(L"BIN", IDR_EFI_DCSBOOT, &sizeDcsBoot); + if (!dcsBootImg) + throw ParameterIncorrect (SRC_POS); + DWORD sizeDcsInt; + byte *dcsIntImg = MapResource(L"BIN", IDR_EFI_DCSINT, &sizeDcsInt); + if (!dcsIntImg) + throw ParameterIncorrect (SRC_POS); + DWORD sizeDcsCfg; + byte *dcsCfgImg = MapResource(L"BIN", IDR_EFI_DCSCFG, &sizeDcsCfg); + if (!dcsCfgImg) + throw ParameterIncorrect (SRC_POS); + DWORD sizeLegacySpeaker; + byte *LegacySpeakerImg = MapResource(L"BIN", IDR_EFI_LEGACYSPEAKER, &sizeLegacySpeaker); + if (!LegacySpeakerImg) + throw ParameterIncorrect (SRC_POS); + DWORD sizeBootMenuLocker; + byte *BootMenuLockerImg = MapResource(L"BIN", IDR_EFI_DCSBML, &sizeBootMenuLocker); + if (!BootMenuLockerImg) + throw ParameterIncorrect (SRC_POS); + DWORD sizeDcsRescue; + byte *DcsRescueImg = MapResource(L"BIN", IDR_EFI_DCSRE, &sizeDcsRescue); + if (!DcsRescueImg) + throw ParameterIncorrect (SRC_POS); - Buffer imageBuf (RescueIsoImageSize); - - byte *image = imageBuf.Ptr(); - memset (image, 0, RescueIsoImageSize); - - // Primary volume descriptor - const char* szPrimVolDesc = "\001CD001\001"; - const char* szPrimVolLabel = "VeraCrypt Rescue Disk "; - memcpy (image + 0x8000, szPrimVolDesc, strlen(szPrimVolDesc) + 1); - memcpy (image + 0x7fff + 41, szPrimVolLabel, strlen(szPrimVolLabel) + 1); - *(uint32 *) (image + 0x7fff + 81) = RescueIsoImageSize / 2048; - *(uint32 *) (image + 0x7fff + 85) = BE32 (RescueIsoImageSize / 2048); - image[0x7fff + 121] = 1; - image[0x7fff + 124] = 1; - image[0x7fff + 125] = 1; - image[0x7fff + 128] = 1; - image[0x7fff + 130] = 8; - image[0x7fff + 131] = 8; - - image[0x7fff + 133] = 10; - image[0x7fff + 140] = 10; - image[0x7fff + 141] = 0x14; - image[0x7fff + 157] = 0x22; - image[0x7fff + 159] = 0x18; - - // Boot record volume descriptor - const char* szBootRecDesc = "CD001\001EL TORITO SPECIFICATION"; - memcpy (image + 0x8801, szBootRecDesc, strlen(szBootRecDesc) + 1); - image[0x8800 + 0x47] = 0x19; - - // Volume descriptor set terminator - const char* szVolDescTerm = "\377CD001\001"; - memcpy (image + 0x9000, szVolDescTerm, strlen(szVolDescTerm) + 1); - - // Path table - image[0xA000 + 0] = 1; - image[0xA000 + 2] = 0x18; - image[0xA000 + 6] = 1; - - // Root directory - image[0xc000 + 0] = 0x22; - image[0xc000 + 2] = 0x18; - image[0xc000 + 9] = 0x18; - image[0xc000 + 11] = 0x08; - image[0xc000 + 16] = 0x08; - image[0xc000 + 25] = 0x02; - image[0xc000 + 28] = 0x01; - image[0xc000 + 31] = 0x01; - image[0xc000 + 32] = 0x01; - image[0xc000 + 34] = 0x22; - image[0xc000 + 36] = 0x18; - image[0xc000 + 43] = 0x18; - image[0xc000 + 45] = 0x08; - image[0xc000 + 50] = 0x08; - image[0xc000 + 59] = 0x02; - image[0xc000 + 62] = 0x01; - *(uint32 *) (image + 0xc000 + 65) = 0x010101; - - // Validation entry - image[0xc800] = 1; - int offset = 0xc800 + 0x1c; - image[offset++] = 0xaa; - image[offset++] = 0x55; - image[offset++] = 0x55; - image[offset] = 0xaa; - - // Initial entry - offset = 0xc820; - image[offset++] = 0x88; - image[offset++] = 2; - image[0xc820 + 6] = 1; - image[0xc820 + 8] = TC_CD_BOOT_LOADER_SECTOR; - - // TrueCrypt Boot Loader - CreateBootLoaderInMemory (image + TC_CD_BOOTSECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE, true); - - // Volume header - if (initialSetup) - { - if (!RescueVolumeHeaderValid) + unsigned int maxRescueZipSize = 4 * 1024 * 1024; + ZRESULT res; + HZIP hz = CreateZip (0, maxRescueZipSize, ZIP_MEMORY); + if (!hz) throw ParameterIncorrect (SRC_POS); - memcpy (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, RescueVolumeHeader, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - } - else - { - Device bootDevice (GetSystemDriveConfiguration().DevicePath, true); - bootDevice.CheckOpened (SRC_POS); - bootDevice.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET); - bootDevice.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - } + finally_do_arg (HZIP, hz, { CloseZip (finally_arg); }); - // Original system loader - try - { - File sysBakFile (GetSystemLoaderBackupPath(), true); - sysBakFile.CheckOpened (SRC_POS); - sysBakFile.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE); + if (ZR_OK != ZipAdd (hz, L"EFI/Boot/bootx64.efi", DcsRescueImg, sizeDcsRescue, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + if (ZR_OK !=ZipAdd (hz, L"EFI/VeraCrypt/DcsBml.dcs", BootMenuLockerImg, sizeBootMenuLocker, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/DcsBoot.efi", dcsBootImg, sizeDcsBoot, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/DcsCfg.dcs", dcsCfgImg, sizeDcsCfg, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/DcsInt.dcs", dcsIntImg, sizeDcsInt, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/LegacySpeaker.dcs", LegacySpeakerImg, sizeLegacySpeaker, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + + Buffer volHeader(TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + + // Volume header + if (initialSetup) + { + if (!RescueVolumeHeaderValid) + throw ParameterIncorrect (SRC_POS); + + memcpy (volHeader.Ptr (), RescueVolumeHeader, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + } + else + { + Device bootDevice (GetSystemDriveConfiguration().DevicePath, true); + bootDevice.CheckOpened (SRC_POS); + bootDevice.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET); + bootDevice.Read (volHeader.Ptr (), TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + } + + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/svh_bak", volHeader.Ptr (), TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + + // Original system loader + res = ZR_WRITE; + try + { + DWORD fileSize = 0; + File sysBakFile (GetSystemLoaderBackupPath(), true); + sysBakFile.CheckOpened (SRC_POS); + sysBakFile.GetFileSize(fileSize); + Buffer fileBuf ((DWORD) fileSize); + DWORD sizeLoader = sysBakFile.Read (fileBuf.Ptr (), fileSize); + res = ZipAdd (hz, L"EFI/Boot/original_bootx64.vc_backup", fileBuf.Ptr (), sizeLoader, ZIP_MEMORY); + } + catch (Exception &e) + { + e.Show (ParentWindow); + Warning ("SYS_LOADER_UNAVAILABLE_FOR_RESCUE_DISK", ParentWindow); + } + + if (res != ZR_OK) + throw ParameterIncorrect (SRC_POS); + + EfiBootConf conf; + wstring dcsPropFileName = GetTempPathString() + L"_dcsproprescue"; + finally_do_arg (wstring, dcsPropFileName, { DeleteFileW (finally_arg.c_str()); }); + if (conf.Save(dcsPropFileName.c_str(), ParentWindow)) + { + DWORD fileSize = 0; + File propFile (dcsPropFileName, true, false); + propFile.CheckOpened (SRC_POS); + propFile.GetFileSize(fileSize); + Buffer propBuf (fileSize); + DWORD sizeDcsProp = propFile.Read (propBuf.Ptr (), fileSize); + + if (ZR_OK != ZipAdd (hz, L"EFI/VeraCrypt/DcsProp", propBuf.Ptr (), sizeDcsProp, ZIP_MEMORY)) + throw ParameterIncorrect (SRC_POS); + } + else + throw ParameterIncorrect (SRC_POS); - image[TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_SECTOR_CONFIG_OFFSET] |= TC_BOOT_CFG_FLAG_RESCUE_DISK_ORIG_SYS_LOADER; + void* pZipContent = NULL; + unsigned long ulZipSize = 0; + if (ZR_OK != ZipGetMemory (hz, &pZipContent, &ulZipSize)) + throw ParameterIncorrect (SRC_POS); + + RescueZipData = new byte[ulZipSize]; + if (!RescueZipData) + throw bad_alloc(); + memcpy (RescueZipData, pZipContent, ulZipSize); + RescueZipSize = ulZipSize; + + if (!isoImagePath.empty()) + { + File isoFile (isoImagePath, false, true); + isoFile.Write (RescueZipData, RescueZipSize); + } } - catch (Exception &e) + else { - e.Show (ParentWindow); - Warning ("SYS_LOADER_UNAVAILABLE_FOR_RESCUE_DISK", ParentWindow); - } + Buffer imageBuf (RescueIsoImageSize); + + byte *image = imageBuf.Ptr(); + memset (image, 0, RescueIsoImageSize); + + // Primary volume descriptor + const char* szPrimVolDesc = "\001CD001\001"; + const char* szPrimVolLabel = "VeraCrypt Rescue Disk "; + memcpy (image + 0x8000, szPrimVolDesc, strlen(szPrimVolDesc) + 1); + memcpy (image + 0x7fff + 41, szPrimVolLabel, strlen(szPrimVolLabel) + 1); + *(uint32 *) (image + 0x7fff + 81) = RescueIsoImageSize / 2048; + *(uint32 *) (image + 0x7fff + 85) = BE32 (RescueIsoImageSize / 2048); + image[0x7fff + 121] = 1; + image[0x7fff + 124] = 1; + image[0x7fff + 125] = 1; + image[0x7fff + 128] = 1; + image[0x7fff + 130] = 8; + image[0x7fff + 131] = 8; + + image[0x7fff + 133] = 10; + image[0x7fff + 140] = 10; + image[0x7fff + 141] = 0x14; + image[0x7fff + 157] = 0x22; + image[0x7fff + 159] = 0x18; + + // Boot record volume descriptor + const char* szBootRecDesc = "CD001\001EL TORITO SPECIFICATION"; + memcpy (image + 0x8801, szBootRecDesc, strlen(szBootRecDesc) + 1); + image[0x8800 + 0x47] = 0x19; + + // Volume descriptor set terminator + const char* szVolDescTerm = "\377CD001\001"; + memcpy (image + 0x9000, szVolDescTerm, strlen(szVolDescTerm) + 1); + + // Path table + image[0xA000 + 0] = 1; + image[0xA000 + 2] = 0x18; + image[0xA000 + 6] = 1; + + // Root directory + image[0xc000 + 0] = 0x22; + image[0xc000 + 2] = 0x18; + image[0xc000 + 9] = 0x18; + image[0xc000 + 11] = 0x08; + image[0xc000 + 16] = 0x08; + image[0xc000 + 25] = 0x02; + image[0xc000 + 28] = 0x01; + image[0xc000 + 31] = 0x01; + image[0xc000 + 32] = 0x01; + image[0xc000 + 34] = 0x22; + image[0xc000 + 36] = 0x18; + image[0xc000 + 43] = 0x18; + image[0xc000 + 45] = 0x08; + image[0xc000 + 50] = 0x08; + image[0xc000 + 59] = 0x02; + image[0xc000 + 62] = 0x01; + *(uint32 *) (image + 0xc000 + 65) = 0x010101; + + // Validation entry + image[0xc800] = 1; + int offset = 0xc800 + 0x1c; + image[offset++] = 0xaa; + image[offset++] = 0x55; + image[offset++] = 0x55; + image[offset] = 0xaa; + + // Initial entry + offset = 0xc820; + image[offset++] = 0x88; + image[offset++] = 2; + image[0xc820 + 6] = 1; + image[0xc820 + 8] = TC_CD_BOOT_LOADER_SECTOR; + + // TrueCrypt Boot Loader + CreateBootLoaderInMemory (image + TC_CD_BOOTSECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE, true); + + // Volume header + if (initialSetup) + { + if (!RescueVolumeHeaderValid) + throw ParameterIncorrect (SRC_POS); + + memcpy (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, RescueVolumeHeader, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + } + else + { + Device bootDevice (GetSystemDriveConfiguration().DevicePath, true); + bootDevice.CheckOpened (SRC_POS); + bootDevice.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET); + bootDevice.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + } - // Boot loader backup - CreateBootLoaderInMemory (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_LOADER_BACKUP_RESCUE_DISK_SECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE, false); + // Original system loader + try + { + File sysBakFile (GetSystemLoaderBackupPath(), true); + sysBakFile.CheckOpened (SRC_POS); + sysBakFile.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE); + + image[TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_SECTOR_CONFIG_OFFSET] |= TC_BOOT_CFG_FLAG_RESCUE_DISK_ORIG_SYS_LOADER; + } + catch (Exception &e) + { + e.Show (ParentWindow); + Warning ("SYS_LOADER_UNAVAILABLE_FOR_RESCUE_DISK", ParentWindow); + } + + // Boot loader backup + CreateBootLoaderInMemory (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_LOADER_BACKUP_RESCUE_DISK_SECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE, false); - RescueIsoImage = new byte[RescueIsoImageSize]; - if (!RescueIsoImage) - throw bad_alloc(); - memcpy (RescueIsoImage, image, RescueIsoImageSize); + RescueIsoImage = new byte[RescueIsoImageSize]; + if (!RescueIsoImage) + throw bad_alloc(); + memcpy (RescueIsoImage, image, RescueIsoImageSize); - if (!isoImagePath.empty()) - { - File isoFile (isoImagePath, false, true); - isoFile.Write (image, RescueIsoImageSize); + if (!isoImagePath.empty()) + { + File isoFile (isoImagePath, false, true); + isoFile.Write (image, RescueIsoImageSize); + } } } #endif @@ -1766,61 +2980,240 @@ namespace VeraCrypt bool BootEncryption::VerifyRescueDisk () { - if (!RescueIsoImage) + BOOL bIsGPT = GetSystemDriveConfiguration().SystemPartition.IsGPT; + if ((bIsGPT && !RescueZipData) || (!bIsGPT && !RescueIsoImage)) throw ParameterIncorrect (SRC_POS); - for (WCHAR drive = L'Z'; drive >= L'C'; --drive) + if (bIsGPT) { - try + const wchar_t* efiFiles[] = { + L"EFI/Boot/bootx64.efi", + L"EFI/VeraCrypt/DcsBml.dcs", + L"EFI/VeraCrypt/DcsBoot.efi", + L"EFI/VeraCrypt/DcsCfg.dcs", + L"EFI/VeraCrypt/DcsInt.dcs", + L"EFI/VeraCrypt/LegacySpeaker.dcs", + L"EFI/VeraCrypt/svh_bak", + L"EFI/Boot/original_bootx64.vc_backup" + }; + + ZRESULT res; + HZIP hz = OpenZip(RescueZipData, RescueZipSize, ZIP_MEMORY); + if (!hz) + throw ParameterIncorrect (SRC_POS); + finally_do_arg (HZIP, hz, { CloseZip (finally_arg); }); + + for (WCHAR drive = L'Z'; drive >= L'C'; --drive) + { + try + { + WCHAR rootPath[4] = { drive, L':', L'\\', 0}; + UINT driveType = GetDriveType (rootPath); + if (DRIVE_REMOVABLE == driveType) + { + // check if it is FAT/FAT32 + WCHAR szNameBuffer[TC_MAX_PATH]; + if (GetVolumeInformationW (rootPath, NULL, 0, NULL, NULL, NULL, szNameBuffer, ARRAYSIZE(szNameBuffer)) + && !wcsncmp (szNameBuffer, L"FAT", 3)) + { + int index, i; + ZIPENTRYW ze; + for (i = 0; i < ARRAYSIZE(efiFiles); i++) + { + bool bMatch = false; + res = FindZipItemW (hz, efiFiles[i], true, &index, &ze); + if ((res == ZR_OK) && (index >= 0)) + { + // check that the file exists on the disk and that it has the same content + StringCbCopyW (szNameBuffer, sizeof (szNameBuffer), rootPath); + StringCbCatW (szNameBuffer, sizeof (szNameBuffer), efiFiles[i]); + + try + { + DWORD dwSize = 0; + File diskFile (szNameBuffer, true); + diskFile.CheckOpened (SRC_POS); + diskFile.GetFileSize (dwSize); + if (dwSize == (DWORD) ze.unc_size) + { + Buffer fileBuf (dwSize); + if (dwSize == diskFile.Read (fileBuf.Ptr (), dwSize)) + { + Buffer efiBuf (dwSize); + res = UnzipItem (hz, ze.index, efiBuf.Ptr (), dwSize, ZIP_MEMORY); + if (res == ZR_OK) + { + bMatch = (memcmp (efiBuf.Ptr(), fileBuf.Ptr(), dwSize) == 0); + } + } + } + } + catch (...) + { + } + + } + else + { + // entry not found in our Rescue ZIP image. Skip it. + bMatch = true; + } + + if (!bMatch) + break; + } + + if (i == ARRAYSIZE(efiFiles)) + { + // All entries processed + return true; + } + } + } + } + catch (...) { } + } + } + else + { + size_t verifiedSectorCount = (TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET + TC_BOOT_LOADER_AREA_SIZE) / 2048; + Buffer buffer ((verifiedSectorCount + 1) * 2048); + for (WCHAR drive = L'Z'; drive >= L'C'; --drive) { - WCHAR rootPath[4] = { drive, L':', L'\\', 0}; - UINT driveType = GetDriveType (rootPath); - // check that it is a CD/DVD drive or a removable media in case a bootable - // USB key was created from the rescue disk ISO file - if ((DRIVE_CDROM == driveType) || (DRIVE_REMOVABLE == driveType)) + try { - rootPath[2] = 0; // remove trailing backslash + WCHAR rootPath[4] = { drive, L':', L'\\', 0}; + UINT driveType = GetDriveType (rootPath); + // check that it is a CD/DVD drive or a removable media in case a bootable + // USB key was created from the rescue disk ISO file + if ((DRIVE_CDROM == driveType) || (DRIVE_REMOVABLE == driveType)) + { + rootPath[2] = 0; // remove trailing backslash - Device driveDevice (rootPath, true); - driveDevice.CheckOpened (SRC_POS); - size_t verifiedSectorCount = (TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET + TC_BOOT_LOADER_AREA_SIZE) / 2048; - Buffer buffer ((verifiedSectorCount + 1) * 2048); + Device driveDevice (rootPath, true); + driveDevice.CheckOpened (SRC_POS); - DWORD bytesRead = driveDevice.Read (buffer.Ptr(), (DWORD) buffer.Size()); - if (bytesRead != buffer.Size()) - continue; + DWORD bytesRead = driveDevice.Read (buffer.Ptr(), (DWORD) buffer.Size()); + if (bytesRead != buffer.Size()) + continue; - if (memcmp (buffer.Ptr(), RescueIsoImage, buffer.Size()) == 0) - return true; + if (memcmp (buffer.Ptr(), RescueIsoImage, buffer.Size()) == 0) + return true; + } } + catch (...) { } } - catch (...) { } } return false; } - bool BootEncryption::VerifyRescueDiskIsoImage (const wchar_t* imageFile) + bool BootEncryption::VerifyRescueDiskImage (const wchar_t* imageFile) { - if (!RescueIsoImage) + BOOL bIsGPT = GetSystemDriveConfiguration().SystemPartition.IsGPT; + if ((bIsGPT && !RescueZipData) || (!bIsGPT && !RescueIsoImage)) throw ParameterIncorrect (SRC_POS); - try + if (bIsGPT) { - File isoFile (imageFile, true); - isoFile.CheckOpened (SRC_POS); - size_t verifiedSectorCount = (TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET + TC_BOOT_LOADER_AREA_SIZE) / 2048; - Buffer buffer ((verifiedSectorCount + 1) * 2048); + try + { + DWORD dwSize = 0; + File rescueFile (imageFile, true); + rescueFile.CheckOpened (SRC_POS); + rescueFile.GetFileSize (dwSize); + Buffer rescueData (dwSize); - DWORD bytesRead = isoFile.Read (buffer.Ptr(), (DWORD) buffer.Size()); - if ( (bytesRead == buffer.Size()) - && (memcmp (buffer.Ptr(), RescueIsoImage, buffer.Size()) == 0) - ) + if (dwSize == rescueFile.Read (rescueData.Ptr (), dwSize)) + { + ZRESULT res; + HZIP hzFile = OpenZip(rescueData.Ptr (), dwSize, ZIP_MEMORY); + if (hzFile) + { + finally_do_arg (HZIP, hzFile, { CloseZip (finally_arg); }); + HZIP hzMem = OpenZip(RescueZipData, RescueZipSize, ZIP_MEMORY); + if (hzMem) + { + finally_do_arg (HZIP, hzMem, { CloseZip (finally_arg); }); + const wchar_t* efiFiles[] = { + L"EFI/Boot/bootx64.efi", + L"EFI/VeraCrypt/DcsBml.dcs", + L"EFI/VeraCrypt/DcsBoot.efi", + L"EFI/VeraCrypt/DcsCfg.dcs", + L"EFI/VeraCrypt/DcsInt.dcs", + L"EFI/VeraCrypt/LegacySpeaker.dcs", + L"EFI/VeraCrypt/svh_bak", + L"EFI/Boot/original_bootx64.vc_backup" + }; + + int index, i; + ZIPENTRYW zeFile, zeMem; + for (i = 0; i < ARRAYSIZE(efiFiles); i++) + { + bool bMatch = false; + res = FindZipItemW (hzMem, efiFiles[i], true, &index, &zeMem); + if ((res == ZR_OK) && (index >= 0)) + { + res = FindZipItemW (hzFile, efiFiles[i], true, &index, &zeFile); + if ((res == ZR_OK) && (index >= 0) && (zeMem.unc_size == zeFile.unc_size)) + { + Buffer fileBuf (zeFile.unc_size); + Buffer memBuf (zeFile.unc_size); + + res = UnzipItem (hzMem, zeMem.index, memBuf.Ptr (), zeMem.unc_size, ZIP_MEMORY); + if (res == ZR_OK) + { + res = UnzipItem (hzFile, zeFile.index, fileBuf.Ptr (), zeFile.unc_size, ZIP_MEMORY); + if (res == ZR_OK) + { + bMatch = (memcmp (memBuf.Ptr (), fileBuf.Ptr (), zeMem.unc_size) == 0); + } + } + } + + } + else + { + // entry not found in our internal Rescue ZIP image. Skip it. + bMatch = true; + } + + if (!bMatch) + break; + } + + if (i == ARRAYSIZE(efiFiles)) + { + // All entries processed + return true; + } + + + } + } + } + } + catch (...) { } + } + else + { + try { - return true; + File rescueFile (imageFile, true); + rescueFile.CheckOpened (SRC_POS); + size_t verifiedSectorCount = (TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET + TC_BOOT_LOADER_AREA_SIZE) / 2048; + Buffer buffer ((verifiedSectorCount + 1) * 2048); + + DWORD bytesRead = rescueFile.Read (buffer.Ptr(), (DWORD) buffer.Size()); + if ( (bytesRead == buffer.Size()) + && (memcmp (buffer.Ptr(), RescueIsoImage, buffer.Size()) == 0) + ) + { + return true; + } } + catch (...) { } } - catch (...) { } return false; } @@ -1900,51 +3293,133 @@ namespace VeraCrypt } +#define VC_EFI_BOOTLOADER_NAME L"DcsBoot" + void BootEncryption::BackupSystemLoader () { - Device device (GetSystemDriveConfiguration().DevicePath, true); - device.CheckOpened (SRC_POS); - byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS]; + if (GetSystemDriveConfiguration().SystemPartition.IsGPT) + { + if (!IsAdmin()) { + if (IsUacSupported()) + { + Elevator::BackupEfiSystemLoader (); + return; + } + else + { + Warning ("ADMIN_PRIVILEGES_WARN_DEVICES", ParentWindow); + } + } + unsigned __int64 loaderSize = 0; - device.SeekAt (0); - device.Read (bootLoaderBuf, sizeof (bootLoaderBuf)); + finally_do ({ EfiBootInst.DismountBootPartition(); }); - // Prevent TrueCrypt loader from being backed up - for (size_t i = 0; i < sizeof (bootLoaderBuf) - strlen (TC_APP_NAME); ++i) - { - if (memcmp (bootLoaderBuf + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) + EfiBootInst.MountBootPartition(0); + + EfiBootInst.GetFileSize(L"\\EFI\\Boot\\bootx64.efi", loaderSize); + + std::vector<byte> bootLoaderBuf ((size_t) loaderSize); + + EfiBootInst.ReadFile(L"\\EFI\\Boot\\bootx64.efi", &bootLoaderBuf[0], (DWORD) loaderSize); + + // Prevent VeraCrypt EFI loader from being backed up + for (size_t i = 0; i < (size_t) loaderSize - (wcslen (VC_EFI_BOOTLOADER_NAME) * 2); ++i) { - if (AskWarnNoYes ("TC_BOOT_LOADER_ALREADY_INSTALLED", ParentWindow) == IDNO) - throw UserAbort (SRC_POS); - return; + if (memcmp (&bootLoaderBuf[i], VC_EFI_BOOTLOADER_NAME, wcslen (VC_EFI_BOOTLOADER_NAME) * 2) == 0) + { + if (AskWarnNoYes ("TC_BOOT_LOADER_ALREADY_INSTALLED", ParentWindow) == IDNO) + throw UserAbort (SRC_POS); + return; + } } + + EfiBootInst.CopyFile(L"\\EFI\\Boot\\bootx64.efi", GetSystemLoaderBackupPath().c_str()); + EfiBootInst.CopyFile(L"\\EFI\\Boot\\bootx64.efi", L"\\EFI\\Boot\\original_bootx64.vc_backup"); } + else + { + Device device (GetSystemDriveConfiguration().DevicePath, true); + device.CheckOpened (SRC_POS); + byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS]; + + device.SeekAt (0); + device.Read (bootLoaderBuf, sizeof (bootLoaderBuf)); + + // Prevent TrueCrypt loader from being backed up + for (size_t i = 0; i < sizeof (bootLoaderBuf) - strlen (TC_APP_NAME); ++i) + { + if (memcmp (bootLoaderBuf + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) + { + if (AskWarnNoYes ("TC_BOOT_LOADER_ALREADY_INSTALLED", ParentWindow) == IDNO) + throw UserAbort (SRC_POS); + return; + } + } - File backupFile (GetSystemLoaderBackupPath(), false, true); - backupFile.Write (bootLoaderBuf, sizeof (bootLoaderBuf)); + File backupFile (GetSystemLoaderBackupPath(), false, true); + backupFile.Write (bootLoaderBuf, sizeof (bootLoaderBuf)); + } } void BootEncryption::RestoreSystemLoader () { - byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS]; + SystemDriveConfiguration config = GetSystemDriveConfiguration(); + if (config.SystemPartition.IsGPT) { + if (!IsAdmin()) { + if (IsUacSupported()) + { + Elevator::RestoreEfiSystemLoader (); + return; + } + else + { + Warning ("ADMIN_PRIVILEGES_WARN_DEVICES", ParentWindow); + } + } - File backupFile (GetSystemLoaderBackupPath(), true); - backupFile.CheckOpened(SRC_POS); - if (backupFile.Read (bootLoaderBuf, sizeof (bootLoaderBuf)) != sizeof (bootLoaderBuf)) - throw ParameterIncorrect (SRC_POS); + finally_do ({ EfiBootInst.DismountBootPartition(); }); + + EfiBootInst.MountBootPartition(0); + + EfiBootInst.DeleteStartExec(); + EfiBootInst.RenameFile(L"\\EFI\\Boot\\original_bootx64.vc_backup", L"\\EFI\\Boot\\bootx64.efi", TRUE); + + EfiBootInst.DelFile(L"\\DcsBoot.efi"); + EfiBootInst.DelFile(L"\\DcsInt.efi"); + EfiBootInst.DelFile(L"\\DcsCfg.efi"); + EfiBootInst.DelFile(L"\\LegacySpeaker.efi"); + EfiBootInst.DelFile(L"\\DcsBoot"); + EfiBootInst.DelFile(L"\\DcsProp"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsBoot.efi"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsInt.dcs"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsCfg.dcs"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\LegacySpeaker.dcs"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsBml.dcs"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsBoot"); + EfiBootInst.DelFile(L"\\EFI\\VeraCrypt\\DcsProp"); + } + else + { + byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS]; - Device device (GetSystemDriveConfiguration().DevicePath); - device.CheckOpened (SRC_POS); + File backupFile (GetSystemLoaderBackupPath(), true); + backupFile.CheckOpened(SRC_POS); + if (backupFile.Read (bootLoaderBuf, sizeof (bootLoaderBuf)) != sizeof (bootLoaderBuf)) + throw ParameterIncorrect (SRC_POS); - // Preserve current partition table - byte mbr[TC_SECTOR_SIZE_BIOS]; - device.SeekAt (0); - device.Read (mbr, sizeof (mbr)); - memcpy (bootLoaderBuf + TC_MAX_MBR_BOOT_CODE_SIZE, mbr + TC_MAX_MBR_BOOT_CODE_SIZE, sizeof (mbr) - TC_MAX_MBR_BOOT_CODE_SIZE); + Device device (GetSystemDriveConfiguration().DevicePath); + device.CheckOpened (SRC_POS); - device.SeekAt (0); - device.Write (bootLoaderBuf, sizeof (bootLoaderBuf)); + // Preserve current partition table + byte mbr[TC_SECTOR_SIZE_BIOS]; + device.SeekAt (0); + device.Read (mbr, sizeof (mbr)); + memcpy (bootLoaderBuf + TC_MAX_MBR_BOOT_CODE_SIZE, mbr + TC_MAX_MBR_BOOT_CODE_SIZE, sizeof (mbr) - TC_MAX_MBR_BOOT_CODE_SIZE); + + device.SeekAt (0); + device.Write (bootLoaderBuf, sizeof (bootLoaderBuf)); + } } #endif // SETUP @@ -2183,11 +3658,34 @@ namespace VeraCrypt RegisterSystemFavoritesService (registerService, FALSE); } + void BootEncryption::GetEfiBootDeviceNumber (PSTORAGE_DEVICE_NUMBER pSdn) + { + SystemDriveConfiguration config = GetSystemDriveConfiguration (); + if (config.SystemPartition.IsGPT && pSdn) + { + if (!IsAdmin() && IsUacSupported()) + { + Elevator::GetEfiBootDeviceNumber (pSdn); + } + else + { + finally_do ({ EfiBootInst.DismountBootPartition(); }); + EfiBootInst.MountBootPartition(0); + memcpy (pSdn, EfiBootInst.GetStorageDeviceNumber(), sizeof (STORAGE_DEVICE_NUMBER)); + } + } + else + { + SetLastError (ERROR_INVALID_PARAMETER); + throw SystemException (SRC_POS); + } + } + void BootEncryption::CheckRequirements () { if (nCurrentOS == WIN_2000) throw ErrorException ("SYS_ENCRYPTION_UNSUPPORTED_ON_CURRENT_OS", SRC_POS); - + if (CurrentOSMajor == 6 && CurrentOSMinor == 0 && CurrentOSServicePack < 1) throw ErrorException ("SYS_ENCRYPTION_UNSUPPORTED_ON_VISTA_SP0", SRC_POS); @@ -2196,7 +3694,7 @@ namespace VeraCrypt SystemDriveConfiguration config = GetSystemDriveConfiguration (); - if (config.SystemPartition.IsGPT) + if (config.SystemPartition.IsGPT && !Is64BitOs()) throw ErrorException ("GPT_BOOT_DRIVE_UNSUPPORTED", SRC_POS); if (SystemDriveIsDynamic()) @@ -2211,7 +3709,13 @@ namespace VeraCrypt throw ErrorException ("SYSENC_UNSUPPORTED_SECTOR_SIZE_BIOS", SRC_POS); bool activePartitionFound = false; - if (!config.SystemPartition.IsGPT) + if (config.SystemPartition.IsGPT) + { + STORAGE_DEVICE_NUMBER sdn; + GetEfiBootDeviceNumber (&sdn); + activePartitionFound = (config.DriveNumber == (int) sdn.DeviceNumber); + } + else { // Determine whether there is an Active partition on the system drive foreach (const Partition &partition, config.Partitions) @@ -2272,7 +3776,7 @@ namespace VeraCrypt if (!pagingFilesOk) { - if (AskWarnYesNoString ((wchar_t *) (wstring (GetString ("PAGING_FILE_NOT_ON_SYS_PARTITION")) + if (AskWarnYesNoString ((wchar_t *) (wstring (GetString ("PAGING_FILE_NOT_ON_SYS_PARTITION")) + GetString ("LEAKS_OUTSIDE_SYSPART_UNIVERSAL_EXPLANATION") + L"\n\n\n" + GetString ("RESTRICT_PAGING_FILES_TO_SYS_PARTITION") @@ -2283,7 +3787,7 @@ namespace VeraCrypt AbortProcessSilent(); } - throw ErrorException (wstring (GetString ("PAGING_FILE_NOT_ON_SYS_PARTITION")) + throw ErrorException (wstring (GetString ("PAGING_FILE_NOT_ON_SYS_PARTITION")) + GetString ("LEAKS_OUTSIDE_SYSPART_UNIVERSAL_EXPLANATION"), SRC_POS); } @@ -2291,14 +3795,14 @@ namespace VeraCrypt wchar_t *configPath = GetConfigPath (L"dummy"); if (configPath && towupper (configPath[0]) != windowsDrive) { - throw ErrorException (wstring (GetString ("USER_PROFILE_NOT_ON_SYS_PARTITION")) + throw ErrorException (wstring (GetString ("USER_PROFILE_NOT_ON_SYS_PARTITION")) + GetString ("LEAKS_OUTSIDE_SYSPART_UNIVERSAL_EXPLANATION"), SRC_POS); } // Temporary files if (towupper (GetTempPathString()[0]) != windowsDrive) { - throw ErrorException (wstring (GetString ("TEMP_NOT_ON_SYS_PARTITION")) + throw ErrorException (wstring (GetString ("TEMP_NOT_ON_SYS_PARTITION")) + GetString ("LEAKS_OUTSIDE_SYSPART_UNIVERSAL_EXPLANATION"), SRC_POS); } } @@ -2315,19 +3819,21 @@ namespace VeraCrypt SystemDriveConfiguration config = GetSystemDriveConfiguration (); - if (encStatus.VolumeHeaderPresent) - { - // Verify CRC of header salt - Device device (config.DevicePath, true); - device.CheckOpened (SRC_POS); - byte header[TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE]; + if (!config.SystemPartition.IsGPT) { + if (encStatus.VolumeHeaderPresent) + { + // Verify CRC of header salt + Device device(config.DevicePath, true); + device.CheckOpened(SRC_POS); + byte header[TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE]; - device.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET); - device.Read (header, sizeof (header)); + device.SeekAt(TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET); + device.Read(header, sizeof(header)); - if (encStatus.VolumeHeaderSaltCrc32 != GetCrc32 ((byte *) header, PKCS5_SALT_SIZE)) - throw ParameterIncorrect (SRC_POS); - } + if (encStatus.VolumeHeaderSaltCrc32 != GetCrc32((byte *)header, PKCS5_SALT_SIZE)) + throw ParameterIncorrect(SRC_POS); + } + } try { @@ -2414,7 +3920,7 @@ namespace VeraCrypt device.Read ((byte *) header, sizeof (header)); PCRYPTO_INFO cryptoInfo = NULL; - + int status = ReadVolumeHeader (!encStatus.HiddenSystem, header, oldPassword, old_pkcs5, old_pim, FALSE, &cryptoInfo, NULL); finally_do_arg (PCRYPTO_INFO, cryptoInfo, { if (finally_arg) crypto_close (finally_arg); }); @@ -2447,7 +3953,7 @@ namespace VeraCrypt UserEnrichRandomPool (hwndDlg); WaitCursor(); - /* The header will be re-encrypted wipePassCount times to prevent adversaries from using + /* The header will be re-encrypted wipePassCount times to prevent adversaries from using techniques such as magnetic force microscopy or magnetic force scanning tunnelling microscopy to recover the overwritten header. According to Peter Gutmann, data should be overwritten 22 times (ideally, 35 times) using non-random patterns and pseudorandom data. However, as users might @@ -2530,20 +4036,24 @@ namespace VeraCrypt try { // check if PIM is stored in MBR - byte userConfig; - ReadBootSectorConfig (nullptr, 0, &userConfig); - if (userConfig & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) + byte userConfig = 0; + if ( ReadBootSectorConfig (nullptr, 0, &userConfig) + && (userConfig & TC_BOOT_USER_CFG_FLAG_DISABLE_PIM) + ) + { storedPimUpdateNeeded = true; + } } catch (...) - {} + { + } } try { // force update of bootloader if fingerprint doesn't match or if the stored PIM changed if (storedPimUpdateNeeded || !CheckBootloaderFingerprint (true)) - InstallBootLoader (device, true, false, pim); + InstallBootLoader (device, true, false, pim, cryptoInfo->pkcs5); } catch (...) {} @@ -2561,7 +4071,7 @@ namespace VeraCrypt } - void BootEncryption::Install (bool hiddenSystem) + void BootEncryption::Install (bool hiddenSystem, int hashAlgo) { BootEncryptionStatus encStatus = GetStatus(); if (encStatus.DriveMounted) @@ -2569,7 +4079,7 @@ namespace VeraCrypt try { - InstallBootLoader (false, hiddenSystem); + InstallBootLoader (false, hiddenSystem, -1, hashAlgo); if (!hiddenSystem) InstallVolumeHeader (); @@ -2663,7 +4173,7 @@ namespace VeraCrypt SelectedEncryptionAlgorithmId = ea; SelectedPrfAlgorithmId = pkcs5; CreateVolumeHeader (volumeSize, encryptedAreaStart, &password, ea, mode, pkcs5, pim); - + if (!rescueIsoImagePath.empty()) CreateRescueIsoImage (true, rescueIsoImagePath); } @@ -2705,7 +4215,7 @@ namespace VeraCrypt BootEncryptionSetupRequest request; ZeroMemory (&request, sizeof (request)); - + request.SetupMode = SetupDecryption; request.DiscardUnreadableEncryptedSectors = discardUnreadableEncryptedSectors; @@ -2721,7 +4231,7 @@ namespace VeraCrypt BootEncryptionSetupRequest request; ZeroMemory (&request, sizeof (request)); - + request.SetupMode = SetupEncryption; request.WipeAlgorithm = wipeAlgorithm; request.ZeroUnreadableSectors = zeroUnreadableSectors; diff --git a/src/Common/BootEncryption.h b/src/Common/BootEncryption.h index d75b650c..c8c9e29f 100644 --- a/src/Common/BootEncryption.h +++ b/src/Common/BootEncryption.h @@ -3,7 +3,7 @@ Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0. - Modifications and additions to the original source code (contained in this file) + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -18,6 +18,16 @@ #include "Exception.h" #include "Platform/PlatformBase.h" #include "Volumes.h" +#include <Winternl.h> + +#define SYSPARTITIONINFORMATION 0x62 + +typedef NTSTATUS (WINAPI *NtQuerySystemInformationFn)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); using namespace std; @@ -26,7 +36,7 @@ namespace VeraCrypt class File { public: - File () : Elevated (false), FileOpen (false), FilePointerPosition(0), Handle(INVALID_HANDLE_VALUE), IsDevice(false), LastError(0) { } + File () : Elevated (false), FileOpen (false), ReadOnly (false), FilePointerPosition(0), Handle(INVALID_HANDLE_VALUE), IsDevice(false), LastError(0) { } File (wstring path,bool readOnly = false, bool create = false); virtual ~File () { Close(); } @@ -35,10 +45,14 @@ namespace VeraCrypt DWORD Read (byte *buffer, DWORD size); void Write (byte *buffer, DWORD size); void SeekAt (int64 position); + void GetFileSize (unsigned __int64& size); + void GetFileSize (DWORD& dwSize); + bool IoCtl(DWORD code, void* inBuf, DWORD inBufSize, void* outBuf, DWORD outBufSize); protected: bool Elevated; bool FileOpen; + bool ReadOnly; uint64 FilePointerPosition; HANDLE Handle; bool IsDevice; @@ -131,6 +145,67 @@ namespace VeraCrypt bool SystemLoaderPresent; }; + class EfiBootConf + { + public: + + int passwordType; + string passwordMsg; + string passwordPicture; + string hashMsg; + int hashAlgo; + int requestHash; + string pimMsg; + int pim; + int requestPim; + int authorizeVisible; + int authorizeRetry; + + EfiBootConf(); + + static BOOL ReadConfigValue (char* configContent, const char *configKey, char *configValue, int maxValueSize); + static int ReadConfigInteger (char* configContent, const char *configKey, int defaultValue); + static char *ReadConfigString (char* configContent, const char *configKey, char *defaultValue, char *str, int maxLen); + static BOOL WriteConfigString (FILE* configFile, char* configContent, const char *configKey, const char *configValue); + static BOOL WriteConfigInteger (FILE* configFile, char* configContent, const char *configKey, int configValue); + BOOL Load (const wchar_t* fileName); + void Load (char* configContent); + BOOL Save (const wchar_t* fileName, HWND hwnd); + }; + + class EfiBoot { + public: + EfiBoot(); + + void MountBootPartition(WCHAR letter); + void DismountBootPartition(); + bool IsEfiBoot(); + + void DeleteStartExec(uint16 statrtOrderNum = 0xDC5B, wchar_t* type = NULL); + void SetStartExec(wstring description, wstring execPath, uint16 statrtOrderNum = 0xDC5B, wchar_t* type = NULL, uint32 attr = 1); + void SaveFile(wchar_t* name, byte* data, DWORD size); + void GetFileSize(const wchar_t* name, unsigned __int64& size); + void ReadFile(const wchar_t* name, byte* data, DWORD size); + void CopyFile(const wchar_t* name, const wchar_t* targetName); + + BOOL RenameFile(wchar_t* name, wchar_t* nameNew, BOOL bForce); + BOOL DelFile(wchar_t* name); + BOOL MkDir(wchar_t* name, bool& bAlreadyExists); + BOOL ReadConfig (wchar_t* name, EfiBootConf& conf); + BOOL UpdateConfig (wchar_t* name, int pim, int hashAlgo, HWND hwndDlg); + BOOL WriteConfig (wchar_t* name, bool preserveUserConfig, int pim, int hashAlgo, const char* passPromptMsg, HWND hwndDlg); + + PSTORAGE_DEVICE_NUMBER GetStorageDeviceNumber () { return &sdn;} + + protected: + bool m_bMounted; + WCHAR EfiBootPartPath[3]; + STORAGE_DEVICE_NUMBER sdn; + PARTITION_INFORMATION_EX partInfo; + WCHAR tempBuf[1024]; + WCHAR systemPartitionPath[MAX_PATH]; + }; + class BootEncryption { public: @@ -168,9 +243,9 @@ namespace VeraCrypt BootEncryptionStatus GetStatus (); void GetVolumeProperties (VOLUME_PROPERTIES_STRUCT *properties); SystemDriveConfiguration GetSystemDriveConfiguration (); - void Install (bool hiddenSystem); - void InstallBootLoader (Device& device, bool preserveUserConfig = false, bool hiddenOSCreation = false, int pim = -1); - void InstallBootLoader (bool preserveUserConfig = false, bool hiddenOSCreation = false); + void Install (bool hiddenSystem, int hashAlgo); + void InstallBootLoader (Device& device, bool preserveUserConfig = false, bool hiddenOSCreation = false, int pim = -1, int hashAlg = -1); + void InstallBootLoader (bool preserveUserConfig = false, bool hiddenOSCreation = false, int pim = -1, int hashAlg = -1); bool CheckBootloaderFingerprint (bool bSilent = false); void InvalidateCachedSysDriveProperties (); bool IsCDRecorderPresent (); @@ -179,8 +254,9 @@ namespace VeraCrypt void PrepareHiddenOSCreation (int ea, int mode, int pkcs5); void PrepareInstallation (bool systemPartitionOnly, Password &password, int ea, int mode, int pkcs5, int pim, const wstring &rescueIsoImagePath); void ProbeRealSystemDriveSize (); - void ReadBootSectorConfig (byte *config, size_t bufLength, byte *userConfig = nullptr, string *customUserMessage = nullptr, uint16 *bootLoaderVersion = nullptr); + bool ReadBootSectorConfig (byte *config, size_t bufLength, byte *userConfig = nullptr, string *customUserMessage = nullptr, uint16 *bootLoaderVersion = nullptr); uint32 ReadDriverConfigurationFlags (); + void ReadEfiConfig (byte* confContent, DWORD maxSize, DWORD* pcbRead); void RegisterBootDriver (bool hiddenSystem); void RegisterFilterDriver (bool registerDriver, FilterType filterType); void RegisterSystemFavoritesService (BOOL registerService); @@ -202,17 +278,20 @@ namespace VeraCrypt bool SystemPartitionCoversWholeDrive (); bool SystemDriveIsDynamic (); bool VerifyRescueDisk (); - bool VerifyRescueDiskIsoImage (const wchar_t* imageFile); + bool VerifyRescueDiskImage (const wchar_t* imageFile); void WipeHiddenOSCreationConfig (); void WriteBootDriveSector (uint64 offset, byte *data); void WriteBootSectorConfig (const byte newConfig[]); - void WriteBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim); + void WriteBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg); + void WriteEfiBootSectorUserConfig (byte userConfig, const string &customUserMessage, int pim, int hashAlg); void WriteLocalMachineRegistryDwordValue (wchar_t *keyPath, wchar_t *valueName, DWORD value); + void GetEfiBootDeviceNumber (PSTORAGE_DEVICE_NUMBER pSdn); + void BackupSystemLoader (); + void RestoreSystemLoader (); protected: static const uint32 RescueIsoImageSize = 1835008; // Size of ISO9660 image with bootable emulated 1.44MB floppy disk image - void BackupSystemLoader (); void CreateBootLoaderInMemory (byte *buffer, size_t bufferSize, bool rescueDisk, bool hiddenOSCreation = false); void CreateVolumeHeader (uint64 volumeSize, uint64 encryptedAreaStart, Password *password, int ea, int mode, int pkcs5, int pim); wstring GetSystemLoaderBackupPath (); @@ -221,8 +300,7 @@ namespace VeraCrypt PartitionList GetDrivePartitions (int driveNumber); wstring GetRemarksOnHiddenOS (); wstring GetWindowsDirectory (); - void RegisterFilter (bool registerFilter, FilterType filterType, const GUID *deviceClassGuid = nullptr); - void RestoreSystemLoader (); + void RegisterFilter (bool registerFilter, FilterType filterType, const GUID *deviceClassGuid = nullptr); void InstallVolumeHeader (); HWND ParentWindow; @@ -231,6 +309,8 @@ namespace VeraCrypt int SelectedPrfAlgorithmId; Partition HiddenOSCandidatePartition; byte *RescueIsoImage; + byte *RescueZipData; + unsigned long RescueZipSize; byte RescueVolumeHeader[TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE]; byte VolumeHeader[TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE]; bool DriveConfigValid; diff --git a/src/Common/Common.rc b/src/Common/Common.rc index b84fba8e..c3dd808e 100644 --- a/src/Common/Common.rc +++ b/src/Common/Common.rc @@ -138,25 +138,30 @@ BEGIN CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,1,158,208,1,WS_EX_STATICEDGE END -IDD_BENCHMARK_DLG DIALOGEX 0, 0, 330, 223 +IDD_BENCHMARK_DLG DIALOGEX 0, 0, 330, 247 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "VeraCrypt - Encryption Algorithm Benchmark" +CAPTION "VeraCrypt - Algorithms Benchmark" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - COMBOBOX IDC_BENCHMARK_BUFFER_SIZE,55,7,77,129,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_BENCHMARK_SORT_METHOD,207,7,116,74,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,37,249,160 - DEFPUSHBUTTON "Benchmark",IDC_PERFORM_BENCHMARK,265,37,58,14 - PUSHBUTTON "Close",IDCLOSE,265,55,58,14 - LTEXT "Hardware-accelerated AES:",IDC_HW_AES_LABEL_LINK,148,210,108,9,SS_NOTIFY,WS_EX_RIGHT - CONTROL "",IDC_HW_AES,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,262,209,57,11,WS_EX_STATICEDGE - LTEXT "Parallelization:",IDC_PARALLELIZATION_LABEL_LINK,4,210,67,9,SS_NOTIFY,WS_EX_RIGHT - CONTROL "",IDC_PARALLELIZATION,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,77,209,57,11,WS_EX_STATICEDGE - CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,2,29,328,1,WS_EX_STATICEDGE - LTEXT "Buffer Size:",IDT_BUFFER_SIZE,0,9,53,8,0,WS_EX_RIGHT - LTEXT "Sort Method:",IDT_SORT_METHOD,135,9,70,8,0,WS_EX_RIGHT - LTEXT "Speed is affected by CPU load and storage device characteristics.\n\nThese tests take place in RAM.",IDT_BOX_BENCHMARK_INFO,266,81,57,116 - CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,2,205,328,1,WS_EX_STATICEDGE + COMBOBOX IDC_BENCHMARK_LIST,55,11,87,53,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_BENCHMARK_BUFFER_SIZE,209,11,87,129,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_PIM,209,11,42,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE + CONTROL "Pre-Boot",IDC_BENCHMARK_PREBOOT,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,258,14,71,10 + COMBOBOX IDC_BENCHMARK_SORT_METHOD,55,40,139,74,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Benchmark",IDC_PERFORM_BENCHMARK,265,60,58,14 + PUSHBUTTON "Close",IDCLOSE,265,79,58,14 + CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,60,249,160 + LTEXT "Hardware-accelerated AES:",IDC_HW_AES_LABEL_LINK,148,233,108,9,SS_NOTIFY,WS_EX_RIGHT + CONTROL "",IDC_HW_AES,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,262,232,57,11,WS_EX_STATICEDGE + LTEXT "Parallelization:",IDC_PARALLELIZATION_LABEL_LINK,4,233,67,9,SS_NOTIFY,WS_EX_RIGHT + CONTROL "",IDC_PARALLELIZATION,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,77,232,57,11,WS_EX_STATICEDGE + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,2,32,328,1,WS_EX_STATICEDGE + LTEXT "Buffer Size:",IDT_BUFFER_SIZE,154,14,53,8,0,WS_EX_RIGHT + LTEXT "Sort Method:",IDT_SORT_METHOD,0,42,53,8,0,WS_EX_RIGHT + LTEXT "Speed is affected by CPU load and storage device characteristics.\n\nThese tests take place in RAM.",IDT_BOX_BENCHMARK_INFO,266,107,57,100 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,2,228,328,1,WS_EX_STATICEDGE + LTEXT "Benchmark: ",IDT_BENCHMARK,0,14,53,8,0,WS_EX_RIGHT + RTEXT "Volume PIM:",IDT_PIM,154,14,53,8,NOT WS_VISIBLE END IDD_CIPHER_TEST_DLG DIALOGEX 0, 0, 326, 249 @@ -384,7 +389,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 323 TOPMARGIN, 7 - BOTTOMMARGIN, 216 + BOTTOMMARGIN, 240 END IDD_CIPHER_TEST_DLG, DIALOG @@ -486,39 +491,53 @@ IDR_BOOT_SECTOR BIN "..\\Boot\\Windows\\Release\\Boo IDR_BOOT_SECTOR_AES BIN "..\\Boot\\Windows\\Release_AES\\BootSector.bin" IDR_BOOT_SECTOR_SERPENT BIN "..\\Boot\\Windows\\Release_Serpent\\BootSector.bin" IDR_BOOT_SECTOR_TWOFISH BIN "..\\Boot\\Windows\\Release_Twofish\\BootSector.bin" +IDR_BOOT_SECTOR_CAMELLIA BIN "..\\Boot\\Windows\\Release_Camellia\\BootSector.bin" IDR_BOOT_LOADER_DECOMPRESSOR BIN "..\\Boot\\Windows\\Release\\Decompressor.com" IDR_BOOT_LOADER BIN "..\\Boot\\Windows\\Release\\BootLoader.com.gz" IDR_BOOT_LOADER_AES BIN "..\\Boot\\Windows\\Release_AES\\BootLoader.com.gz" IDR_BOOT_LOADER_SERPENT BIN "..\\Boot\\Windows\\Release_Serpent\\BootLoader.com.gz" IDR_BOOT_LOADER_TWOFISH BIN "..\\Boot\\Windows\\Release_Twofish\\BootLoader.com.gz" +IDR_BOOT_LOADER_CAMELLIA BIN "..\\Boot\\Windows\\Release_Camellia\\BootLoader.com.gz" IDR_BOOT_SECTOR_SHA2 BIN "..\\Boot\\Windows\\Release_SHA2\\BootSector.bin" IDR_BOOT_SECTOR_AES_SHA2 BIN "..\\Boot\\Windows\\Release_AES_SHA2\\BootSector.bin" IDR_BOOT_SECTOR_SERPENT_SHA2 BIN "..\\Boot\\Windows\\Release_Serpent_SHA2\\BootSector.bin" IDR_BOOT_SECTOR_TWOFISH_SHA2 BIN "..\\Boot\\Windows\\Release_Twofish_SHA2\\BootSector.bin" +IDR_BOOT_SECTOR_CAMELLIA_SHA2 BIN "..\\Boot\\Windows\\Release_Camellia_SHA2\\BootSector.bin" IDR_BOOT_LOADER_SHA2 BIN "..\\Boot\\Windows\\Release_SHA2\\BootLoader.com.gz" IDR_BOOT_LOADER_AES_SHA2 BIN "..\\Boot\\Windows\\Release_AES_SHA2\\BootLoader.com.gz" IDR_BOOT_LOADER_SERPENT_SHA2 BIN "..\\Boot\\Windows\\Release_Serpent_SHA2\\BootLoader.com.gz" IDR_BOOT_LOADER_TWOFISH_SHA2 BIN "..\\Boot\\Windows\\Release_Twofish_SHA2\\BootLoader.com.gz" +IDR_BOOT_LOADER_CAMELLIA_SHA2 BIN "..\\Boot\\Windows\\Release_Camellia_SHA2\\BootLoader.com.gz" IDR_RESCUE_BOOT_SECTOR BIN "..\\Boot\\Windows\\Rescue\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_AES BIN "..\\Boot\\Windows\\Rescue_AES\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_SERPENT BIN "..\\Boot\\Windows\\Rescue_Serpent\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_TWOFISH BIN "..\\Boot\\Windows\\Rescue_Twofish\\BootSector.bin" +IDR_RESCUE_BOOT_SECTOR_CAMELLIA BIN "..\\Boot\\Windows\\Rescue_Camellia\\BootSector.bin" IDR_RESCUE_LOADER BIN "..\\Boot\\Windows\\Rescue\\BootLoader.com.gz" IDR_RESCUE_LOADER_AES BIN "..\\Boot\\Windows\\Rescue_AES\\BootLoader.com.gz" IDR_RESCUE_LOADER_SERPENT BIN "..\\Boot\\Windows\\Rescue_Serpent\\BootLoader.com.gz" IDR_RESCUE_LOADER_TWOFISH BIN "..\\Boot\\Windows\\Rescue_Twofish\\BootLoader.com.gz" +IDR_RESCUE_LOADER_CAMELLIA BIN "..\\Boot\\Windows\\Rescue_Camellia\\BootLoader.com.gz" IDR_RESCUE_BOOT_SECTOR_SHA2 BIN "..\\Boot\\Windows\\Rescue_SHA2\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_AES_SHA2 BIN "..\\Boot\\Windows\\Rescue_AES_SHA2\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_SERPENT_SHA2 BIN "..\\Boot\\Windows\\Rescue_Serpent_SHA2\\BootSector.bin" IDR_RESCUE_BOOT_SECTOR_TWOFISH_SHA2 BIN "..\\Boot\\Windows\\Rescue_Twofish_SHA2\\BootSector.bin" +IDR_RESCUE_BOOT_SECTOR_CAMELLIA_SHA2 BIN "..\\Boot\\Windows\\Rescue_Camellia_SHA2\\BootSector.bin" IDR_RESCUE_LOADER_SHA2 BIN "..\\Boot\\Windows\\Rescue_SHA2\\BootLoader.com.gz" IDR_RESCUE_LOADER_AES_SHA2 BIN "..\\Boot\\Windows\\Rescue_AES_SHA2\\BootLoader.com.gz" IDR_RESCUE_LOADER_SERPENT_SHA2 BIN "..\\Boot\\Windows\\Rescue_Serpent_SHA2\\BootLoader.com.gz" IDR_RESCUE_LOADER_TWOFISH_SHA2 BIN "..\\Boot\\Windows\\Rescue_Twofish_SHA2\\BootLoader.com.gz" +IDR_RESCUE_LOADER_CAMELLIA_SHA2 BIN "..\\Boot\\Windows\\Rescue_Camellia_SHA2\\BootLoader.com.gz" +IDR_EFI_DCSBOOT BIN "..\\Boot\\EFI\\DcsBoot.efi" +IDR_EFI_DCSINT BIN "..\\Boot\\EFI\\DcsInt.efi" +IDR_EFI_DCSCFG BIN "..\\Boot\\EFI\\DcsCfg.efi" +IDR_EFI_LEGACYSPEAKER BIN "..\\Boot\\EFI\\LegacySpeaker.efi" +IDR_EFI_DCSBML BIN "..\\Boot\\EFI\\DcsBml.efi" +IDR_EFI_DCSRE BIN "..\\Boot\\EFI\\DcsRe.efi" ///////////////////////////////////////////////////////////////////////////// // @@ -547,18 +566,18 @@ IDR_LICENSE TEXT "..\\Resources\\Texts\\License.r // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" diff --git a/src/Common/Crypto.c b/src/Common/Crypto.c index e5a3218e..f3045d0c 100644 --- a/src/Common/Crypto.c +++ b/src/Common/Crypto.c @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -16,12 +16,16 @@ #include "Xts.h" #include "Crc.h" #include "Common/Endian.h" +#if !defined(_UEFI) #include <string.h> #ifndef TC_WINDOWS_BOOT #include "EncryptionThreadPool.h" #endif +#endif #include "Volumes.h" +#include "cpu.h" +#pragma warning (disable:4706) // assignment within conditional expression /* Update the following when adding a new cipher or EA: Crypto.h: @@ -52,6 +56,11 @@ static Cipher Ciphers[] = { AES, L"AES", 16, 32, AES_KS }, { SERPENT, L"Serpent", 16, 32, 140*4 }, { TWOFISH, L"Twofish", 16, 32, TWOFISH_KS }, + { CAMELLIA, L"Camellia", 16, 32, CAMELLIA_KS }, +#if defined(CIPHER_GOST89) + { GOST89, L"GOST89", 16, 32, GOST_KS }, +#endif // defined(CIPHER_GOST89) + { KUZNYECHIK, L"Kuznyechik",16, 32, KUZNYECHIK_KS }, #endif { 0, 0, 0, 0, 0 } }; @@ -64,16 +73,21 @@ static EncryptionAlgorithm EncryptionAlgorithms[] = #ifndef TC_WINDOWS_BOOT - { { 0, 0 }, { 0, 0}, 0 }, // Must be all-zero - { { AES, 0 }, { XTS, 0 }, 1 }, - { { SERPENT, 0 }, { XTS, 0 }, 1 }, - { { TWOFISH, 0 }, { XTS, 0 }, 1 }, - { { TWOFISH, AES, 0 }, { XTS, 0 }, 1 }, - { { SERPENT, TWOFISH, AES, 0 }, { XTS, 0 }, 1 }, - { { AES, SERPENT, 0 }, { XTS, 0 }, 1 }, - { { AES, TWOFISH, SERPENT, 0 }, { XTS, 0 }, 1 }, - { { SERPENT, TWOFISH, 0 }, { XTS, 0 }, 1 }, - { { 0, 0 }, { 0, 0}, 0 } // Must be all-zero + { { 0, 0 }, { 0, 0}, 0, 0 }, // Must be all-zero + { { AES, 0 }, { XTS, 0 }, 1, 1 }, + { { SERPENT, 0 }, { XTS, 0 }, 1, 1 }, + { { TWOFISH, 0 }, { XTS, 0 }, 1, 1 }, + { { CAMELLIA, 0 }, { XTS, 0 }, 1, 1 }, +#if defined(CIPHER_GOST89) + { { GOST89, 0 }, { XTS, 0 }, 0, 1 }, +#endif // defined(CIPHER_GOST89) + { { KUZNYECHIK, 0 }, { XTS, 0 }, 0, 1 }, + { { TWOFISH, AES, 0 }, { XTS, 0 }, 1, 1 }, + { { SERPENT, TWOFISH, AES, 0 }, { XTS, 0 }, 1, 1 }, + { { AES, SERPENT, 0 }, { XTS, 0 }, 1, 1 }, + { { AES, TWOFISH, SERPENT, 0 }, { XTS, 0 }, 1, 1 }, + { { SERPENT, TWOFISH, 0 }, { XTS, 0 }, 1, 1 }, + { { 0, 0 }, { 0, 0}, 0, 0 } // Must be all-zero #else // TC_WINDOWS_BOOT @@ -97,11 +111,12 @@ static EncryptionAlgorithm EncryptionAlgorithms[] = #ifndef TC_WINDOWS_BOOT // Hash algorithms static Hash Hashes[] = -{ // ID Name Deprecated System Encryption - { SHA512, L"SHA-512", FALSE, FALSE }, - { WHIRLPOOL, L"Whirlpool", FALSE, FALSE }, - { SHA256, L"SHA-256", FALSE, TRUE }, - { RIPEMD160, L"RIPEMD-160", TRUE, TRUE }, +{ // ID Name Deprecated System Encryption + { SHA512, L"SHA-512", FALSE, FALSE }, + { WHIRLPOOL, L"Whirlpool", FALSE, FALSE }, + { SHA256, L"SHA-256", FALSE, TRUE }, + { RIPEMD160, L"RIPEMD-160", TRUE, TRUE }, + { STREEBOG, L"Streebog", FALSE, FALSE }, { 0, 0, 0 } }; #endif @@ -129,11 +144,28 @@ int CipherInit (int cipher, unsigned char *key, unsigned __int8 *ks) case SERPENT: serpent_set_key (key, ks); break; - + case TWOFISH: twofish_set_key ((TwofishInstance *)ks, (const u4byte *)key); break; +#if !defined (TC_WINDOWS_BOOT) || defined (TC_WINDOWS_BOOT_CAMELLIA) + case CAMELLIA: + camellia_set_key (key, ks); + break; +#endif + +#if !defined(TC_WINDOWS_BOOT) +#if defined(CIPHER_GOST89) + case GOST89: + gost_set_key(key, (gost_kds*)ks); + break; +#endif // && defined(CIPHER_GOST89) + case KUZNYECHIK: + kuznyechik_set_key(key, (kuznyechik_kds*)ks); + break; +#endif // !defined(TC_WINDOWS_BOOT) + default: // Unknown/wrong cipher ID return ERR_CIPHER_INIT_FAILURE; @@ -146,7 +178,7 @@ void EncipherBlock(int cipher, void *data, void *ks) { switch (cipher) { - case AES: + case AES: // In 32-bit kernel mode, due to KeSaveFloatingPointState() overhead, AES instructions can be used only when processing the whole data unit. #if (defined (_WIN64) || !defined (TC_WINDOWS_DRIVER)) && !defined (TC_WINDOWS_BOOT) if (IsAesHwCpuSupported()) @@ -158,6 +190,15 @@ void EncipherBlock(int cipher, void *data, void *ks) case TWOFISH: twofish_encrypt (ks, data, data); break; case SERPENT: serpent_encrypt (data, data, ks); break; +#if !defined (TC_WINDOWS_BOOT) || defined (TC_WINDOWS_BOOT_CAMELLIA) + case CAMELLIA: camellia_encrypt (data, data, ks); break; +#endif +#if !defined(TC_WINDOWS_BOOT) +#if defined(CIPHER_GOST89) + case GOST89: gost_encrypt(data, data, ks, 1); break; +#endif // defined(CIPHER_GOST89) + case KUZNYECHIK: kuznyechik_encrypt_block(data, data, ks); break; +#endif // !defined(TC_WINDOWS_BOOT) default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID } } @@ -191,6 +232,9 @@ void EncipherBlocks (int cipher, void *dataPtr, void *ks, size_t blockCount) KeRestoreFloatingPointState (&floatingPointState); #endif } + else if (cipher == GOST89) { + gost_encrypt(data, data, ks, (int)blockCount); + } else { size_t blockSize = CipherGetBlockSize (cipher); @@ -210,6 +254,17 @@ void DecipherBlock(int cipher, void *data, void *ks) { case SERPENT: serpent_decrypt (data, data, ks); break; case TWOFISH: twofish_decrypt (ks, data, data); break; +#if !defined (TC_WINDOWS_BOOT) || defined (TC_WINDOWS_BOOT_CAMELLIA) + case CAMELLIA: camellia_decrypt (data, data, ks); break; +#endif +#if !defined(TC_WINDOWS_BOOT) +#if defined(CIPHER_GOST89) + case GOST89: gost_decrypt(data, data, ks, 1); break; +#endif // defined(CIPHER_GOST89) + case KUZNYECHIK: kuznyechik_decrypt_block(data, data, ks); break; +#endif // !defined(TC_WINDOWS_BOOT) + + #ifndef TC_WINDOWS_BOOT case AES: @@ -257,6 +312,9 @@ void DecipherBlocks (int cipher, void *dataPtr, void *ks, size_t blockCount) KeRestoreFloatingPointState (&floatingPointState); #endif } + else if (cipher == GOST89) { + gost_decrypt(data, data, ks, (int)blockCount); + } else { size_t blockSize = CipherGetBlockSize (cipher); @@ -325,7 +383,8 @@ int CipherGetKeyScheduleSize (int cipherId) BOOL CipherSupportsIntraDataUnitParallelization (int cipher) { - return cipher == AES && IsAesHwCpuSupported(); + return cipher == AES && IsAesHwCpuSupported() || + cipher == GOST89; } #endif @@ -399,11 +458,11 @@ BOOL EAInitMode (PCRYPTO_INFO ci) /* Note: XTS mode could potentially be initialized with a weak key causing all blocks in one data unit on the volume to be tweaked with zero tweaks (i.e. 512 bytes of the volume would be encrypted in ECB mode). However, to create a TrueCrypt volume with such a weak key, each human being on Earth would have - to create approximately 11,378,125,361,078,862 (about eleven quadrillion) TrueCrypt volumes (provided + to create approximately 11,378,125,361,078,862 (about eleven quadrillion) TrueCrypt volumes (provided that the size of each of the volumes is 1024 terabytes). */ break; - default: + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } @@ -450,8 +509,12 @@ int EAGetByName (wchar_t *name) do { - EAGetName (n, ea, 1); - if (_wcsicmp (n, name) == 0) + EAGetName(n, ea, 1); +#if defined(_UEFI) + if (wcscmp(n, name) == 0) +#else + if (_wcsicmp(n, name) == 0) +#endif return ea; } while (ea = EAGetNext (ea)); @@ -488,7 +551,7 @@ int EAGetNextMode (int ea, int previousModeId) int c, i = 0; while (c = EncryptionAlgorithms[ea].Modes[i++]) { - if (c == previousModeId) + if (c == previousModeId) return EncryptionAlgorithms[ea].Modes[i]; } @@ -591,7 +654,7 @@ int EAGetNextCipher (int ea, int previousCipherId) int c, i = 0; while (c = EncryptionAlgorithms[ea].Ciphers[i++]) { - if (c == previousCipherId) + if (c == previousCipherId) return EncryptionAlgorithms[ea].Ciphers[i]; } @@ -608,7 +671,7 @@ int EAGetPreviousCipher (int ea, int previousCipherId) while (c = EncryptionAlgorithms[ea].Ciphers[i++]) { - if (c == previousCipherId) + if (c == previousCipherId) return EncryptionAlgorithms[ea].Ciphers[i - 2]; } @@ -621,6 +684,12 @@ int EAIsFormatEnabled (int ea) return EncryptionAlgorithms[ea].FormatEnabled; } +#ifndef TC_WINDOWS_BOOT +int EAIsMbrSysEncEnabled (int ea) +{ + return EncryptionAlgorithms[ea].MbrSysEncEnabled; +} +#endif // Returns TRUE if the mode of operation is supported for the encryption algorithm BOOL EAIsModeSupported (int ea, int testedMode) @@ -720,7 +789,7 @@ PCRYPTO_INFO crypto_open () memset (cryptoInfo, 0, sizeof (CRYPTO_INFO)); -#ifndef DEVICE_DRIVER +#if !defined(DEVICE_DRIVER) && !defined(_UEFI) VirtualLock (cryptoInfo, sizeof (CRYPTO_INFO)); #endif @@ -755,7 +824,7 @@ void crypto_close (PCRYPTO_INFO cryptoInfo) if (cryptoInfo != NULL) { burn (cryptoInfo, sizeof (CRYPTO_INFO)); -#ifndef DEVICE_DRIVER +#if !defined(DEVICE_DRIVER) && !defined(_UEFI) VirtualUnlock (cryptoInfo, sizeof (CRYPTO_INFO)); #endif TCfree (cryptoInfo); @@ -777,7 +846,7 @@ void crypto_close (PCRYPTO_INFO cryptoInfo) // EncryptBuffer // // buf: data to be encrypted; the start of the buffer is assumed to be aligned with the start of a data unit. -// len: number of bytes to encrypt; must be divisible by the block size (for cascaded ciphers, divisible +// len: number of bytes to encrypt; must be divisible by the block size (for cascaded ciphers, divisible // by the largest block size used within the cascade) void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo) { @@ -808,7 +877,7 @@ void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_ } break; - default: + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } @@ -819,7 +888,7 @@ void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_ // unitNo: sequential number of the data unit with which the buffer starts // nbrUnits: number of data units in the buffer void EncryptDataUnits (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, uint32 nbrUnits, PCRYPTO_INFO ci) -#ifndef TC_WINDOWS_BOOT +#if !defined(TC_WINDOWS_BOOT) && !defined(_UEFI) { EncryptionThreadPoolDoWork (EncryptDataUnitsWork, buf, structUnitNo, nbrUnits, ci); } @@ -850,7 +919,7 @@ void EncryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *s } break; - default: + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } @@ -859,7 +928,7 @@ void EncryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *s // DecryptBuffer // // buf: data to be decrypted; the start of the buffer is assumed to be aligned with the start of a data unit. -// len: number of bytes to decrypt; must be divisible by the block size (for cascaded ciphers, divisible +// len: number of bytes to decrypt; must be divisible by the block size (for cascaded ciphers, divisible // by the largest block size used within the cascade) void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo) { @@ -890,7 +959,7 @@ void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_ } break; - default: + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } @@ -900,7 +969,7 @@ void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_ // unitNo: sequential number of the data unit with which the buffer starts // nbrUnits: number of data units in the buffer void DecryptDataUnits (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, uint32 nbrUnits, PCRYPTO_INFO ci) -#ifndef TC_WINDOWS_BOOT +#if !defined(TC_WINDOWS_BOOT) && !defined(_UEFI) { EncryptionThreadPoolDoWork (DecryptDataUnitsWork, buf, structUnitNo, nbrUnits, ci); } @@ -935,7 +1004,7 @@ void DecryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *s } break; - default: + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; } @@ -945,7 +1014,7 @@ void DecryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *s #else // TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE -#if !defined (TC_WINDOWS_BOOT_AES) && !defined (TC_WINDOWS_BOOT_SERPENT) && !defined (TC_WINDOWS_BOOT_TWOFISH) +#if !defined (TC_WINDOWS_BOOT_AES) && !defined (TC_WINDOWS_BOOT_SERPENT) && !defined (TC_WINDOWS_BOOT_TWOFISH) && !defined (TC_WINDOWS_BOOT_CAMELLIA) #error No cipher defined #endif @@ -955,11 +1024,13 @@ void EncipherBlock(int cipher, void *data, void *ks) if (IsAesHwCpuSupported()) aes_hw_cpu_encrypt ((byte *) ks, data); else - aes_encrypt (data, data, ks); + aes_encrypt (data, data, ks); #elif defined (TC_WINDOWS_BOOT_SERPENT) serpent_encrypt (data, data, ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_encrypt (ks, data, data); +#elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_encrypt (data, data, ks); #endif } @@ -969,11 +1040,13 @@ void DecipherBlock(int cipher, void *data, void *ks) if (IsAesHwCpuSupported()) aes_hw_cpu_decrypt ((byte *) ks + sizeof (aes_encrypt_ctx) + 14 * 16, data); else - aes_decrypt (data, data, (aes_decrypt_ctx *) ((byte *) ks + sizeof(aes_encrypt_ctx))); + aes_decrypt (data, data, (aes_decrypt_ctx *) ((byte *) ks + sizeof(aes_encrypt_ctx))); #elif defined (TC_WINDOWS_BOOT_SERPENT) serpent_decrypt (data, data, ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_decrypt (ks, data, data); +#elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_decrypt (data, data, ks); #endif } @@ -1033,7 +1106,11 @@ BOOL IsAesHwCpuSupported () if (!stateValid) { +#ifdef TC_WINDOWS_BOOT_AES state = is_aes_hw_cpu_supported() ? TRUE : FALSE; +#else + state = g_hasAESNI ? TRUE : FALSE; +#endif stateValid = TRUE; } diff --git a/src/Common/Crypto.h b/src/Common/Crypto.h index 5d9fff97..7d99f233 100644 --- a/src/Common/Crypto.h +++ b/src/Common/Crypto.h @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -45,20 +45,21 @@ extern "C" { #define MASTER_KEYDATA_SIZE 256 // The first PRF to try when mounting -#define FIRST_PRF_ID 1 +#define FIRST_PRF_ID 1 -// Hash algorithms (pseudorandom functions). +// Hash algorithms (pseudorandom functions). enum { SHA512 = FIRST_PRF_ID, WHIRLPOOL, SHA256, RIPEMD160, + STREEBOG, HASH_ENUM_END_ID }; // The last PRF to try when mounting and also the number of implemented PRFs -#define LAST_PRF_ID (HASH_ENUM_END_ID - 1) +#define LAST_PRF_ID (HASH_ENUM_END_ID - 1) #define RIPEMD160_BLOCKSIZE 64 #define RIPEMD160_DIGESTSIZE 20 @@ -72,6 +73,9 @@ enum #define WHIRLPOOL_BLOCKSIZE 64 #define WHIRLPOOL_DIGESTSIZE 64 +#define STREEBOG_BLOCKSIZE 64 +#define STREEBOG_DIGESTSIZE 64 + #define MAX_DIGESTSIZE WHIRLPOOL_DIGESTSIZE #define DEFAULT_HASH_ALGORITHM FIRST_PRF_ID @@ -105,8 +109,11 @@ enum { NONE = 0, AES, - SERPENT, - TWOFISH + SERPENT, + TWOFISH, + CAMELLIA, + GOST89, + KUZNYECHIK }; typedef struct @@ -126,6 +133,9 @@ typedef struct { int Ciphers[4]; // Null terminated array of ciphers used by encryption algorithm int Modes[LAST_MODE_OF_OPERATION + 1]; // Null terminated array of modes of operation +#ifndef TC_WINDOWS_BOOT + BOOL MbrSysEncEnabled; +#endif int FormatEnabled; } EncryptionAlgorithm; @@ -155,12 +165,16 @@ typedef struct # define MAX_EXPANDED_KEY SERPENT_KS # elif defined (TC_WINDOWS_BOOT_TWOFISH) # define MAX_EXPANDED_KEY TWOFISH_KS +# elif defined (TC_WINDOWS_BOOT_CAMELLIA) +# define MAX_EXPANDED_KEY CAMELLIA_KS # endif #else - -#define MAX_EXPANDED_KEY (AES_KS + SERPENT_KS + TWOFISH_KS) - +#ifdef TC_WINDOWS_BOOT +#define MAX_EXPANDED_KEY max((AES_KS + SERPENT_KS + TWOFISH_KS), CAMELLIA_KS) +#else +#define MAX_EXPANDED_KEY max(max(max((AES_KS + SERPENT_KS + TWOFISH_KS), GOST_KS), CAMELLIA_KS), KUZNYECHIK_KS) +#endif #endif #ifdef DEBUG @@ -186,6 +200,12 @@ typedef struct #ifndef TC_WINDOWS_BOOT # include "Sha2.h" # include "Whirlpool.h" +# include "Streebog.h" +# include "GostCipher.h" +# include "kuznyechik.h" +# include "Camellia.h" +#else +# include "CamelliaSmall.h" #endif #include "GfMul.h" @@ -201,7 +221,7 @@ typedef struct keyInfo_t int keyLength; /* Length of the key */ uint64 dummy; /* Dummy field to ensure 16-byte alignment of this structure */ __int8 salt[PKCS5_SALT_SIZE]; /* PKCS-5 salt */ - __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* Concatenated master primary and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ + CRYPTOPP_ALIGN_DATA(16) __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* Concatenated master primary and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ CRYPTOPP_ALIGN_DATA(16) __int8 userKey[MAX_PASSWORD]; /* Password (to which keyfiles may have been applied). WITHOUT +1 for the null terminator. */ } KEY_INFO, *PKEY_INFO; @@ -221,12 +241,12 @@ typedef struct CRYPTO_INFO_t #ifndef TC_WINDOWS_BOOT uint16 HeaderVersion; - GfCtx gf_ctx; + GfCtx gf_ctx; - unsigned __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* This holds the volume header area containing concatenated master key(s) and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ - unsigned __int8 k2[MASTER_KEYDATA_SIZE]; /* For XTS, this contains the secondary key (if cascade, multiple concatenated). For LRW (deprecated/legacy), it contains the tweak key. For CBC (deprecated/legacy), it contains the IV seed. */ + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* This holds the volume header area containing concatenated master key(s) and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 k2[MASTER_KEYDATA_SIZE]; /* For XTS, this contains the secondary key (if cascade, multiple concatenated). For LRW (deprecated/legacy), it contains the tweak key. For CBC (deprecated/legacy), it contains the IV seed. */ unsigned __int8 salt[PKCS5_SALT_SIZE]; - int noIterations; + int noIterations; BOOL bTrueCryptMode; int volumePim; @@ -235,7 +255,7 @@ typedef struct CRYPTO_INFO_t BOOL bProtectHiddenVolume; // Indicates whether the volume contains a hidden volume to be protected against overwriting BOOL bHiddenVolProtectionAction; // TRUE if a write operation has been denied by the driver in order to prevent the hidden volume from being overwritten (set to FALSE upon volume mount). - + uint64 volDataAreaOffset; // Absolute position, in bytes, of the first data sector of the volume. uint64 hiddenVolumeSize; // Size of the hidden volume excluding the header (in bytes). Set to 0 for standard volumes. @@ -262,7 +282,7 @@ typedef struct CRYPTO_INFO_t } CRYPTO_INFO, *PCRYPTO_INFO; -#ifdef _WIN32 +#if defined(_WIN32) || defined(_UEFI) #pragma pack (push) #pragma pack(1) @@ -331,6 +351,9 @@ int EAGetLastCipher (int ea); int EAGetNextCipher (int ea, int previousCipherId); int EAGetPreviousCipher (int ea, int previousCipherId); int EAIsFormatEnabled (int ea); +#ifndef TC_WINDOWS_BOOT +int EAIsMbrSysEncEnabled (int ea); +#endif BOOL EAIsModeSupported (int ea, int testedMode); diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 13a439e0..30571260 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -78,6 +78,7 @@ char *LastDialogId; wchar_t szHelpFile[TC_MAX_PATH]; wchar_t szHelpFile2[TC_MAX_PATH]; wchar_t SecurityTokenLibraryPath[TC_MAX_PATH]; +char CmdTokenPin [TC_MAX_PATH] = {0}; HFONT hFixedDigitFont = NULL; HFONT hBoldFont = NULL; @@ -113,9 +114,9 @@ BOOL bMountFavoritesOnLogon = FALSE; BOOL bHistory = FALSE; -// Status of detection of hidden sectors (whole-system-drive encryption). +// Status of detection of hidden sectors (whole-system-drive encryption). // 0 - Unknown/undetermined/completed, 1: Detection is or was in progress (but did not complete e.g. due to system crash). -int HiddenSectorDetectionStatus = 0; +int HiddenSectorDetectionStatus = 0; OSVersionEnum nCurrentOS = WIN_UNKNOWN; int CurrentOSMajor = 0; @@ -128,7 +129,7 @@ BOOL bPortableModeConfirmed = FALSE; // TRUE if it is certain that the instance BOOL bInPlaceEncNonSysPending = FALSE; // TRUE if the non-system in-place encryption config file indicates that one or more partitions are scheduled to be encrypted. This flag is set only when config files are loaded during app startup. -/* Globals used by Mount and Format (separately per instance) */ +/* Globals used by Mount and Format (separately per instance) */ BOOL PimEnable = FALSE; BOOL KeyFilesEnable = FALSE; KeyFile *FirstKeyFile = NULL; @@ -147,7 +148,7 @@ BOOL WaitDialogDisplaying = FALSE; HANDLE hDriver = INVALID_HANDLE_VALUE; /* This mutex is used to prevent multiple instances of the wizard or main app from dealing with system encryption */ -volatile HANDLE hSysEncMutex = NULL; +volatile HANDLE hSysEncMutex = NULL; /* This mutex is used for non-system in-place encryption but only for informative (non-blocking) purposes, such as whether an app should prompt the user whether to resume scheduled process. */ @@ -171,7 +172,7 @@ ATOM hDlgClass, hSplashClass; /* This value may changed only by calling ChangeSystemEncryptionStatus(). Only the wizard can change it (others may still read it though). */ -int SystemEncryptionStatus = SYSENC_STATUS_NONE; +int SystemEncryptionStatus = SYSENC_STATUS_NONE; /* Only the wizard can change this value (others may only read it). */ WipeAlgorithmId nWipeMode = TC_WIPE_NONE; @@ -238,6 +239,23 @@ HMODULE hwinscarddll = NULL; #define FREE_DLL(h) if (h) { FreeLibrary (h); h = NULL;} +#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE +#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE 0x00000001 +#endif + +#ifndef BASE_SEARCH_PATH_PERMANENT +#define BASE_SEARCH_PATH_PERMANENT 0x00008000 +#endif + +#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 +#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#endif + +typedef BOOL (WINAPI *SetDllDirectoryPtr)(LPCWSTR lpPathName); +typedef BOOL (WINAPI *SetSearchPathModePtr)(DWORD Flags); +typedef BOOL (WINAPI *SetDefaultDllDirectoriesPtr)(DWORD DirectoryFlags); + + typedef void (WINAPI *InitCommonControlsPtr)(void); typedef HIMAGELIST (WINAPI *ImageList_CreatePtr)(int cx, int cy, UINT flags, int cInitial, int cGrow); typedef int (WINAPI *ImageList_AddPtr)(HIMAGELIST himl, HBITMAP hbmImage, HBITMAP hbmMask); @@ -254,6 +272,10 @@ typedef HRESULT (STDAPICALLTYPE *SHStrDupWPtr)(LPCWSTR psz, LPWSTR *ppwsz); // ChangeWindowMessageFilter typedef BOOL (WINAPI *ChangeWindowMessageFilterPtr) (UINT, DWORD); +SetDllDirectoryPtr SetDllDirectoryFn = NULL; +SetSearchPathModePtr SetSearchPathModeFn = NULL; +SetDefaultDllDirectoriesPtr SetDefaultDllDirectoriesFn = NULL; + ImageList_CreatePtr ImageList_CreateFn = NULL; ImageList_AddPtr ImageList_AddFn = NULL; @@ -294,13 +316,20 @@ ChangeWindowMessageFilterPtr ChangeWindowMessageFilterFn = NULL; #error PKCS5_BENCHMARKS and HASH_FNC_BENCHMARKS are both TRUE (at least one of them should be FALSE). #endif -enum +enum +{ + BENCHMARK_TYPE_ENCRYPTION = 0, + BENCHMARK_TYPE_PRF, + BENCHMARK_TYPE_HASH +}; + +enum { BENCHMARK_SORT_BY_NAME = 0, BENCHMARK_SORT_BY_SPEED }; -typedef struct +typedef struct { int id; wchar_t name[100]; @@ -315,11 +344,15 @@ int benchmarkBufferSize = BENCHMARK_DEFAULT_BUF_SIZE; int benchmarkLastBufferSize = BENCHMARK_DEFAULT_BUF_SIZE; int benchmarkSortMethod = BENCHMARK_SORT_BY_SPEED; LARGE_INTEGER benchmarkPerformanceFrequency; +int benchmarkType = BENCHMARK_TYPE_ENCRYPTION; +int benchmarkPim = -1; +BOOL benchmarkPreBoot = FALSE; +BOOL benchmarkGPT = FALSE; #endif // #ifndef SETUP -typedef struct +typedef struct { void *strings; BOOL bold; @@ -329,6 +362,8 @@ typedef struct void cleanup () { + burn (&CmdTokenPin, sizeof (CmdTokenPin)); + /* Cleanup the GDI fonts */ if (hFixedFont != NULL) DeleteObject (hFixedFont); @@ -356,7 +391,7 @@ void cleanup () /* Close the device driver handle */ if (hDriver != INVALID_HANDLE_VALUE) { - // Unload driver mode if possible (non-install mode) + // Unload driver mode if possible (non-install mode) if (IsNonInstallMode ()) { // If a dismount was forced in the lifetime of the driver, Windows may later prevent it to be loaded again from @@ -710,7 +745,7 @@ BOOL IsDiskError (DWORD error) DWORD handleWin32Error (HWND hwndDlg, const char* srcPos) { PWSTR lpMsgBuf; - DWORD dwError = GetLastError (); + DWORD dwError = GetLastError (); wchar_t szErrorValue[32]; wchar_t* pszDesc; @@ -833,7 +868,7 @@ int GetTextGfxWidth (HWND hwndDlgItem, const wchar_t *text, HFONT hFont) { SIZE sizes; TEXTMETRIC textMetrics; - HDC hdc = GetDC (hwndDlgItem); + HDC hdc = GetDC (hwndDlgItem); SelectObject(hdc, (HGDIOBJ) hFont); @@ -841,7 +876,7 @@ int GetTextGfxWidth (HWND hwndDlgItem, const wchar_t *text, HFONT hFont) GetTextMetrics(hdc, &textMetrics); // Necessary for non-TrueType raster fonts (tmOverhang) - ReleaseDC (hwndDlgItem, hdc); + ReleaseDC (hwndDlgItem, hdc); return ((int) sizes.cx - (int) textMetrics.tmOverhang); } @@ -850,13 +885,13 @@ int GetTextGfxWidth (HWND hwndDlgItem, const wchar_t *text, HFONT hFont) int GetTextGfxHeight (HWND hwndDlgItem, const wchar_t *text, HFONT hFont) { SIZE sizes; - HDC hdc = GetDC (hwndDlgItem); + HDC hdc = GetDC (hwndDlgItem); SelectObject(hdc, (HGDIOBJ) hFont); GetTextExtentPoint32W (hdc, text, (int) wcslen (text), &sizes); - ReleaseDC (hwndDlgItem, hdc); + ReleaseDC (hwndDlgItem, hdc); return ((int) sizes.cy); } @@ -872,7 +907,7 @@ std::wstring FitPathInGfxWidth (HWND hwnd, HFONT hFont, LONG width, const std::w rect.right = width; rect.bottom = LONG_MAX; - HDC hdc = GetDC (hwnd); + HDC hdc = GetDC (hwnd); SelectObject (hdc, (HGDIOBJ) hFont); wchar_t pathBuf[TC_MAX_PATH]; @@ -881,7 +916,7 @@ std::wstring FitPathInGfxWidth (HWND hwnd, HFONT hFont, LONG width, const std::w if (DrawText (hdc, pathBuf, (int) path.size(), &rect, DT_CALCRECT | DT_MODIFYSTRING | DT_PATH_ELLIPSIS | DT_SINGLELINE) != 0) newPath = pathBuf; - ReleaseDC (hwnd, hdc); + ReleaseDC (hwnd, hdc); return newPath; } @@ -961,12 +996,12 @@ void AccommodateTextField (HWND hwndDlg, UINT ctrlId, BOOL bFirstUpdate, HFONT h width = GetTextGfxWidth (hwndCtrl, text, hFont); height = GetTextGfxHeight (hwndCtrl, text, hFont); - GetClientRect (hwndCtrl, &rec); + GetClientRect (hwndCtrl, &rec); origWidth = rec.right; origHeight = rec.bottom; if (width >= 0 - && (!bFirstUpdate || origWidth > width)) // The original width of the field is the maximum allowed size + && (!bFirstUpdate || origWidth > width)) // The original width of the field is the maximum allowed size { horizSubOffset = origWidth - width; vertSubOffset = origHeight - height; @@ -989,7 +1024,7 @@ void AccommodateTextField (HWND hwndDlg, UINT ctrlId, BOOL bFirstUpdate, HFONT h alignPosDiff = horizSubOffset / 2; else if (windowInfo.dwStyle & SS_RIGHT) alignPosDiff = horizSubOffset; - + // Resize/move if (alignPosDiff > 0) { @@ -1044,7 +1079,7 @@ static LRESULT CALLBACK BootPwdFieldProc (HWND hwnd, UINT message, WPARAM wParam // Protects an input field from having its content updated by a Paste action. Used for pre-boot password -// input fields (only the US keyboard layout is supported in pre-boot environment so we must prevent the +// input fields (only the US keyboard layout is supported in pre-boot environment so we must prevent the // user from pasting a password typed using a non-US keyboard layout). void ToBootPwdField (HWND hwndDlg, UINT ctrlId) { @@ -1071,7 +1106,7 @@ BOOL CALLBACK AuxiliaryDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP if (hDC) { ScreenDPI = GetDeviceCaps (hDC, LOGPIXELSY); - ReleaseDC (hwndDlg, hDC); + ReleaseDC (hwndDlg, hDC); } DPIScaleFactorX = 1; @@ -1080,7 +1115,7 @@ BOOL CALLBACK AuxiliaryDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP if (ScreenDPI != USER_DEFAULT_SCREEN_DPI) { - // Windows skews the GUI aspect ratio if the user has a non-default DPI. Hence, working with + // Windows skews the GUI aspect ratio if the user has a non-default DPI. Hence, working with // actual screen DPI is redundant and leads to incorrect results. What really matters here is // how Windows actually renders our GUI. This is determined by comparing the expected and current // sizes of a hidden calibration text field. @@ -1139,7 +1174,7 @@ BOOL CALLBACK AboutDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam GetClientRect (GetDlgItem (hwndDlg, IDC_ABOUT_LOGO_AREA), &rec); SetWindowPos (GetDlgItem (hwndDlg, IDC_ABOUT_BKG), HWND_TOP, 0, 0, rec.right, rec.bottom, SWP_NOMOVE); - // Resize the logo bitmap if the user has a non-default DPI + // Resize the logo bitmap if the user has a non-default DPI if (ScreenDPI != USER_DEFAULT_SCREEN_DPI) { // Logo (must recreate and keep the original aspect ratio as Windows distorts it) @@ -1455,7 +1490,7 @@ void InitDialog (HWND hwndDlg) void ProcessPaintMessages (HWND hwnd, int maxMessagesToProcess) { MSG paintMsg; - int msgCounter = maxMessagesToProcess; + int msgCounter = maxMessagesToProcess; while (PeekMessageW (&paintMsg, hwnd, 0, 0, PM_REMOVE | PM_QS_PAINT) != 0 && msgCounter-- > 0) { @@ -1483,16 +1518,16 @@ HDC CreateMemBitmap (HINSTANCE hInstance, HWND hwnd, wchar_t *resource) } -/* Renders the specified bitmap at the specified location and stretches it to fit (anti-aliasing is applied). +/* Renders the specified bitmap at the specified location and stretches it to fit (anti-aliasing is applied). If bDirectRender is FALSE and both nWidth and nHeight are zero, the width and height of hwndDest are retrieved and adjusted according to screen DPI (the width and height of the resultant image are adjusted the same way); furthermore, if bKeepAspectRatio is TRUE, the smaller DPI factor of the two (i.e. horiz. or vert.) is used both for horiz. and vert. scaling (note that the overall GUI aspect ratio changes irregularly in -both directions depending on the DPI). If bDirectRender is TRUE, bKeepAspectRatio is ignored. +both directions depending on the DPI). If bDirectRender is TRUE, bKeepAspectRatio is ignored. This function returns a handle to the scaled bitmap. When the bitmap is no longer needed, it should be -deleted by calling DeleteObject() with the handle passed as the parameter. -Known Windows issues: -- For some reason, anti-aliasing is not applied if the source bitmap contains less than 16K pixels. +deleted by calling DeleteObject() with the handle passed as the parameter. +Known Windows issues: +- For some reason, anti-aliasing is not applied if the source bitmap contains less than 16K pixels. - Windows 2000 may produce slightly inaccurate colors even when source, buffer, and target are 24-bit true color. */ HBITMAP RenderBitmap (wchar_t *resource, HWND hwndDest, int x, int y, int nWidth, int nHeight, BOOL bDirectRender, BOOL bKeepAspectRatio) { @@ -1541,11 +1576,11 @@ HBITMAP RenderBitmap (wchar_t *resource, HWND hwndDest, int x, int y, int nWidth GetObject (picture, sizeof (BITMAP), &bitmap); - hdcRescaled = CreateCompatibleDC (hdcSrc); + hdcRescaled = CreateCompatibleDC (hdcSrc); if (hdcRescaled) { - hbmpRescaled = CreateCompatibleBitmap (hdcSrc, nWidth, nHeight); + hbmpRescaled = CreateCompatibleBitmap (hdcSrc, nWidth, nHeight); SelectObject (hdcRescaled, hbmpRescaled); @@ -1561,7 +1596,7 @@ HBITMAP RenderBitmap (wchar_t *resource, HWND hwndDest, int x, int y, int nWidth hdcSrc, 0, 0, - bitmap.bmWidth, + bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); @@ -1670,8 +1705,8 @@ RegisterRedTick (HINSTANCE hInstance) wc.hCursor = NULL; wc.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH); wc.lpszClassName = L"VCREDTICK"; - wc.lpfnWndProc = &RedTick; - + wc.lpfnWndProc = &RedTick; + rc = (ULONG) RegisterClassW (&wc); return rc == 0 ? FALSE : TRUE; @@ -1788,7 +1823,7 @@ void PopulateWipeModeCombo (HWND hComboBox, BOOL bNA, BOOL bInPlaceEncryption, B { if (!bHeaderWipe) { - AddComboPair (hComboBox, GetString ("WIPE_MODE_NONE"), TC_WIPE_NONE); + AddComboPair (hComboBox, GetString ("WIPE_MODE_NONE"), TC_WIPE_NONE); } AddComboPair (hComboBox, GetString ("WIPE_MODE_1_RAND"), TC_WIPE_1_RAND); @@ -2055,7 +2090,7 @@ void ExceptionHandlerThread (void *threadArg) else lpack[0] = 0; - + sprintf (url, TC_APPLINK_SECURE "&dest=err-report%s&os=%s&osver=%d.%d.%d&arch=%s&cpus=%d&app=%s&cksum=%x&dlg=%s&err=%x&addr=%x" , lpack , GetWindowsEdition().c_str() @@ -2122,9 +2157,48 @@ static LRESULT CALLBACK NonInstallUacWndProc (HWND hWnd, UINT message, WPARAM wP return DefWindowProcW (hWnd, message, wParam, lParam); } +BOOL LaunchElevatedProcess (HWND hwndDlg, const wchar_t* szModPath, const wchar_t* args) +{ + wchar_t newCmdLine[4096]; + WNDCLASSEXW wcex; + HWND hWnd; + + memset (&wcex, 0, sizeof (wcex)); + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = (WNDPROC) NonInstallUacWndProc; + wcex.hInstance = hInst; + wcex.lpszClassName = L"VeraCrypt"; + RegisterClassExW (&wcex); + + // A small transparent window is necessary to bring the new instance to foreground + hWnd = CreateWindowExW (WS_EX_TOOLWINDOW | WS_EX_LAYERED, + L"VeraCrypt", L"VeraCrypt", 0, + GetSystemMetrics (SM_CXSCREEN)/2, + GetSystemMetrics (SM_CYSCREEN)/2, + 1, 1, NULL, NULL, hInst, NULL); + + SetLayeredWindowAttributes (hWnd, 0, 0, LWA_ALPHA); + ShowWindow (hWnd, SW_SHOWNORMAL); + + StringCbCopyW (newCmdLine, sizeof(newCmdLine), L"/q UAC "); + StringCbCatW (newCmdLine, sizeof (newCmdLine), args); + + if ((int)ShellExecuteW (hWnd, L"runas", szModPath, newCmdLine, NULL, SW_SHOWNORMAL) <= 32) + { + if (hwndDlg) + handleWin32Error (hwndDlg, SRC_POS); + return FALSE; + } + else + { + Sleep (2000); + return TRUE; + } +} + // Mutex handling to prevent multiple instances of the wizard or main app from dealing with system encryption. -// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). +// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). BOOL CreateSysEncMutex (void) { return TCCreateMutex (&hSysEncMutex, TC_MUTEX_NAME_SYSENC); @@ -2144,7 +2218,7 @@ void CloseSysEncMutex (void) } -// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). +// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). BOOL CreateNonSysInplaceEncMutex (void) { return TCCreateMutex (&hNonSysInplaceEncMutex, TC_MUTEX_NAME_NONSYS_INPLACE_ENC); @@ -2166,14 +2240,14 @@ void CloseNonSysInplaceEncMutex (void) // Returns TRUE if another instance of the wizard is preparing, resuming or performing non-system in-place encryption BOOL NonSysInplaceEncInProgressElsewhere (void) { - return (!InstanceHasNonSysInplaceEncMutex () + return (!InstanceHasNonSysInplaceEncMutex () && MutexExistsOnSystem (TC_MUTEX_NAME_NONSYS_INPLACE_ENC)); } // Mutex handling to prevent multiple instances of the wizard or main app from trying to install // or register the driver or from trying to launch it in portable mode at the same time. -// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). +// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). BOOL CreateDriverSetupMutex (void) { return TCCreateMutex (&hDriverSetupMutex, TC_MUTEX_NAME_DRIVER_SETUP); @@ -2204,7 +2278,7 @@ BOOL IsTrueCryptInstallerRunning (void) } -// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). +// Returns TRUE if the mutex is (or had been) successfully acquired (otherwise FALSE). BOOL TCCreateMutex (volatile HANDLE *hMutex, wchar_t *name) { if (*hMutex != NULL) @@ -2244,7 +2318,7 @@ void TCCloseMutex (volatile HANDLE *hMutex) } -// Returns TRUE if a process running on the system has the specified mutex (otherwise FALSE). +// Returns TRUE if a process running on the system has the specified mutex (otherwise FALSE). BOOL MutexExistsOnSystem (wchar_t *name) { if (name[0] == 0) @@ -2258,7 +2332,7 @@ BOOL MutexExistsOnSystem (wchar_t *name) return FALSE; if (GetLastError () == ERROR_ACCESS_DENIED) // On Vista, this is returned if the owner of the mutex is elevated while we are not - return TRUE; + return TRUE; // The call failed and it is not certain whether the mutex exists or not return FALSE; @@ -2530,10 +2604,24 @@ static void LoadSystemDll (LPCTSTR szModuleName, HMODULE *pHandle, BOOL bIgnoreE void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) { WNDCLASSW wc; - char langId[6]; - InitCommonControlsPtr InitCommonControlsFn = NULL; + char langId[6]; + InitCommonControlsPtr InitCommonControlsFn = NULL; + + /* remove current directory from dll search path */ + SetDllDirectoryFn = (SetDllDirectoryPtr) GetProcAddress (GetModuleHandle(L"kernel32.dll"), "SetDllDirectoryW"); + SetSearchPathModeFn = (SetSearchPathModePtr) GetProcAddress (GetModuleHandle(L"kernel32.dll"), "SetSearchPathMode"); + SetDefaultDllDirectoriesFn = (SetDefaultDllDirectoriesPtr) GetProcAddress (GetModuleHandle(L"kernel32.dll"), "SetDefaultDllDirectories"); - InitOSVersionInfo(); + if (SetDllDirectoryFn) + SetDllDirectoryFn (L""); + if (SetSearchPathModeFn) + SetSearchPathModeFn (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT); + if (SetDefaultDllDirectoriesFn) + SetDefaultDllDirectoriesFn (LOAD_LIBRARY_SEARCH_SYSTEM32); + + InitOSVersionInfo(); + + VirtualLock (&CmdTokenPin, sizeof (CmdTokenPin)); InitializeCriticalSection (&csWNetCalls); @@ -2551,11 +2639,13 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) LoadSystemDll (L"secur32.dll", &hsecur32dll, TRUE, SRC_POS); LoadSystemDll (L"msasn1.dll", &hmsasn1dll, TRUE, SRC_POS); LoadSystemDll (L"Usp10.DLL", &hUsp10Dll, TRUE, SRC_POS); - LoadSystemDll (L"UXTheme.dll", &hUXThemeDll, TRUE, SRC_POS); + if (IsOSAtLeast (WIN_7)) + LoadSystemDll (L"dwmapi.dll", &hdwmapidll, TRUE, SRC_POS); + LoadSystemDll (L"UXTheme.dll", &hUXThemeDll, TRUE, SRC_POS); - LoadSystemDll (L"msls31.dll", &hMsls31, TRUE, SRC_POS); + LoadSystemDll (L"msls31.dll", &hMsls31, TRUE, SRC_POS); LoadSystemDll (L"SETUPAPI.DLL", &hSetupDll, FALSE, SRC_POS); - LoadSystemDll (L"SHLWAPI.DLL", &hShlwapiDll, FALSE, SRC_POS); + LoadSystemDll (L"SHLWAPI.DLL", &hShlwapiDll, FALSE, SRC_POS); LoadSystemDll (L"userenv.dll", &hUserenvDll, TRUE, SRC_POS); LoadSystemDll (L"rsaenh.dll", &hRsaenhDll, TRUE, SRC_POS); @@ -2572,7 +2662,7 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) } if (IsOSAtLeast (WIN_VISTA)) - { + { LoadSystemDll (L"netapi32.dll", &hnetapi32dll, TRUE, SRC_POS); LoadSystemDll (L"authz.dll", &hauthzdll, TRUE, SRC_POS); LoadSystemDll (L"xmllite.dll", &hxmllitedll, TRUE, SRC_POS); @@ -2580,10 +2670,11 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) } if (IsOSAtLeast (WIN_VISTA)) - { - LoadSystemDll (L"spp.dll", &hsppdll, TRUE, SRC_POS); - LoadSystemDll (L"vssapi.dll", &vssapidll, TRUE, SRC_POS); + { + LoadSystemDll (L"atl.dll", &hsppdll, TRUE, SRC_POS); LoadSystemDll (L"vsstrace.dll", &hvsstracedll, TRUE, SRC_POS); + LoadSystemDll (L"vssapi.dll", &vssapidll, TRUE, SRC_POS); + LoadSystemDll (L"spp.dll", &hsppdll, TRUE, SRC_POS); if (IsOSAtLeast (WIN_7)) { @@ -2593,20 +2684,18 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) LoadSystemDll (L"devobj.dll", &hdevobjdll, TRUE, SRC_POS); LoadSystemDll (L"powrprof.dll", &hpowrprofdll, TRUE, SRC_POS); - LoadSystemDll (L"dwmapi.dll", &hdwmapidll, TRUE, SRC_POS); - LoadSystemDll (L"crypt32.dll", &hcrypt32dll, TRUE, SRC_POS); LoadSystemDll (L"bcrypt.dll", &hbcryptdll, TRUE, SRC_POS); - LoadSystemDll (L"bcryptprimitives.dll", &hbcryptprimitivesdll, TRUE, SRC_POS); + LoadSystemDll (L"bcryptprimitives.dll", &hbcryptprimitivesdll, TRUE, SRC_POS); } - } + } #else LoadSystemDll (L"WINSCARD.DLL", &hwinscarddll, TRUE, SRC_POS); #endif LoadSystemDll (L"COMCTL32.DLL", &hComctl32Dll, FALSE, SRC_POS); - + // call InitCommonControls function InitCommonControlsFn = (InitCommonControlsPtr) GetProcAddress (hComctl32Dll, "InitCommonControls"); ImageList_AddFn = (ImageList_AddPtr) GetProcAddress (hComctl32Dll, "ImageList_Add"); @@ -2667,7 +2756,7 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) // Language langId[0] = 0; SetPreferredLangId (ConfigReadString ("Language", "", langId, sizeof (langId))); - + if (langId[0] == 0) { if (IsNonInstallMode ()) @@ -2692,9 +2781,7 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) // A new instance of the application must be created with elevated privileges. if (IsNonInstallMode () && !IsAdmin () && IsUacSupported ()) { - wchar_t modPath[MAX_PATH], newCmdLine[4096]; - WNDCLASSEXW wcex; - HWND hWnd; + wchar_t modPath[MAX_PATH]; if (wcsstr (lpszCommandLine, L"/q UAC ") == lpszCommandLine) { @@ -2702,33 +2789,12 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) exit (1); } - memset (&wcex, 0, sizeof (wcex)); - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpfnWndProc = (WNDPROC) NonInstallUacWndProc; - wcex.hInstance = hInstance; - wcex.lpszClassName = L"VeraCrypt"; - RegisterClassExW (&wcex); - - // A small transparent window is necessary to bring the new instance to foreground - hWnd = CreateWindowExW (WS_EX_TOOLWINDOW | WS_EX_LAYERED, - L"VeraCrypt", L"VeraCrypt", 0, - GetSystemMetrics (SM_CXSCREEN)/2, - GetSystemMetrics (SM_CYSCREEN)/2, - 1, 1, NULL, NULL, hInstance, NULL); - - SetLayeredWindowAttributes (hWnd, 0, 0, LWA_ALPHA); - ShowWindow (hWnd, SW_SHOWNORMAL); - GetModuleFileNameW (NULL, modPath, ARRAYSIZE (modPath)); - StringCbCopyW (newCmdLine, sizeof(newCmdLine), L"/q UAC "); - StringCbCatW (newCmdLine, sizeof (newCmdLine), lpszCommandLine); - - if ((int)ShellExecuteW (hWnd, L"runas", modPath, newCmdLine, NULL, SW_SHOWNORMAL) <= 32) + if (LaunchElevatedProcess (NULL, modPath, lpszCommandLine)) + exit (0); + else exit (1); - - Sleep (2000); - exit (0); } #endif @@ -2781,7 +2847,7 @@ void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine) break; } } - + /* Get the attributes for the standard dialog class */ if ((GetClassInfoW (hInst, WINDOWS_DIALOG_CLASS, &wc)) == 0) { @@ -2972,7 +3038,7 @@ BOOL OpenDevice (const wchar_t *lpszPath, OPEN_TEST_STRUCT *driver, BOOL detectF &dwResult, NULL); // check variable driver - if ( bResult + if ( bResult && ( (driver->bDetectTCBootLoader != TRUE && driver->bDetectTCBootLoader != FALSE) || (driver->TCBootLoaderDetected != TRUE && driver->TCBootLoaderDetected != FALSE) || (driver->DetectFilesystem != TRUE && driver->DetectFilesystem != FALSE) || @@ -2998,7 +3064,7 @@ BOOL OpenDevice (const wchar_t *lpszPath, OPEN_TEST_STRUCT *driver, BOOL detectF else return FALSE; } - + return TRUE; } @@ -3026,10 +3092,10 @@ BOOL GetDriveLabel (int driveNo, wchar_t *label, int labelSize) /* Stores the device path of the system partition in SysPartitionDevicePath and the device path of the system drive in SysDriveDevicePath. -IMPORTANT: As this may take a very long time if called for the first time, it should be called only before performing - a dangerous operation (such as header backup restore or formatting a supposedly non-system device) never - at WM_INITDIALOG or any other GUI events -- instead call IsSystemDevicePath (path, hwndDlg, FALSE) for - very fast preliminary GUI checks; also note that right after the "Select Device" dialog exits with an OK +IMPORTANT: As this may take a very long time if called for the first time, it should be called only before performing + a dangerous operation (such as header backup restore or formatting a supposedly non-system device) never + at WM_INITDIALOG or any other GUI events -- instead call IsSystemDevicePath (path, hwndDlg, FALSE) for + very fast preliminary GUI checks; also note that right after the "Select Device" dialog exits with an OK return code, you can use the global flags bSysPartitionSelected and bSysDriveSelected to see if the user selected the system partition/device. After this function completes successfully, the results are cached for the rest of the session and repeated @@ -3037,13 +3103,13 @@ executions complete very fast. Returns TRUE if successful (otherwise FALSE). */ BOOL GetSysDevicePaths (HWND hwndDlg) { if (!bCachedSysDevicePathsValid - || wcslen (SysPartitionDevicePath) <= 1 + || wcslen (SysPartitionDevicePath) <= 1 || wcslen (SysDriveDevicePath) <= 1) { foreach (const HostDevice &device, GetAvailableHostDevices (false, true)) { if (device.ContainsSystem) - StringCchCopyW (device.IsPartition ? SysPartitionDevicePath : SysDriveDevicePath, TC_MAX_PATH, device.Path.c_str()); + StringCchCopyW (device.IsPartition ? SysPartitionDevicePath : SysDriveDevicePath, TC_MAX_PATH, device.Path.c_str()); } if (IsOSAtLeast (WIN_7)) @@ -3070,24 +3136,24 @@ BOOL GetSysDevicePaths (HWND hwndDlg) bCachedSysDevicePathsValid = 1; } - return (bCachedSysDevicePathsValid - && wcslen (SysPartitionDevicePath) > 1 + return (bCachedSysDevicePathsValid + && wcslen (SysPartitionDevicePath) > 1 && wcslen (SysDriveDevicePath) > 1); } -/* Determines whether the device path is the path of the system partition or of the system drive (or neither). -If bReliableRequired is TRUE, very fast execution is guaranteed, but the results cannot be relied upon. +/* Determines whether the device path is the path of the system partition or of the system drive (or neither). +If bReliableRequired is TRUE, very fast execution is guaranteed, but the results cannot be relied upon. If it's FALSE and the function is called for the first time, execution may take up to one minute but the results are reliable. IMPORTANT: As the execution may take a very long time if called for the first time with bReliableRequired set to TRUE, it should be called with bReliableRequired set to TRUE only before performing a dangerous - operation (such as header backup restore or formatting a supposedly non-system device) never at - WM_INITDIALOG or any other GUI events (use IsSystemDevicePath(path, hwndDlg, FALSE) for fast - preliminary GUI checks; also note that right after the "Select Device" dialog exits with an OK + operation (such as header backup restore or formatting a supposedly non-system device) never at + WM_INITDIALOG or any other GUI events (use IsSystemDevicePath(path, hwndDlg, FALSE) for fast + preliminary GUI checks; also note that right after the "Select Device" dialog exits with an OK return code, you can use the global flags bSysPartitionSelected and bSysDriveSelected to see if the user selected the system partition/device). After this function completes successfully, the results are cached for the rest of the session, bReliableRequired -is ignored (TRUE implied), repeated executions complete very fast, and the results are always reliable. +is ignored (TRUE implied), repeated executions complete very fast, and the results are always reliable. Return codes: 1 - it is the system partition path (e.g. \Device\Harddisk0\Partition1) 2 - it is the system drive path (e.g. \Device\Harddisk0\Partition0) @@ -3122,10 +3188,10 @@ int IsSystemDevicePath (const wchar_t *path, HWND hwndDlg, BOOL bReliableRequire /* Determines whether the path points to a non-system partition on the system drive. IMPORTANT: As this may take a very long time if called for the first time, it should be called - only before performing a dangerous operation, never at WM_INITDIALOG or any other GUI events. + only before performing a dangerous operation, never at WM_INITDIALOG or any other GUI events. Return codes: -0 - it isn't a non-system partition on the system drive -1 - it's a non-system partition on the system drive +0 - it isn't a non-system partition on the system drive +1 - it's a non-system partition on the system drive -1 - the result can't be determined, isn't reliable, or there was an error. */ int IsNonSysPartitionOnSysDrive (const wchar_t *path) { @@ -3165,12 +3231,12 @@ int IsNonSysPartitionOnSysDrive (const wchar_t *path) if (wcsncmp (tmpPath, SysDriveDevicePath, max (wcslen(tmpPath), wcslen(SysDriveDevicePath))) == 0) { - // It is a non-system partition on the system drive + // It is a non-system partition on the system drive return 1; } - else + else { - // The partition is not on the system drive + // The partition is not on the system drive return 0; } } @@ -3415,17 +3481,17 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l LocalizeDialog (hwndDlg, "IDD_RAWDEVICES_DLG"); SendMessage (hList,LVM_SETEXTENDEDLISTVIEWSTYLE,0, - LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_TWOCLICKACTIVATE|LVS_EX_LABELTIP - ); + LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_TWOCLICKACTIVATE|LVS_EX_LABELTIP + ); - memset (&LvCol,0,sizeof(LvCol)); - LvCol.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM|LVCF_FMT; + memset (&LvCol,0,sizeof(LvCol)); + LvCol.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM|LVCF_FMT; LvCol.pszText = GetString ("DEVICE"); LvCol.cx = CompensateXDPI (186); LvCol.fmt = LVCFMT_LEFT; SendMessage (hList,LVM_INSERTCOLUMNW,0,(LPARAM)&LvCol); - LvCol.pszText = GetString ("DRIVE"); + LvCol.pszText = GetString ("DRIVE"); LvCol.cx = CompensateXDPI (38); LvCol.fmt = LVCFMT_LEFT; SendMessage (hList,LVM_INSERTCOLUMNW,1,(LPARAM)&LvCol); @@ -3479,7 +3545,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l // Path if (!device.IsPartition || device.DynamicVolume) { - if (!device.Floppy && (device.Size == 0) + if (!device.Floppy && (device.Size == 0) && (device.IsPartition || device.Partitions.empty() || device.Partitions[0].Size == 0) ) continue; @@ -3487,7 +3553,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l if (line > 1) { ListItemAdd (hList, item.iItem, L""); - item.iItem = line++; + item.iItem = line++; } if (device.Floppy || device.DynamicVolume) @@ -3540,7 +3606,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l } #endif - item.iItem = line++; + item.iItem = line++; } SendMessageW(hList, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(LVSCW_AUTOSIZE_USEHEADER, 0)); @@ -3563,8 +3629,8 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l { BOOL bEnableOkButton = FALSE; LVITEM LvItem; - memset(&LvItem,0,sizeof(LvItem)); - LvItem.mask = LVIF_TEXT | LVIF_PARAM; + memset(&LvItem,0,sizeof(LvItem)); + LvItem.mask = LVIF_TEXT | LVIF_PARAM; LvItem.iItem = ((LPNMLISTVIEW) lParam)->iItem; LvItem.pszText = lpszFileName; LvItem.cchTextMax = TC_MAX_PATH; @@ -3597,7 +3663,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l int selectedItem = ListView_GetSelectionMark (GetDlgItem (hwndDlg, IDC_DEVICELIST)); if (selectedItem == -1 || itemToDeviceMap.find (selectedItem) == itemToDeviceMap.end()) - return 1; // non-device line selected + return 1; // non-device line selected const HostDevice selectedDevice = itemToDeviceMap[selectedItem]; StringCchCopyW (lpszFileName, TC_MAX_PATH, selectedDevice.Path.c_str()); @@ -3710,7 +3776,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l } } - // Disallow format if the device contains partitions, but not if the partition is virtual or system + // Disallow format if the device contains partitions, but not if the partition is virtual or system if (!selectedDevice.IsVirtualPartition && !bHiddenVolDirect) { @@ -3731,7 +3797,7 @@ BOOL CALLBACK RawDevicesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l #endif // #ifdef VOLFORMAT } - else + else bSysDriveSelected = FALSE; #ifdef VOLFORMAT @@ -3908,7 +3974,7 @@ BOOL DriverUnload () if (hDriver == INVALID_HANDLE_VALUE) return TRUE; - + try { if (BootEncryption (NULL).GetStatus().DeviceFilterActive) @@ -4025,7 +4091,7 @@ start: } // Try to open a handle to the driver again (keep the mutex in case the other instance failed) - goto start; + goto start; } else { @@ -4034,7 +4100,7 @@ start: if (SystemEncryptionStatus != SYSENC_STATUS_NONE) { // This is an inconsistent state. The config file indicates system encryption should be - // active, but the driver is not running. This may happen e.g. when the pretest fails and + // active, but the driver is not running. This may happen e.g. when the pretest fails and // the user selects "Last Known Good Configuration" from the Windows boot menu. // To fix this, we're going to reinstall the driver, start it, and register it for boot. @@ -4068,7 +4134,7 @@ load: return res; bPortableModeConfirmed = TRUE; - + if (hDriver != INVALID_HANDLE_VALUE) CloseHandle (hDriver); hDriver = CreateFile (WIN32_ROOT_PREFIX, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); @@ -4242,7 +4308,7 @@ BOOL SelectMultipleFiles (HWND hwndDlg, const char *stringId, wchar_t *lpszFileN | OFN_PATHMUSTEXIST | OFN_ALLOWMULTISELECT | (keepHistory ? 0 : OFN_DONTADDTORECENT); - + if (!keepHistory) CleanLastVisitedMRU (); @@ -4272,7 +4338,7 @@ BOOL SelectMultipleFiles (HWND hwndDlg, const char *stringId, wchar_t *lpszFileN CleanLastVisitedMRU (); status = TRUE; - + ret: SystemFileSelectorCallPending = FALSE; ResetCurrentDirectory(); @@ -4306,10 +4372,10 @@ BOOL SelectMultipleFilesNext (wchar_t *lpszFileName, size_t cbFileName) } -static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData) +static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData) { switch(uMsg) { - case BFFM_INITIALIZED: + case BFFM_INITIALIZED: { /* WParam is TRUE since we are passing a path. It would be FALSE if we were passing a pidl. */ @@ -4317,12 +4383,12 @@ static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pDa break; } - case BFFM_SELCHANGED: + case BFFM_SELCHANGED: { wchar_t szDir[TC_MAX_PATH]; /* Set the status window to the currently selected path. */ - if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir)) + if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir)) { SendMessage (hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)szDir); } @@ -4346,7 +4412,7 @@ BOOL BrowseDirectories (HWND hwndDlg, char *lpszTitle, wchar_t *dirName) CoInitialize (NULL); - if (SUCCEEDED (SHGetMalloc (&pMalloc))) + if (SUCCEEDED (SHGetMalloc (&pMalloc))) { ZeroMemory (&bi, sizeof(bi)); bi.hwndOwner = hwndDlg; @@ -4358,9 +4424,9 @@ BOOL BrowseDirectories (HWND hwndDlg, char *lpszTitle, wchar_t *dirName) bi.lParam = (LPARAM)dirName; pidl = SHBrowseForFolderW (&bi); - if (pidl != NULL) + if (pidl != NULL) { - if (SHGetPathFromIDList(pidl, dirName)) + if (SHGetPathFromIDList(pidl, dirName)) { bOK = TRUE; } @@ -4574,7 +4640,7 @@ static BOOL CALLBACK LocalizeDialogEnum( HWND hwnd, LPARAM font) // Font SendMessageW (hwnd, WM_SETFONT, (WPARAM) font, 0); - + return TRUE; } @@ -4588,7 +4654,7 @@ void LocalizeDialog (HWND hwnd, char *stringId) SetWindowTextW (hwnd, L"VeraCrypt"); else SetWindowTextW (hwnd, GetString (stringId)); - + if (hUserFont != 0) EnumChildWindows (hwnd, LocalizeDialogEnum, (LPARAM) hUserFont); } @@ -4669,10 +4735,10 @@ BOOL UpdateDriveCustomLabel (int driveNo, wchar_t* effectiveLabel, BOOL bSetValu DWORD cbLabelLen = (DWORD) ((wcslen (effectiveLabel) + 1) * sizeof (wchar_t)); BOOL bToBeDeleted = FALSE; - StringCbPrintfW (wszRegPath, sizeof (wszRegPath), L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons\\%s\\DefaultLabel", driveStr); - + StringCbPrintfW (wszRegPath, sizeof (wszRegPath), L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons\\%s\\DefaultLabel", driveStr); + if (bSetValue) - lStatus = RegCreateKeyExW (HKEY_CURRENT_USER, wszRegPath, NULL, NULL, 0, + lStatus = RegCreateKeyExW (HKEY_CURRENT_USER, wszRegPath, NULL, NULL, 0, KEY_READ | KEY_WRITE | KEY_SET_VALUE, NULL, &hKey, NULL); else lStatus = RegOpenKeyExW (HKEY_CURRENT_USER, wszRegPath, 0, KEY_READ | KEY_WRITE | KEY_SET_VALUE, &hKey); @@ -4696,7 +4762,7 @@ BOOL UpdateDriveCustomLabel (int driveNo, wchar_t* effectiveLabel, BOOL bSetValu if (bToBeDeleted) { - StringCbPrintfW (wszRegPath, sizeof (wszRegPath), L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons\\%s", driveStr); + StringCbPrintfW (wszRegPath, sizeof (wszRegPath), L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons\\%s", driveStr); lStatus = RegOpenKeyExW (HKEY_CURRENT_USER, wszRegPath, 0, KEY_READ | KEY_WRITE | KEY_SET_VALUE, &hKey); if (ERROR_SUCCESS == lStatus) { @@ -4722,7 +4788,7 @@ wstring GetUserFriendlyVersionString (int version) versionString.insert (version > 0xfff ? 2 : 1,L"."); if (versionString[versionString.length()-1] == L'0') - versionString.erase (versionString.length()-1, 1); + versionString.erase (versionString.length()-1, 1); return (versionString); } @@ -4774,7 +4840,7 @@ bool HexWideStringToArray (const wchar_t* hexStr, std::vector<byte>& arr) arr.clear(); if (len %2) return false; - + for (i = 0; i < len/2; i++) { if (!HexToByte (*hexStr++, b1) || !HexToByte (*hexStr++, b2)) @@ -4837,7 +4903,7 @@ void GetSpeedString (unsigned __int64 speed, wchar_t *str, size_t cbStr) { static wchar_t *b, *kb, *mb, *gb, *tb, *pb; static int serNo; - + if (b == NULL || serNo != LocalizationSerialNo) { serNo = LocalizationSerialNo; @@ -4871,6 +4937,60 @@ void GetSpeedString (unsigned __int64 speed, wchar_t *str, size_t cbStr) StringCbPrintfW (str, cbStr, L"%I64d %s", speed, b); } +static void ResetBenchmarkList (HWND hwndDlg) +{ + LVCOLUMNW LvCol; + + HWND hList = GetDlgItem (hwndDlg, IDC_RESULTS); + + /* Render the results */ + // delete data + SendMessage (hList, LVM_DELETEALLITEMS, 0, 0); + // Delete headers + SendMessageW (hList, LVM_DELETECOLUMN, 1, 0); + SendMessageW (hList, LVM_DELETECOLUMN, 1, 0); + SendMessageW (hList, LVM_DELETECOLUMN, 1, 0); + + memset (&LvCol,0,sizeof(LvCol)); + LvCol.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM|LVCF_FMT; + switch(benchmarkType) { + case BENCHMARK_TYPE_ENCRYPTION: + // Create headers + LvCol.pszText = GetString ("ENCRYPTION"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,1,(LPARAM)&LvCol); + + LvCol.pszText = GetString ("DECRYPTION"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,2,(LPARAM)&LvCol); + + LvCol.pszText = GetString ("MEAN"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,3,(LPARAM)&LvCol); + break; + case BENCHMARK_TYPE_HASH: + LvCol.pszText = GetString ("MEAN"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,1,(LPARAM)&LvCol); + break; + case BENCHMARK_TYPE_PRF: + LvCol.pszText = GetString ("TIME"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,1,(LPARAM)&LvCol); + + LvCol.pszText = GetString ("ITERATIONS"); + LvCol.cx = CompensateXDPI (80); + LvCol.fmt = LVCFMT_RIGHT; + SendMessageW (hList,LVM_INSERTCOLUMNW,2,(LPARAM)&LvCol); + break; + } +} + static void DisplayBenchmarkResults (HWND hwndDlg) { wchar_t item1[100]={0}; @@ -4880,6 +5000,8 @@ static void DisplayBenchmarkResults (HWND hwndDlg) BOOL unsorted = TRUE; BENCHMARK_REC tmp_line; + ResetBenchmarkList (hwndDlg); + /* Sort the list */ switch (benchmarkSortMethod) @@ -4891,7 +5013,10 @@ static void DisplayBenchmarkResults (HWND hwndDlg) unsorted = FALSE; for (i = 0; i < benchmarkTotalItems - 1; i++) { - if (benchmarkTable[i].meanBytesPerSec < benchmarkTable[i+1].meanBytesPerSec) + + if (((benchmarkType == BENCHMARK_TYPE_PRF) && (benchmarkTable[i].meanBytesPerSec > benchmarkTable[i+1].meanBytesPerSec)) || + ((benchmarkType != BENCHMARK_TYPE_PRF) && (benchmarkTable[i].meanBytesPerSec < benchmarkTable[i+1].meanBytesPerSec)) + ) { unsorted = TRUE; memcpy (&tmp_line, &benchmarkTable[i], sizeof(BENCHMARK_REC)); @@ -4920,11 +5045,7 @@ static void DisplayBenchmarkResults (HWND hwndDlg) } break; } - - /* Render the results */ - - SendMessage (hList,LVM_DELETEALLITEMS,0,(LPARAM)&LvItem); - + for (i = 0; i < benchmarkTotalItems; i++) { ea = benchmarkTable[i].id; @@ -4934,37 +5055,41 @@ static void DisplayBenchmarkResults (HWND hwndDlg) LvItem.iItem = i; LvItem.iSubItem = 0; LvItem.pszText = (LPWSTR) benchmarkTable[i].name; - SendMessageW (hList, LVM_INSERTITEM, 0, (LPARAM)&LvItem); - -#if PKCS5_BENCHMARKS - wcscpy (item1, L"-"); -#else - GetSpeedString ((unsigned __int64) (benchmarkLastBufferSize / ((float) benchmarkTable[i].encSpeed / benchmarkPerformanceFrequency.QuadPart)), item1, sizeof(item1)); -#endif - LvItem.iSubItem = 1; - LvItem.pszText = item1; - - SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); - -#if PKCS5_BENCHMARKS - wcscpy (item1, L"-"); -#else - GetSpeedString ((unsigned __int64) (benchmarkLastBufferSize / ((float) benchmarkTable[i].decSpeed / benchmarkPerformanceFrequency.QuadPart)), item1, sizeof(item1)); -#endif - LvItem.iSubItem = 2; - LvItem.pszText = item1; - - SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); - -#if PKCS5_BENCHMARKS - swprintf (item1, L"%d t", benchmarkTable[i].encSpeed); -#else - GetSpeedString (benchmarkTable[i].meanBytesPerSec, item1, sizeof(item1)); -#endif - LvItem.iSubItem = 3; - LvItem.pszText = item1; - - SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + SendMessageW (hList, LVM_INSERTITEM, 0, (LPARAM)&LvItem); + switch(benchmarkType) { + case BENCHMARK_TYPE_ENCRYPTION: + GetSpeedString ((unsigned __int64) (benchmarkLastBufferSize / ((float) benchmarkTable[i].encSpeed / benchmarkPerformanceFrequency.QuadPart)), item1, sizeof(item1)); + LvItem.iSubItem = 1; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + + GetSpeedString ((unsigned __int64) (benchmarkLastBufferSize / ((float) benchmarkTable[i].decSpeed / benchmarkPerformanceFrequency.QuadPart)), item1, sizeof(item1)); + LvItem.iSubItem = 2; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + + GetSpeedString (benchmarkTable[i].meanBytesPerSec, item1, sizeof(item1)); + LvItem.iSubItem = 3; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + break; + case BENCHMARK_TYPE_HASH: + GetSpeedString (benchmarkTable[i].meanBytesPerSec, item1, sizeof(item1)); + LvItem.iSubItem = 1; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + break; + case BENCHMARK_TYPE_PRF: + swprintf_s (item1, sizeof(item1) / sizeof(item1[0]), L"%d ms", benchmarkTable[i].meanBytesPerSec); + LvItem.iSubItem = 1; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + swprintf_s (item1, sizeof(item1) / sizeof(item1[0]), L"%d", benchmarkTable[i].decSpeed); + LvItem.iSubItem = 2; + LvItem.pszText = item1; + SendMessageW (hList, LVM_SETITEMW, 0, (LPARAM)&LvItem); + break; + } } SendMessageW(hList, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(LVSCW_AUTOSIZE_USEHEADER, 0)); @@ -4978,7 +5103,7 @@ static void DisplayBenchmarkResults (HWND hwndDlg) typedef struct { HWND hBenchDlg; - BOOL bStatus; + BOOL bStatus; } BenchmarkThreadParam; static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg); @@ -4993,17 +5118,21 @@ void CALLBACK BenchmarkThreadProc(void* pArg, HWND hwndDlg) static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) { LARGE_INTEGER performanceCountStart, performanceCountEnd; - BYTE *lpTestBuffer; + BYTE *lpTestBuffer = NULL; PCRYPTO_INFO ci = NULL; UINT64_STRUCT startDataUnitNo; + SYSTEM_INFO sysInfo = {0}; + GetSystemInfo (&sysInfo); startDataUnitNo.Value = 0; -#if !(PKCS5_BENCHMARKS || HASH_FNC_BENCHMARKS) + /* set priority to critical only when there are 2 or more CPUs on the system */ + if (sysInfo.dwNumberOfProcessors > 1) + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + ci = crypto_open (); if (!ci) return FALSE; -#endif if (QueryPerformanceFrequency (&benchmarkPerformanceFrequency) == 0) { @@ -5013,142 +5142,141 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) return FALSE; } - lpTestBuffer = (BYTE *) malloc(benchmarkBufferSize - (benchmarkBufferSize % 16)); - if (lpTestBuffer == NULL) + if (benchmarkType != BENCHMARK_TYPE_PRF) { - if (ci) - crypto_close (ci); - MessageBoxW (hwndDlg, GetString ("ERR_MEM_ALLOC"), lpszTitle, ICON_HAND); - return FALSE; - } - VirtualLock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); - - WaitCursor (); - benchmarkTotalItems = 0; - -#if !(PKCS5_BENCHMARKS || HASH_FNC_BENCHMARKS) - // CPU "warm up" (an attempt to prevent skewed results on systems where CPU frequency - // gradually changes depending on CPU load). - ci->ea = EAGetFirst(); - if (!EAInit (ci->ea, ci->master_keydata, ci->ks)) - { - ci->mode = FIRST_MODE_OF_OPERATION_ID; - if (EAInitMode (ci)) + lpTestBuffer = (BYTE *) _aligned_malloc(benchmarkBufferSize - (benchmarkBufferSize % 16), 16); + if (lpTestBuffer == NULL) { - int i; - - for (i = 0; i < 10; i++) - { - EncryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); - DecryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); - } + if (ci) + crypto_close (ci); + MessageBoxW (hwndDlg, GetString ("ERR_MEM_ALLOC"), lpszTitle, ICON_HAND); + return FALSE; } + VirtualLock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); } -#endif - -#if HASH_FNC_BENCHMARKS - /* Measures the speed at which each of the hash algorithms processes the message to produce - a single digest. - - The hash algorithm benchmarks are included here for development purposes only. Do not enable - them when building a public release (the benchmark GUI strings wouldn't make sense). */ + WaitCursor (); + benchmarkTotalItems = 0; - { - BYTE *digest [MAX_DIGESTSIZE]; - WHIRLPOOL_CTX wctx; - RMD160_CTX rctx; - sha512_ctx s2ctx; - sha256_ctx s256ctx; - int hid; + switch(benchmarkType) { - for (hid = FIRST_PRF_ID; hid <= LAST_PRF_ID; hid++) + case BENCHMARK_TYPE_HASH: + /* Measures the speed at which each of the hash algorithms processes the message to produce + a single digest. + */ { - if (QueryPerformanceCounter (&performanceCountStart) == 0) - goto counter_error; + BYTE *digest [MAX_DIGESTSIZE]; + WHIRLPOOL_CTX wctx; + RMD160_CTX rctx; + sha512_ctx s2ctx; + sha256_ctx s256ctx; + STREEBOG_CTX stctx; - switch (hid) - { + int hid, i; - case SHA512: - sha512_begin (&s2ctx); - sha512_hash (lpTestBuffer, benchmarkBufferSize, &s2ctx); - sha512_end ((unsigned char *) digest, &s2ctx); - break; + for (hid = FIRST_PRF_ID; hid <= LAST_PRF_ID; hid++) + { + if (QueryPerformanceCounter (&performanceCountStart) == 0) + goto counter_error; - case SHA256: - sha256_begin (&s256ctx); - sha256_hash (lpTestBuffer, benchmarkBufferSize, &s256ctx); - sha256_end ((unsigned char *) digest, &s256ctx); - break; + for (i = 1; i <= 2; i++) + { + switch (hid) + { - case RIPEMD160: - RMD160Init(&rctx); - RMD160Update(&rctx, lpTestBuffer, benchmarkBufferSize); - RMD160Final((unsigned char *) digest, &rctx); - break; + case SHA512: + sha512_begin (&s2ctx); + sha512_hash (lpTestBuffer, benchmarkBufferSize, &s2ctx); + sha512_end ((unsigned char *) digest, &s2ctx); + break; + + case SHA256: + sha256_begin (&s256ctx); + sha256_hash (lpTestBuffer, benchmarkBufferSize, &s256ctx); + sha256_end ((unsigned char *) digest, &s256ctx); + break; + + case RIPEMD160: + RMD160Init(&rctx); + RMD160Update(&rctx, lpTestBuffer, benchmarkBufferSize); + RMD160Final((unsigned char *) digest, &rctx); + break; + + case WHIRLPOOL: + WHIRLPOOL_init (&wctx); + WHIRLPOOL_add (lpTestBuffer, benchmarkBufferSize, &wctx); + WHIRLPOOL_finalize (&wctx, (unsigned char *) digest); + break; + + case STREEBOG: + STREEBOG_init(&stctx); + STREEBOG_add(&stctx, lpTestBuffer, benchmarkBufferSize); + STREEBOG_finalize(&stctx, (unsigned char *)digest); + break; - case WHIRLPOOL: - WHIRLPOOL_init (&wctx); - WHIRLPOOL_add (lpTestBuffer, benchmarkBufferSize * 8, &wctx); - WHIRLPOOL_finalize (&wctx, (unsigned char *) digest); - break; - } + } + } - if (QueryPerformanceCounter (&performanceCountEnd) == 0) - goto counter_error; + if (QueryPerformanceCounter (&performanceCountEnd) == 0) + goto counter_error; - benchmarkTable[benchmarkTotalItems].encSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; + benchmarkTable[benchmarkTotalItems].encSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; - benchmarkTable[benchmarkTotalItems].decSpeed = benchmarkTable[benchmarkTotalItems].encSpeed; - benchmarkTable[benchmarkTotalItems].id = hid; - benchmarkTable[benchmarkTotalItems].meanBytesPerSec = ((unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].encSpeed / benchmarkPerformanceFrequency.QuadPart)) + (unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].decSpeed / benchmarkPerformanceFrequency.QuadPart))) / 2; - StringCbPrintfA (benchmarkTable[benchmarkTotalItems].name, sizeof(benchmarkTable[benchmarkTotalItems].name),"%s", HashGetName(hid)); + benchmarkTable[benchmarkTotalItems].decSpeed = benchmarkTable[benchmarkTotalItems].encSpeed; + benchmarkTable[benchmarkTotalItems].id = hid; + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].encSpeed / benchmarkPerformanceFrequency.QuadPart / 2)); + StringCbPrintfW (benchmarkTable[benchmarkTotalItems].name, sizeof(benchmarkTable[benchmarkTotalItems].name),L"%s", HashGetName(hid)); - benchmarkTotalItems++; + benchmarkTotalItems++; + } } - } - -#elif PKCS5_BENCHMARKS // #if HASH_FNC_BENCHMARKS + break; + case BENCHMARK_TYPE_PRF: /* Measures the time that it takes for the PKCS-5 routine to derive a header key using - each of the implemented PRF algorithms. - - The PKCS-5 benchmarks are included here for development purposes only. Do not enable - them when building a public release (the benchmark GUI strings wouldn't make sense). */ + each of the implemented PRF algorithms. + */ { int thid, i; char dk[MASTER_KEYDATA_SIZE]; char *tmp_salt = {"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x01\x23\x45\x67\x89\xAB\xCD\xEF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x01\x23\x45\x67\x89\xAB\xCD\xEF\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"}; - for (thid = FIRST_PRF_ID; thid <= LAST_PRF_ID; thid++) + for (thid = FIRST_PRF_ID; thid <= LAST_PRF_ID; thid++) { + if (benchmarkPreBoot && !benchmarkGPT && !HashForSystemEncryption (thid)) + continue; + if (QueryPerformanceCounter (&performanceCountStart) == 0) goto counter_error; - for (i = 1; i <= 5; i++) + for (i = 1; i <= 2; i++) { switch (thid) { case SHA512: /* PKCS-5 test with HMAC-SHA-512 used as the PRF */ - derive_key_sha512 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, 0, FALSE, FALSE), dk, MASTER_KEYDATA_SIZE); + derive_key_sha512 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); break; case SHA256: /* PKCS-5 test with HMAC-SHA-256 used as the PRF */ - derive_key_sha256 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, 0, FALSE, FALSE), dk, MASTER_KEYDATA_SIZE); + derive_key_sha256 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); break; case RIPEMD160: /* PKCS-5 test with HMAC-RIPEMD-160 used as the PRF */ - derive_key_ripemd160 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, 0, FALSE, FALSE), dk, MASTER_KEYDATA_SIZE); + derive_key_ripemd160 ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); break; case WHIRLPOOL: /* PKCS-5 test with HMAC-Whirlpool used as the PRF */ - derive_key_whirlpool ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, 0, FALSE, FALSE), dk, MASTER_KEYDATA_SIZE); + derive_key_whirlpool ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); + break; + + case STREEBOG: + /* PKCS-5 test with HMAC-STREEBOG used as the PRF */ + derive_key_streebog("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); break; } } @@ -5158,62 +5286,113 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) benchmarkTable[benchmarkTotalItems].encSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; benchmarkTable[benchmarkTotalItems].id = thid; - StringCbPrintfW (benchmarkTable[benchmarkTotalItems].name, sizeof(benchmarkTable[benchmarkTable[benchmarkTotalItems].name),L"%s", get_pkcs5_prf_name (thid)); + benchmarkTable[benchmarkTotalItems].decSpeed = get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot); + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (unsigned __int64) (1000 * ((float) benchmarkTable[benchmarkTotalItems].encSpeed / benchmarkPerformanceFrequency.QuadPart / 2)); + if (benchmarkPreBoot) + { + /* heuristics for boot times */ + if (benchmarkGPT) + { + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (benchmarkTable[benchmarkTotalItems].meanBytesPerSec * 8) / 5; + } + else + { + if (thid == SHA256) + { +#ifdef _WIN64 + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (benchmarkTable[benchmarkTotalItems].meanBytesPerSec * 26); +#else + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (benchmarkTable[benchmarkTotalItems].meanBytesPerSec * 24); +#endif + } + else + { +#ifdef _WIN64 + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (benchmarkTable[benchmarkTotalItems].meanBytesPerSec * 21) / 5; +#else + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = (benchmarkTable[benchmarkTotalItems].meanBytesPerSec * 18) / 5; +#endif + } + } + } + StringCbPrintfW (benchmarkTable[benchmarkTotalItems].name, sizeof(benchmarkTable[benchmarkTotalItems].name),L"%s", get_pkcs5_prf_name (thid)); benchmarkTotalItems++; } } + break; + case BENCHMARK_TYPE_ENCRYPTION: + { + /* Encryption algorithm benchmarks */ -#else // #elif PKCS5_BENCHMARKS + // CPU "warm up" (an attempt to prevent skewed results on systems where CPU frequency + // gradually changes depending on CPU load). + ci->ea = EAGetFirst(); + if (!EAInit (ci->ea, ci->master_keydata, ci->ks)) + { + ci->mode = FIRST_MODE_OF_OPERATION_ID; + if (EAInitMode (ci)) + { + int i; - /* Encryption algorithm benchmarks */ + for (i = 0; i < 10; i++) + { + EncryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); + DecryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); + } + } + } - for (ci->ea = EAGetFirst(); ci->ea != 0; ci->ea = EAGetNext(ci->ea)) - { - if (!EAIsFormatEnabled (ci->ea)) - continue; + for (ci->ea = EAGetFirst(); ci->ea != 0; ci->ea = EAGetNext(ci->ea)) + { + if (!EAIsFormatEnabled (ci->ea)) + continue; - if (ERR_CIPHER_INIT_FAILURE == EAInit (ci->ea, ci->master_keydata, ci->ks)) - goto counter_error; + if (ERR_CIPHER_INIT_FAILURE == EAInit (ci->ea, ci->master_keydata, ci->ks)) + goto counter_error; - ci->mode = FIRST_MODE_OF_OPERATION_ID; - if (!EAInitMode (ci)) - goto counter_error; + ci->mode = FIRST_MODE_OF_OPERATION_ID; + if (!EAInitMode (ci)) + goto counter_error; - if (QueryPerformanceCounter (&performanceCountStart) == 0) - goto counter_error; + if (QueryPerformanceCounter (&performanceCountStart) == 0) + goto counter_error; - EncryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); + EncryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); - if (QueryPerformanceCounter (&performanceCountEnd) == 0) - goto counter_error; + if (QueryPerformanceCounter (&performanceCountEnd) == 0) + goto counter_error; - benchmarkTable[benchmarkTotalItems].encSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; + benchmarkTable[benchmarkTotalItems].encSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; - if (QueryPerformanceCounter (&performanceCountStart) == 0) - goto counter_error; + if (QueryPerformanceCounter (&performanceCountStart) == 0) + goto counter_error; - DecryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); + DecryptDataUnits (lpTestBuffer, &startDataUnitNo, (TC_LARGEST_COMPILER_UINT) benchmarkBufferSize / ENCRYPTION_DATA_UNIT_SIZE, ci); - if (QueryPerformanceCounter (&performanceCountEnd) == 0) - goto counter_error; + if (QueryPerformanceCounter (&performanceCountEnd) == 0) + goto counter_error; - benchmarkTable[benchmarkTotalItems].decSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; - benchmarkTable[benchmarkTotalItems].id = ci->ea; - benchmarkTable[benchmarkTotalItems].meanBytesPerSec = ((unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].encSpeed / benchmarkPerformanceFrequency.QuadPart)) + (unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].decSpeed / benchmarkPerformanceFrequency.QuadPart))) / 2; - EAGetName (benchmarkTable[benchmarkTotalItems].name, ci->ea, 1); + benchmarkTable[benchmarkTotalItems].decSpeed = performanceCountEnd.QuadPart - performanceCountStart.QuadPart; + benchmarkTable[benchmarkTotalItems].id = ci->ea; + benchmarkTable[benchmarkTotalItems].meanBytesPerSec = ((unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].encSpeed / benchmarkPerformanceFrequency.QuadPart)) + (unsigned __int64) (benchmarkBufferSize / ((float) benchmarkTable[benchmarkTotalItems].decSpeed / benchmarkPerformanceFrequency.QuadPart))) / 2; + EAGetName (benchmarkTable[benchmarkTotalItems].name, ci->ea, 1); - benchmarkTotalItems++; + benchmarkTotalItems++; + } + } + break; } -#endif // #elif PKCS5_BENCHMARKS (#else) - if (ci) crypto_close (ci); - VirtualUnlock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); + if (lpTestBuffer) + { + VirtualUnlock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); - free(lpTestBuffer); + _aligned_free(lpTestBuffer); + } benchmarkLastBufferSize = benchmarkBufferSize; @@ -5226,13 +5405,16 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) return TRUE; counter_error: - + if (ci) crypto_close (ci); - VirtualUnlock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); + if (lpTestBuffer) + { + VirtualUnlock (lpTestBuffer, benchmarkBufferSize - (benchmarkBufferSize % 16)); - free(lpTestBuffer); + _aligned_free(lpTestBuffer); + } NormalCursor (); @@ -5248,8 +5430,7 @@ BOOL CALLBACK BenchmarkDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP { WORD lw = LOWORD (wParam); LPARAM nIndex; - HWND hCboxSortMethod = GetDlgItem (hwndDlg, IDC_BENCHMARK_SORT_METHOD); - HWND hCboxBufferSize = GetDlgItem (hwndDlg, IDC_BENCHMARK_BUFFER_SIZE); + static HWND hCboxSortMethod = NULL, hCboxBufferSize = NULL, hCboxList = NULL; switch (msg) { @@ -5258,37 +5439,35 @@ BOOL CALLBACK BenchmarkDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP LVCOLUMNW LvCol; wchar_t s[128]; HWND hList = GetDlgItem (hwndDlg, IDC_RESULTS); + hCboxSortMethod = GetDlgItem (hwndDlg, IDC_BENCHMARK_SORT_METHOD); + hCboxBufferSize = GetDlgItem (hwndDlg, IDC_BENCHMARK_BUFFER_SIZE); + hCboxList = GetDlgItem (hwndDlg, IDC_BENCHMARK_LIST); LocalizeDialog (hwndDlg, "IDD_BENCHMARK_DLG"); benchmarkBufferSize = BENCHMARK_DEFAULT_BUF_SIZE; benchmarkSortMethod = BENCHMARK_SORT_BY_SPEED; + benchmarkType = BENCHMARK_TYPE_ENCRYPTION; + + if (lParam) + { + benchmarkGPT = TRUE; + } + else + benchmarkGPT = FALSE; SendMessage (hList,LVM_SETEXTENDEDLISTVIEWSTYLE,0, - LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_LABELTIP - ); + LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_LABELTIP + ); - memset (&LvCol,0,sizeof(LvCol)); - LvCol.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM|LVCF_FMT; + memset (&LvCol,0,sizeof(LvCol)); + LvCol.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM|LVCF_FMT; LvCol.pszText = GetString ("ALGORITHM"); LvCol.cx = CompensateXDPI (114); LvCol.fmt = LVCFMT_LEFT; SendMessage (hList,LVM_INSERTCOLUMNW,0,(LPARAM)&LvCol); - LvCol.pszText = GetString ("ENCRYPTION"); - LvCol.cx = CompensateXDPI (80); - LvCol.fmt = LVCFMT_RIGHT; - SendMessageW (hList,LVM_INSERTCOLUMNW,1,(LPARAM)&LvCol); - - LvCol.pszText = GetString ("DECRYPTION"); - LvCol.cx = CompensateXDPI (80); - LvCol.fmt = LVCFMT_RIGHT; - SendMessageW (hList,LVM_INSERTCOLUMNW,2,(LPARAM)&LvCol); - - LvCol.pszText = GetString ("MEAN"); - LvCol.cx = CompensateXDPI (80); - LvCol.fmt = LVCFMT_RIGHT; - SendMessageW (hList,LVM_INSERTCOLUMNW,3,(LPARAM)&LvCol); + ResetBenchmarkList (hwndDlg); /* Combo boxes */ @@ -5304,6 +5483,21 @@ BOOL CALLBACK BenchmarkDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP SendMessage (hCboxSortMethod, CB_SETCURSEL, 1, 0); // Default sort method + // benchmark list + + SendMessage (hCboxList, CB_RESETCONTENT, 0, 0); + + nIndex = SendMessageW (hCboxList, CB_ADDSTRING, 0, (LPARAM) GetString ("ENCRYPTION_ALGORITHM")); + SendMessage (hCboxList, CB_SETITEMDATA, nIndex, (LPARAM) 0); + + nIndex = SendMessageW (hCboxList, CB_ADDSTRING, 0, (LPARAM) GetString ("PKCS5_PRF")); + SendMessage (hCboxList, CB_SETITEMDATA, nIndex, (LPARAM) 0); + + nIndex = SendMessageW (hCboxList, CB_ADDSTRING, 0, (LPARAM) GetString ("IDT_HASH_ALGO")); + SendMessage (hCboxList, CB_SETITEMDATA, nIndex, (LPARAM) 0); + + SendMessage (hCboxList, CB_SETCURSEL, 0, 0); // Default: benchmark of encryption + // Buffer size SendMessage (hCboxBufferSize, CB_RESETCONTENT, 0, 0); @@ -5352,12 +5546,13 @@ BOOL CALLBACK BenchmarkDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP uint32 driverConfig = ReadDriverConfigurationFlags(); + int isAesHwSupported = is_aes_hw_cpu_supported(); - SetDlgItemTextW (hwndDlg, IDC_HW_AES, (wstring (L" ") + (GetString (is_aes_hw_cpu_supported() ? ((driverConfig & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION) ? "UISTR_DISABLED" : "UISTR_YES") : "NOT_APPLICABLE_OR_NOT_AVAILABLE"))).c_str()); + SetDlgItemTextW (hwndDlg, IDC_HW_AES, (wstring (L" ") + (GetString (isAesHwSupported ? ((driverConfig & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION) ? "UISTR_DISABLED" : "UISTR_YES") : "NOT_APPLICABLE_OR_NOT_AVAILABLE"))).c_str()); ToHyperlink (hwndDlg, IDC_HW_AES_LABEL_LINK); - if (is_aes_hw_cpu_supported() && (driverConfig & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION)) + if (isAesHwSupported && (driverConfig & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION)) { Warning ("DISABLED_HW_AES_AFFECTS_PERFORMANCE", hwndDlg); } @@ -5409,10 +5604,46 @@ BOOL CALLBACK BenchmarkDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lP } return 1; + case IDC_BENCHMARK_LIST: + + nIndex = SendMessage (hCboxList, CB_GETCURSEL, 0, 0); + if (nIndex != benchmarkType) + { + benchmarkType = (int) nIndex; + benchmarkTotalItems = 0; + ResetBenchmarkList (hwndDlg); + } + + if (benchmarkType == BENCHMARK_TYPE_PRF) + { + ShowWindow (GetDlgItem (hwndDlg, IDC_BENCHMARK_BUFFER_SIZE), SW_HIDE); + ShowWindow (GetDlgItem (hwndDlg, IDT_BUFFER_SIZE), SW_HIDE); + ShowWindow (GetDlgItem (hwndDlg, IDC_PIM), SW_SHOW); + ShowWindow (GetDlgItem (hwndDlg, IDT_PIM), SW_SHOW); + ShowWindow (GetDlgItem (hwndDlg, IDC_BENCHMARK_PREBOOT), SW_SHOW); + } + else + { + ShowWindow (GetDlgItem (hwndDlg, IDC_BENCHMARK_BUFFER_SIZE), SW_SHOW); + ShowWindow (GetDlgItem (hwndDlg, IDT_BUFFER_SIZE), SW_SHOW); + ShowWindow (GetDlgItem (hwndDlg, IDC_PIM), SW_HIDE); + ShowWindow (GetDlgItem (hwndDlg, IDT_PIM), SW_HIDE); + ShowWindow (GetDlgItem (hwndDlg, IDC_BENCHMARK_PREBOOT), SW_HIDE); + } + return 1; + case IDC_PERFORM_BENCHMARK: - nIndex = SendMessage (hCboxBufferSize, CB_GETCURSEL, 0, 0); - benchmarkBufferSize = (int) SendMessage (hCboxBufferSize, CB_GETITEMDATA, nIndex, 0); + if (benchmarkType == BENCHMARK_TYPE_PRF) + { + benchmarkPim = GetPim (hwndDlg, IDC_PIM); + benchmarkPreBoot = GetCheckBox (hwndDlg, IDC_BENCHMARK_PREBOOT); + } + else + { + nIndex = SendMessage (hCboxBufferSize, CB_GETCURSEL, 0, 0); + benchmarkBufferSize = (int) SendMessage (hCboxBufferSize, CB_GETITEMDATA, nIndex, 0); + } BenchmarkThreadParam threadParam; threadParam.hBenchDlg = hwndDlg; @@ -5518,7 +5749,7 @@ static BOOL CALLBACK RandomPoolEnrichementDlgProc (HWND hwndDlg, UINT msg, WPARA SetTimer (hwndDlg, 0xfd, RANDPOOL_DISPLAY_REFRESH_INTERVAL, NULL); SendMessage (GetDlgItem (hwndDlg, IDC_POOL_CONTENTS), WM_SETFONT, (WPARAM) hFixedDigitFont, (LPARAM) TRUE); - + hEntropyBar = GetDlgItem (hwndDlg, IDC_ENTROPY_BAR); SendMessage (hEntropyBar, PBM_SETRANGE32, 0, maxEntropyLevel); SendMessage (hEntropyBar, PBM_SETSTEP, 1, 0); @@ -5553,7 +5784,7 @@ static BOOL CALLBACK RandomPoolEnrichementDlgProc (HWND hwndDlg, UINT msg, WPARA else if (bUseMask) { /* use mask to compute a randomized ascii representation */ - tmpByte = (randPool[row * RANDPOOL_DISPLAY_COLUMNS + col] - + tmpByte = (randPool[row * RANDPOOL_DISPLAY_COLUMNS + col] - lastRandPool[row * RANDPOOL_DISPLAY_COLUMNS + col]) ^ maskRandPool [row * RANDPOOL_DISPLAY_COLUMNS + col]; tmp[0] = (wchar_t) (((tmpByte >> 4) % 6) + L'*'); tmp[1] = (wchar_t) (((tmpByte & 0x0F) % 6) + L'*'); @@ -5714,8 +5945,8 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP SendMessage (hEntropyBar, PBM_SETSTEP, 1, 0); SendMessage (hEntropyBar, PBM_SETSTATE, PBST_ERROR, 0); -#ifndef VOLFORMAT - if (Randinit ()) +#ifndef VOLFORMAT + if (Randinit ()) { handleError (hwndDlg, (CryptoAPILastError == ERROR_SUCCESS)? ERR_RAND_INIT_FAILED : ERR_CAPI_INIT_FAILED, SRC_POS); EndDialog (hwndDlg, IDCLOSE); @@ -5761,7 +5992,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP else if (bUseMask) { /* use mask to compute a randomized ASCII representation */ - tmpByte = (randPool[row * RANDPOOL_DISPLAY_COLUMNS + col] - + tmpByte = (randPool[row * RANDPOOL_DISPLAY_COLUMNS + col] - lastRandPool[row * RANDPOOL_DISPLAY_COLUMNS + col]) ^ maskRandPool [row * RANDPOOL_DISPLAY_COLUMNS + col]; tmp[0] = (wchar_t) (((tmpByte >> 4) % 6) + L'*'); tmp[1] = (wchar_t) (((tmpByte & 0x0F) % 6) + L'*'); @@ -5889,7 +6120,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP for (i= 0; i < keyfilesCount; i++) { StringCbCopyW(szFileName, sizeof(szFileName), szDirName); - + if (i > 0) { StringCbPrintfW(szSuffix, sizeof(szSuffix), L"_%d", i); @@ -5946,7 +6177,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP NormalCursor(); return 1; } - + /* since keyfilesSize < 1024 * 1024, we mask with 0x000FFFFF */ keyfilesSize = (long) (((unsigned long) keyfilesSize) & 0x000FFFFF); @@ -5954,7 +6185,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP keyfilesSize += 64; } - /* Generate the keyfile */ + /* Generate the keyfile */ if (!RandgetBytesFull (hwndDlg, keyfile, keyfilesSize, TRUE, TRUE)) { _close (fhKeyfile); @@ -5962,7 +6193,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP TCfree(keyfile); NormalCursor(); return 1; - } + } /* Write the keyfile */ status = _write (fhKeyfile, keyfile, keyfilesSize); @@ -5975,7 +6206,7 @@ BOOL CALLBACK KeyfileGeneratorDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LP NormalCursor(); handleWin32Error (hwndDlg, SRC_POS); return 1; - } + } } TCfree(keyfile); @@ -5994,7 +6225,7 @@ exit: WaitCursor(); KillTimer (hwndDlg, 0xfd); -#ifndef VOLFORMAT +#ifndef VOLFORMAT RandStop (FALSE); #endif /* Cleanup */ @@ -6109,7 +6340,7 @@ CipherTestDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { ShowWindow(GetDlgItem(hwndDlg, IDC_TESTS_MESSAGE), SW_SHOWNORMAL); SetWindowTextW(GetDlgItem(hwndDlg, IDC_TESTS_MESSAGE), GetString ("TESTS_FAILED")); - } + } else { ShowWindow(GetDlgItem(hwndDlg, IDC_TESTS_MESSAGE), SW_SHOWNORMAL); @@ -6137,7 +6368,7 @@ CipherTestDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) if (lw == IDOK || lw == IDC_ENCRYPT || lw == IDC_DECRYPT) { - char key[128+1], inputtext[128+1], secondaryKey[64+1], dataUnitNo[16+1]; + CRYPTOPP_ALIGN_DATA(16) char key[128+1], inputtext[128+1], secondaryKey[64+1], dataUnitNo[16+1]; wchar_t szTmp[128+1]; int ks, pt, n, tlen, blockNo = 0; BOOL bEncrypt; @@ -6215,7 +6446,7 @@ CipherTestDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) inputtext[n] = (char) x; } - + // XTS if (bXTSTestEnabled) { @@ -6272,10 +6503,10 @@ CipherTestDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) blockNo = (int) SendMessage (GetDlgItem (hwndDlg, IDC_TEST_BLOCK_NUMBER), CB_GETITEMDATA, SendMessage (GetDlgItem (hwndDlg, IDC_TEST_BLOCK_NUMBER), CB_GETCURSEL, 0, 0), 0); } // if (bXTSTestEnabled) - + /* Perform the actual tests */ - if (ks != CB_ERR && pt != CB_ERR) + if (ks != CB_ERR && pt != CB_ERR) { char tmp[128]; int tmpRetVal; @@ -6372,7 +6603,7 @@ CipherTestDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; } -void +void ResetCipherTest(HWND hwndDlg, int idTestCipher) { int ndx; @@ -6406,11 +6637,16 @@ ResetCipherTest(HWND hwndDlg, int idTestCipher) SetWindowText(GetDlgItem(hwndDlg, IDC_SECONDARY_KEY), L"0000000000000000000000000000000000000000000000000000000000000000"); SetWindowText(GetDlgItem(hwndDlg, IDC_TEST_DATA_UNIT_NUMBER), L"0"); - + SetWindowText(GetDlgItem(hwndDlg, IDC_PLAINTEXT), L"0000000000000000"); SetWindowText(GetDlgItem(hwndDlg, IDC_CIPHERTEXT), L"0000000000000000"); - if (idTestCipher == AES || idTestCipher == SERPENT || idTestCipher == TWOFISH) + if (idTestCipher == AES || idTestCipher == SERPENT || idTestCipher == TWOFISH || idTestCipher == CAMELLIA +#if defined(CIPHER_GOST89) + || idTestCipher == GOST89 +#endif + || idTestCipher == KUZNYECHIK + ) { ndx = (int) SendMessage (GetDlgItem(hwndDlg, IDC_KEY_SIZE), CB_ADDSTRING, 0,(LPARAM) L"256"); SendMessage(GetDlgItem(hwndDlg, IDC_KEY_SIZE), CB_SETITEMDATA, ndx,(LPARAM) 32); @@ -6476,7 +6712,7 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA pStr++; pwStr++; - do + do { if (*pStr != 0) { @@ -6490,7 +6726,7 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA hUserFont), nLongestButtonCaptionWidth); - nLongestButtonCaptionCharLen = max (nLongestButtonCaptionCharLen, + nLongestButtonCaptionCharLen = max (nLongestButtonCaptionCharLen, (int) wcslen ((const wchar_t *) (bResolve ? GetString(*pStr) : *pwStr))); } @@ -6509,14 +6745,14 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA // Length of main message in characters (not bytes) nMainTextLenInChars = (int) wcslen ((const wchar_t *) (bResolve ? GetString(*(pStrOrig+1)) : *(pwStrOrig+1))); - if (nMainTextLenInChars > 200 + if (nMainTextLenInChars > 200 && nMainTextLenInChars / nLongestButtonCaptionCharLen >= 10) { - // As the main text is longer than 200 characters, we will "pad" the widest button caption with - // spaces (if it is not wide enough) so as to increase the width of the whole dialog window. + // As the main text is longer than 200 characters, we will "pad" the widest button caption with + // spaces (if it is not wide enough) so as to increase the width of the whole dialog window. // Otherwise, it would look too tall (dialog boxes look better when they are more wide than tall). nLongestButtonCaptionWidth = CompensateXDPI (max ( - nLongestButtonCaptionWidth, + nLongestButtonCaptionWidth, min (350, nMainTextLenInChars))); } @@ -6544,10 +6780,10 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA do { - offset = FindString ((char *) (bResolve ? GetString(*(pStrOrig+1)) : *(pwStrOrig+1)), + offset = FindString ((char *) (bResolve ? GetString(*(pStrOrig+1)) : *(pwStrOrig+1)), (char *) L"\n", - nMainTextLenInChars * 2, - (int) wcslen (L"\n") * 2, + nMainTextLenInChars * 2, + (int) wcslen (L"\n") * 2, offset + 1); newLineSeqCount++; @@ -6568,8 +6804,8 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA // Reduction in height according to the number of shown buttons vertSubOffset = ((MAX_MULTI_CHOICES - nActiveChoices) * nBaseButtonHeight); - if (horizSubOffset > 0 - || vertMsgHeightOffset > 0 + if (horizSubOffset > 0 + || vertMsgHeightOffset > 0 || vertOffset > 0) { // Resize/move each button if necessary @@ -6603,7 +6839,7 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA trec.right + 2 + horizSubOffset, trec.bottom + 2, TRUE); - + GetWindowRect(GetDlgItem(hwndDlg, IDC_MC_DLG_HR2), &rec); GetClientRect(GetDlgItem(hwndDlg, IDC_MC_DLG_HR2), &trec); MoveWindow (GetDlgItem(hwndDlg, IDC_MC_DLG_HR2), @@ -6657,7 +6893,7 @@ BOOL CALLBACK MultiChoiceDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA BOOL CheckCapsLock (HWND hwnd, BOOL quiet) { - if ((GetKeyState(VK_CAPITAL) & 1) != 0) + if ((GetKeyState(VK_CAPITAL) & 1) != 0) { if (!quiet) { @@ -6686,7 +6922,7 @@ BOOL CheckFileExtension (wchar_t *fileName) L".vb", L".vbe", L".vbs", L".vsmacros", L".vss", L".vst", L".vsw", L".ws", L".wsc", L".wsf", L".wsh", L".xsd", L".xsl", // These additional file extensions are usually watched by antivirus programs L".386", L".acm", L".ade", L".adp", L".ani", L".app", L".asd", L".asf", L".asx", L".awx", L".ax", L".boo", L".bz2", L".cdf", - L".class", L".dhtm", L".dhtml",L".dlo", L".emf", L".eml", L".flt", L".fot", L".gz", L".hlp", L".htm", L".html", L".ini", + L".class", L".dhtm", L".dhtml",L".dlo", L".emf", L".eml", L".flt", L".fot", L".gz", L".hlp", L".htm", L".html", L".ini", L".j2k", L".jar", L".jff", L".jif", L".jmh", L".jng", L".jp2", L".jpe", L".jpeg", L".jpg", L".lsp", L".mod", L".nws", L".obj", L".olb", L".osd", L".ov1", L".ov2", L".ov3", L".ovl", L".ovl", L".ovr", L".pdr", L".pgm", L".php", L".pkg", L".pl", L".png", L".pot", L".pps", L".ppt", L".ps1", L".ps1xml", L".psc1", L".rar", L".rpl", L".rtf", L".sbf", L".script", L".sh", L".sha", L".shtm", @@ -6858,7 +7094,7 @@ int DriverUnmountVolume (HWND hwndDlg, int nDosDriveNo, BOOL forced) memcpy (wszLabel, prop.wszLabel, sizeof (wszLabel)); bDriverSetLabel = prop.bDriverSetLabel; } - + unmount.nDosDriveNo = nDosDriveNo; unmount.ignoreOpenFiles = forced; @@ -6928,11 +7164,11 @@ void BroadcastDeviceChange (WPARAM message, int nDosDriveNo, DWORD driveMap) } } - dbv.dbcv_size = sizeof (dbv); - dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME; + dbv.dbcv_size = sizeof (dbv); + dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME; dbv.dbcv_reserved = 0; dbv.dbcv_unitmask = driveMap; - dbv.dbcv_flags = 0; + dbv.dbcv_flags = 0; UINT timeOut = 1000; @@ -7032,8 +7268,8 @@ BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) SetWindowLongPtrW (hProgress, GWL_STYLE, PBS_MARQUEE | GetWindowLongPtrW (hProgress, GWL_STYLE)); ::SendMessageW(hProgress, PBM_SETMARQUEE, (WPARAM) TRUE, (LPARAM) 0); } - - thParam->hwnd = hwndDlg; + + thParam->hwnd = hwndDlg; // For now, we don't have system menu is the resources but we leave this code // if it is enabled in the future @@ -7047,8 +7283,8 @@ BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) HICON hIcon = (HICON)::LoadImage(hInst, MAKEINTRESOURCE(IDI_TRUECRYPT_ICON), IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR); ::SendMessage(hwndDlg, WM_SETICON, TRUE, (LPARAM)hIcon); HICON hIconSmall = (HICON)::LoadImage(hInst, MAKEINTRESOURCE(IDI_TRUECRYPT_ICON), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); - ::SendMessage(hwndDlg, WM_SETICON, FALSE, (LPARAM)hIconSmall); - } + ::SendMessage(hwndDlg, WM_SETICON, FALSE, (LPARAM)hIconSmall); + } LocalizeDialog (hwndDlg, NULL); _beginthread(WaitThread, 0, thParam); @@ -7076,26 +7312,26 @@ BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) void BringToForeground(HWND hWnd) { if(!::IsWindow(hWnd)) return; - + DWORD lockTimeOut = 0; HWND hCurrWnd = ::GetForegroundWindow(); DWORD dwThisTID = ::GetCurrentThreadId(), dwCurrTID = ::GetWindowThreadProcessId(hCurrWnd,0); - + if (hCurrWnd != hWnd) { if(dwThisTID != dwCurrTID) { ::AttachThreadInput(dwThisTID, dwCurrTID, TRUE); - + ::SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,0,&lockTimeOut,0); ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,0,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE); - + ::AllowSetForegroundWindow(ASFW_ANY); } - + ::SetForegroundWindow(hWnd); - + if(dwThisTID != dwCurrTID) { ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,(PVOID)lockTimeOut,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE); @@ -7168,12 +7404,12 @@ static BOOL PerformMountIoctl (MOUNT_STRUCT* pmount, LPDWORD pdwResult, BOOL use CreateFullVolumePath (pmount->wszVolume, sizeof(pmount->wszVolume), devicePath.c_str(), &bDevice); } } - + return DeviceIoControl (hDriver, TC_IOCTL_MOUNT_VOLUME, pmount, sizeof (MOUNT_STRUCT), pmount, sizeof (MOUNT_STRUCT), pdwResult, NULL); } -// specific definitions and implementation for support of mount operation +// specific definitions and implementation for support of mount operation // in wait dialog mechanism typedef struct @@ -7306,7 +7542,7 @@ retry: path = path.substr (4); StringCchCopyW (volumePath, TC_MAX_PATH, path.c_str()); } - + if (path.find (L"Volume{") == 0 && path.rfind (L"}\\") == path.size() - 2) { wstring resolvedPath = VolumeGuidPathToDevicePath (path); @@ -7318,7 +7554,7 @@ retry: if ((path.length () >= 3) && (_wcsnicmp (path.c_str(), L"ID:", 3) == 0)) { std::vector<byte> arr; - if ( (path.length() == (3 + 2*VOLUME_ID_SIZE)) + if ( (path.length() == (3 + 2*VOLUME_ID_SIZE)) && HexWideStringToArray (path.c_str() + 3, arr) && (arr.size() == VOLUME_ID_SIZE) ) @@ -7355,7 +7591,7 @@ retry: mount.BytesPerSector = bps; mount.BytesPerPhysicalSector = bps; } - + if (IsOSAtLeast (WIN_VISTA)) { if ( (wcslen(root) >= 2) @@ -7463,7 +7699,7 @@ retry: goto retry; } - // Ask user + // Ask user if (IDYES == AskWarnNoYes ("FILE_IN_USE", hwndDlg)) { mount.bExclusiveAccess = FALSE; @@ -7484,12 +7720,12 @@ retry: { if (mount.nReturnCode == ERR_PASSWORD_WRONG) { - // Do not report wrong password, if not instructed to + // Do not report wrong password, if not instructed to if (bReportWrongPassword) { IncreaseWrongPwdRetryCount (1); // We increase the count here only if bReportWrongPassword is TRUE, because "Auto-Mount All Devices" and other callers do it separately - if (WrongPwdRetryCountOverLimit () + if (WrongPwdRetryCountOverLimit () && !mount.UseBackupHeader) { // Retry using embedded header backup (if any) @@ -7537,7 +7773,7 @@ retry: if (bReportWrongPassword && !Silent) Warning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK", hwndDlg); } - + LastMountedVolumeDirty = mount.FilesystemDirty; if (mount.FilesystemDirty) @@ -9984,6 +10220,18 @@ void Applink (char *dest, BOOL bSendOS, char *extraOutput) { StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Twofish"); } + else if (strcmp(dest, "gost89") == 0) + { + StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=GOST89"); + } + else if (strcmp(dest, "kuznyechik") == 0) + { + StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Kuznyechik"); + } + else if (strcmp(dest, "camellia") == 0) + { + StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Camellia"); + } else if (strcmp(dest, "cascades") == 0) { StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Cascades"); @@ -10980,15 +11228,27 @@ BOOL InitSecurityTokenLibrary (HWND hwndDlg) PinRequestHandler(HWND hwnd) : m_hwnd(hwnd) {} virtual void operator() (string &str) { - HWND hParent = IsWindow (m_hwnd)? m_hwnd : GetActiveWindow(); - if (!hParent) - hParent = GetForegroundWindow (); - if (DialogBoxParamW (hInst, MAKEINTRESOURCEW (IDD_TOKEN_PASSWORD), hParent, (DLGPROC) SecurityTokenPasswordDlgProc, (LPARAM) &str) == IDCANCEL) - throw UserAbort (SRC_POS); - + if (CmdTokenPin[0]) + { + str = CmdTokenPin; + } + else + { + HWND hParent = IsWindow (m_hwnd)? m_hwnd : GetActiveWindow(); + if (!hParent) + hParent = GetForegroundWindow (); + if (DialogBoxParamW (hInst, MAKEINTRESOURCEW (IDD_TOKEN_PASSWORD), hParent, (DLGPROC) SecurityTokenPasswordDlgProc, (LPARAM) &str) == IDCANCEL) + throw UserAbort (SRC_POS); + } if (hCursor != NULL) SetCursor (hCursor); } + + virtual void notifyIncorrectPin () + { + // clear wrong PIN + burn (&CmdTokenPin, sizeof (CmdTokenPin)); + } }; struct WarningHandler : public SendExceptionFunctor @@ -11888,3 +12148,52 @@ BOOL CopyTextToClipboard (LPCWSTR txtValue) return bRet; } + +BOOL GetFreeDriveLetter(WCHAR* pCh) { + DWORD dwUsedDrives = GetLogicalDrives(); + WCHAR l; + for (l = L'A'; l <= L'Z'; l++) { + if ((dwUsedDrives & 1) == 0) { + *pCh = l; + return TRUE; + } + dwUsedDrives = dwUsedDrives >> 1; + } + return FALSE; +} + +BOOL RaisePrivileges(void) +{ + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + BOOL bRet = FALSE; + DWORD dwLastError = 0; + + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &hToken)) + { + if (LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME, + &tkp.Privileges[0].Luid)) + { + DWORD len; + + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + bRet = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, &len); + if (!bRet) + dwLastError = GetLastError (); + } + else + dwLastError = GetLastError (); + + CloseHandle(hToken); + } + else + dwLastError = GetLastError (); + + SetLastError (dwLastError); + + return bRet; +}
\ No newline at end of file diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h index 9387336e..9ae9d5b2 100644 --- a/src/Common/Dlgcode.h +++ b/src/Common/Dlgcode.h @@ -102,6 +102,7 @@ extern char *ConfigBuffer; extern wchar_t szHelpFile[TC_MAX_PATH]; extern wchar_t szHelpFile2[TC_MAX_PATH]; extern wchar_t SecurityTokenLibraryPath[TC_MAX_PATH]; +extern char CmdTokenPin [TC_MAX_PATH]; extern HFONT hFixedDigitFont; extern HFONT hBoldFont; extern HFONT hTitleFont; @@ -509,6 +510,9 @@ void AllowMessageInUIPI (UINT msg); BOOL IsRepeatedByteArray (byte value, const byte* buffer, size_t bufferSize); BOOL TranslateVolumeID (HWND hwndDlg, wchar_t* pathValue, size_t cchPathValue); BOOL CopyTextToClipboard (const wchar_t* txtValue); +BOOL LaunchElevatedProcess (HWND hwndDlg, const wchar_t* szModPath, const wchar_t* args); +BOOL GetFreeDriveLetter(WCHAR* pCh); +BOOL RaisePrivileges(void); #ifdef __cplusplus } diff --git a/src/Common/EncryptionThreadPool.c b/src/Common/EncryptionThreadPool.c index d99512a9..f1207113 100644 --- a/src/Common/EncryptionThreadPool.c +++ b/src/Common/EncryptionThreadPool.c @@ -182,6 +182,11 @@ static TC_THREAD_PROC EncryptionThreadProc (void *threadArg) workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.DerivedKey, GetMaxPkcs5OutSize()); break; + case STREEBOG: + derive_key_streebog(workItem->KeyDerivation.Password, workItem->KeyDerivation.PasswordLength, workItem->KeyDerivation.Salt, PKCS5_SALT_SIZE, + workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.DerivedKey, GetMaxPkcs5OutSize()); + break; + default: TC_THROW_FATAL_EXCEPTION; } diff --git a/src/Common/Endian.h b/src/Common/Endian.h index 00fed048..fd4c901c 100644 --- a/src/Common/Endian.h +++ b/src/Common/Endian.h @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -21,7 +21,7 @@ extern "C" { #endif -#ifdef _WIN32 +#if defined(_WIN32) || defined(_UEFI) # ifndef LITTLE_ENDIAN # define LITTLE_ENDIAN 1234 @@ -110,7 +110,7 @@ extern "C" ( ( unsigned __int32 ) memPtr[ -2 ] << 8 ) | ( unsigned __int32 ) memPtr[ -1 ] ) #define mgetWord(memPtr) \ - ( memPtr += 2, ((( unsigned short ) memPtr[ -2 ] << 8 ) | ( ( unsigned short ) memPtr[ -1 ] )) ) + ( memPtr += 2, ((( unsigned short ) memPtr[ -2 ] << 8 ) | ( ( unsigned short ) memPtr[ -1 ] )) ) #define mgetByte(memPtr) \ ( ( unsigned char ) *memPtr++ ) @@ -139,7 +139,7 @@ unsigned __int16 MirrorBytes16 (unsigned __int16 x); unsigned __int32 MirrorBytes32 (unsigned __int32 x); #ifndef TC_NO_COMPILER_INT64 uint64 MirrorBytes64 (uint64 x); -#endif +#endif void LongReverse ( unsigned __int32 *buffer , unsigned byteCount ); #if defined(__cplusplus) diff --git a/src/Common/Fat.c b/src/Common/Fat.c index fb7dc1ff..7625e869 100644 --- a/src/Common/Fat.c +++ b/src/Common/Fat.c @@ -262,7 +262,7 @@ FormatFat (void* hwndDlgPtr, unsigned __int64 startSector, fatparams * ft, void unsigned __int64 nSecNo = startSector; int x, n; int retVal; - char temporaryKey[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) char temporaryKey[MASTER_KEYDATA_SIZE]; HWND hwndDlg = (HWND) hwndDlgPtr; LARGE_INTEGER startOffset; diff --git a/src/Common/Format.c b/src/Common/Format.c index f3114e5e..4df27c1e 100644 --- a/src/Common/Format.c +++ b/src/Common/Format.c @@ -566,10 +566,63 @@ begin_format: // Fill reserved header sectors (including the backup header area) with random data if (!volParams->hiddenVol) { - nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE); + BOOL bUpdateBackup = FALSE; + + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, dataAreaSize, FALSE, FALSE, FALSE); if (nStatus != ERR_SUCCESS) goto error; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes. + + while (TRUE) + { + PCRYPTO_INFO dummyInfo = NULL; + LARGE_INTEGER hiddenOffset; + + hiddenOffset.QuadPart = bUpdateBackup ? dataAreaSize + TC_VOLUME_HEADER_GROUP_SIZE + TC_HIDDEN_VOLUME_HEADER_OFFSET: TC_HIDDEN_VOLUME_HEADER_OFFSET; + + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + header, + volParams->ea, + FIRST_MODE_OF_OPERATION_ID, + NULL, + 0, + 0, + NULL, + &dummyInfo, + dataAreaSize, + dataAreaSize, + dataOffset, + dataAreaSize, + 0, + volParams->headerFlags, + FormatSectorSize, + FALSE); + + if (nStatus != ERR_SUCCESS) + goto error; + + crypto_close (dummyInfo); + + if (!SetFilePointerEx ((HANDLE) dev, hiddenOffset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (!WriteEffectiveVolumeHeader (volParams->bDevice, dev, header)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (bUpdateBackup) + break; + + bUpdateBackup = TRUE; + } } #ifndef DEBUG @@ -700,8 +753,8 @@ int FormatNoFs (HWND hwndDlg, unsigned __int64 startSector, __int64 num_sectors, unsigned __int64 nSecNo = startSector; int retVal = 0; DWORD err; - char temporaryKey[MASTER_KEYDATA_SIZE]; - char originalK2[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) char temporaryKey[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) char originalK2[MASTER_KEYDATA_SIZE]; LARGE_INTEGER startOffset; LARGE_INTEGER newOffset; diff --git a/src/Common/Keyfiles.c b/src/Common/Keyfiles.c index 5ee5bccf..14d415f0 100644 --- a/src/Common/Keyfiles.c +++ b/src/Common/Keyfiles.c @@ -238,11 +238,6 @@ close: BOOL KeyFilesApply (HWND hwndDlg, Password *password, KeyFile *firstKeyFile, const wchar_t* volumeFileName) { - return KeyFilesApplyWithPin (hwndDlg, password, nullptr, firstKeyFile, volumeFileName); -} - -BOOL KeyFilesApplyWithPin (HWND hwndDlg, Password *password, char* pin, KeyFile *firstKeyFile, const wchar_t* volumeFileName) -{ BOOL status = TRUE; KeyFile kfSubStruct; KeyFile *kf; @@ -271,7 +266,7 @@ BOOL KeyFilesApplyWithPin (HWND hwndDlg, Password *password, char* pin, KeyFile // Apply security token keyfile vector <byte> keyfileData; SecurityTokenKeyfilePath secPath (kf->FileName); - SecurityToken::GetKeyfileData (SecurityTokenKeyfile (secPath, pin), pin, keyfileData); + SecurityToken::GetKeyfileData (SecurityTokenKeyfile (secPath), keyfileData); if (keyfileData.empty()) { diff --git a/src/Common/Keyfiles.h b/src/Common/Keyfiles.h index c94f1378..a247cbc7 100644 --- a/src/Common/Keyfiles.h +++ b/src/Common/Keyfiles.h @@ -40,7 +40,6 @@ void KeyFileRemoveAll (KeyFile **firstKeyFile); KeyFile *KeyFileClone (KeyFile *keyFile); void KeyFileCloneAll (KeyFile *firstKeyFile, KeyFile **outputKeyFile); BOOL KeyFilesApply (HWND hwndDlg, Password *password, KeyFile *firstKeyFilem, const wchar_t* volumeFileName); -BOOL KeyFilesApplyWithPin (HWND hwndDlg, Password *password, char* pin, KeyFile *firstKeyFilem, const wchar_t* volumeFileName); BOOL CALLBACK KeyFilesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL KeyfilesPopupMenu (HWND hwndDlg, POINT popupPosition, KeyFilesDlgParam *dialogParam); diff --git a/src/Common/Language.c b/src/Common/Language.c index f03a4e54..cb0178d9 100644 --- a/src/Common/Language.c +++ b/src/Common/Language.c @@ -361,9 +361,11 @@ BOOL LoadLanguageFile () header = headerPtr; if (header == NULL) return FALSE; + header--; do { + header++; if (sscanf (header, "#define %127s %d", key, &intKey) == 2) { WCHAR *str = GetString (key); @@ -372,7 +374,7 @@ BOOL LoadLanguageFile () AddDictionaryEntry (NULL, intKey, str); } - } while ((header = strchr (header, '\n') + 1) != (char *) 1); + } while ((header = strchr (header, '\n')) != NULL); free (headerPtr); } diff --git a/src/Common/Language.xml b/src/Common/Language.xml index 99e260c0..38f2cc49 100644 --- a/src/Common/Language.xml +++ b/src/Common/Language.xml @@ -261,7 +261,7 @@ <control lang="en" key="IDM_UNMOUNTALL">Dismount All Mounted Volumes</control> <control lang="en" key="IDM_UNMOUNT_VOLUME">Dismount Volume</control> <control lang="en" key="IDM_VERIFY_RESCUE_DISK">Verify Rescue Disk</control> - <control lang="en" key="IDM_VERIFY_RESCUE_DISK_ISO">Verify Rescue Disk ISO Image</control> + <control lang="en" key="IDM_VERIFY_RESCUE_DISK_ISO">Verify Rescue Disk Image</control> <control lang="en" key="IDM_VERSION_HISTORY">Version History</control> <control lang="en" key="IDM_VOLUME_EXPANDER">Volume Expander</control> <control lang="en" key="IDM_VOLUME_PROPERTIES">Volume Properties</control> @@ -335,7 +335,7 @@ <control lang="en" key="IDC_USE_EMBEDDED_HEADER_BAK">Use backup header embedded in &volume if available</control> <control lang="en" key="IDC_XTS_MODE_ENABLED">XTS mode</control> <control lang="en" key="IDD_ABOUT_DLG">About VeraCrypt</control> - <control lang="en" key="IDD_BENCHMARK_DLG">VeraCrypt - Encryption Algorithm Benchmark</control> + <control lang="en" key="IDD_BENCHMARK_DLG">VeraCrypt - Algorithms Benchmark</control> <control lang="en" key="IDD_CIPHER_TEST_DLG">VeraCrypt - Test Vectors</control> <control lang="en" key="IDD_COMMANDHELP_DLG">Command Line Help</control> <control lang="en" key="IDD_KEYFILES">VeraCrypt - Keyfiles</control> @@ -962,14 +962,14 @@ <string lang="en" key="VOLUME_HAS_NO_BACKUP_HEADER">There is no backup header embedded in this volume (note that only volumes created by VeraCrypt 6.0 or later contain embedded backup headers).</string> <string lang="en" key="BACKUP_HEADER_NOT_FOR_SYS_DEVICE">You are attempting to back up the header of the system partition/drive. This is not allowed. Backup/restore operations pertaining to the system partition/drive can be performed only using the VeraCrypt Rescue Disk.\n\nDo you want to create a VeraCrypt Rescue Disk?</string> <string lang="en" key="RESTORE_HEADER_NOT_FOR_SYS_DEVICE">You are attempting to restore the header of a virtual VeraCrypt volume but you selected the system partition/drive. This is not allowed. Backup/restore operations pertaining to the system partition/drive can be performed only using the VeraCrypt Rescue Disk.\n\nDo you want to create a VeraCrypt Rescue Disk?</string> - <string lang="en" key="RESCUE_DISK_NON_WIZARD_CREATION_SELECT_PATH">After you click OK, you will select a filename for the new VeraCrypt Rescue Disk ISO image and the location where you wish to place it.</string> + <string lang="en" key="RESCUE_DISK_NON_WIZARD_CREATION_SELECT_PATH">After you click OK, you will select a filename for the new VeraCrypt Rescue Disk image and the location where you wish to place it.</string> <string lang="en" key="RESCUE_DISK_NON_WIZARD_CREATION_BURN">The Rescue Disk image has been created and stored in this file:\n%s\n\nNow you need to burn the Rescue Disk image to a CD or DVD.\n\nIMPORTANT: Note that the file must be written to the CD/DVD as an ISO disk image (not as an individual file). For information on how to do so, please refer to the documentation of your CD/DVD recording software.\n\nAfter you burn the Rescue Disk, select 'System' > 'Verify Rescue Disk' to verify that it has been correctly burned.</string> <string lang="en" key="RESCUE_DISK_NON_WIZARD_CREATION_WIN_ISOBURN">The Rescue Disk image has been created and stored in this file:\n%s\n\nNow you need to burn the Rescue Disk image to a CD or DVD.\n\nDo you want to launch the Microsoft Windows Disc Image Burner now?\n\nNote: After you burn the Rescue Disk, select 'System' > 'Verify Rescue Disk' to verify that it has been correctly burned.</string> - <string lang="en" key="RESCUE_DISK_NON_WIZARD_CHECK_INSERT">Please insert your VeraCrypt Rescue Disk into your CD/DVD drive and click OK to verify it.</string> + <string lang="en" key="RESCUE_DISK_NON_WIZARD_CHECK_INSERT">Please insert your VeraCrypt Rescue Disk and click OK to verify it.</string> <string lang="en" key="RESCUE_DISK_NON_WIZARD_CHECK_PASSED">The VeraCrypt Rescue Disk has been successfully verified.</string> <string lang="en" key="RESCUE_DISK_NON_WIZARD_CHECK_FAILED">Cannot verify that the Rescue Disk has been correctly burned.\n\nIf you have burned the Rescue Disk, please eject and reinsert the CD/DVD; then try again. If this does not help, please try other CD/DVD recording software and/or medium.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created for a different master key, password, salt, etc., please note that such Rescue Disk will always fail this verification. To create a new Rescue Disk fully compatible with your current configuration, select 'System' > 'Create Rescue Disk'.</string> - <string lang="en" key="RESCUE_DISK_ISO_IMAGE_CHECK_PASSED">The VeraCrypt Rescue Disk ISO image has been successfully verified.</string> - <string lang="en" key="RESCUE_DISK_ISO_IMAGE_CHECK_FAILED">The Rescue Disk ISO image verification failed.\n\nIf you attempted to verify a VeraCrypt Rescue Disk ISO image created for a different master key, password, salt, etc., please note that such Rescue Disk ISO image will always fail this verification. To create a new Rescue Disk ISO image fully compatible with your current configuration, select 'System' > 'Create Rescue Disk'.</string> + <string lang="en" key="RESCUE_DISK_ISO_IMAGE_CHECK_PASSED">The VeraCrypt Rescue Disk image has been successfully verified.</string> + <string lang="en" key="RESCUE_DISK_ISO_IMAGE_CHECK_FAILED">The Rescue Disk image verification failed.\n\nIf you attempted to verify a VeraCrypt Rescue Disk image created for a different master key, password, salt, etc., please note that such Rescue Disk image will always fail this verification. To create a new Rescue Disk image fully compatible with your current configuration, select 'System' > 'Create Rescue Disk'.</string> <string lang="en" key="ERROR_CREATING_RESCUE_DISK">Error creating VeraCrypt Rescue Disk.</string> <string lang="en" key="CANNOT_CREATE_RESCUE_DISK_ON_HIDDEN_OS">VeraCrypt Rescue Disk cannot be created when a hidden operating system is running.\n\nTo create a VeraCrypt Rescue Disk, boot the decoy operating system and then select 'System' > 'Create Rescue Disk'.</string> <string lang="en" key="RESCUE_DISK_CHECK_FAILED">Cannot verify that the Rescue Disk has been correctly burned.\n\nIf you have burned the Rescue Disk, please eject and reinsert the CD/DVD; then click Next to try again. If this does not help, please try another medium%s.\n\nIf you have not burned the Rescue Disk yet, please do so, and then click Next.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created before you started this wizard, please note that such Rescue Disk cannot be used, because it was created for a different master key. You need to burn the newly generated Rescue Disk.</string> @@ -1077,7 +1077,7 @@ <string lang="en" key="SYS_ENCRYPTION_UPGRADE_UNSUPPORTED_ON_VISTA_SP0">VeraCrypt no longer supports encryption of the system partition/drive on Windows Vista with no Service Pack installed. Before upgrading VeraCrypt, please install Service Pack 1 or higher for Windows Vista.</string> <string lang="en" key="FEATURE_REQUIRES_INSTALLATION">Error: This feature requires VeraCrypt to be installed on the system (you are running VeraCrypt in portable mode).\n\nPlease install VeraCrypt and then try again.</string> <string lang="en" key="WINDOWS_NOT_ON_BOOT_DRIVE_ERROR">WARNING: Windows does not appear to be installed on the drive from which it boots. This is not supported.\n\nYou should continue only if you are sure that Windows is installed on the drive from which it boots.\n\nDo you want to continue?</string> - <string lang="en" key="GPT_BOOT_DRIVE_UNSUPPORTED">Your system drive has a GUID partition table (GPT). Currently, only drives with a MBR partition table are supported.</string> + <string lang="en" key="GPT_BOOT_DRIVE_UNSUPPORTED">You are running a 32-bit Windows and your system drive has a GUID partition table (GPT). Currently, only Windows 64-bit is supported for GPT system encryption.</string> <string lang="en" key="TC_BOOT_LOADER_ALREADY_INSTALLED">CAUTION: The VeraCrypt Boot Loader is already installed on your system drive!\n\nIt is possible that another system on your computer is already encrypted.\n\nWARNING: PROCEEDING WITH ENCRYPTION OF THE CURRENTLY RUNNING SYSTEM MAY MAKE OTHER SYSTEM(S) IMPOSSIBLE TO START AND RELATED DATA INACCESSIBLE.\n\nAre you sure you want to continue?</string> <string lang="en" key="SYS_LOADER_RESTORE_FAILED">Failed to restore the original system loader.\n\nPlease use your VeraCrypt Rescue Disk ('Repair Options' > 'Restore original system loader') or Windows installation medium to replace the VeraCrypt Boot Loader with the Windows system loader.</string> <string lang="en" key="SYS_LOADER_UNAVAILABLE_FOR_RESCUE_DISK">The original system loader will not be stored on the Rescue Disk (probable cause: missing backup file).</string> @@ -1399,6 +1399,20 @@ <string lang="en" key="PIM_TOO_BIG">Personal Iterations Multiplier (PIM) maximum value is 2147468.</string> <control lang="en" key="IDC_SKIP_RESCUE_VERIFICATION">Skip Rescue Disk verification</control> <control lang="en" key="IDC_HIDE_WAITING_DIALOG">Don't show wait message dialog when performing operations</control> + <control lang="en" key="IDC_DISABLE_BOOT_LOADER_HASH_PROMPT">Do not request Hash algorithm in the pre-boot authentication screen</control> + <string lang="en" key="GOST89_HELP">The GOST block cipher, defined in the standard GOST 28147-89 under name Magma, is a Soviet and Russian government standard symmetric key block cipher.\n\nDeveloped in the 1970s, the standard had been marked "Top Secret" and then downgraded to "Secret" in 1990. It was a Soviet alternative to the United States standard algorithm, DES.</string> + <string lang="en" key="KUZNYECHIK_HELP">Kuznyechik is a block cipher first published in 2015 and defined in the National Standard of the Russian Federation GOST R 34.12-2015 and also in RFC 7801. 256-bit key, 128-bit block. Mode of operation is XTS.</string> + <string lang="en" key="CAMELLIA_HELP">Jointly developed by Mitsubishi Electric and NTT of Japan. First published on 2000. 256-bit key, 128-bit block. Mode of operation is XTS. It has been approved for use by the ISO/IEC, the European Union's NESSIE project and the Japanese CRYPTREC project.</string> + <string lang="en" key="TIME">Time</string> + <string lang="en" key="ITERATIONS">Iterations</string> + <string lang="en" key="PRE-BOOT">Pre-Boot</string> + <string lang="en" key="RESCUE_DISK_EFI_INFO">Before you can encrypt the partition, you must create a VeraCrypt Rescue Disk (VRD), which serves the following purposes:\n\n- If the VeraCrypt Boot Loader, master key, or other critical data gets damaged, the VRD allows you to restore it (note, however, that you will still have to enter the correct password then).\n\n- If Windows gets damaged and cannot start, the VRD allows you to permanently decrypt the partition before Windows starts.\n\n- The VRD will contain a backup of the present EFI boot loader and will allow you to restore it if necessary.\n\nThe VeraCrypt Rescue Disk ZIP image will be created in the location specified below.</string> + <string lang="en" key="RESCUE_DISK_EFI_EXTRACT_INFO">The Rescue Disk ZIP image has been created and stored in this file:\n%s\n\nNow you need to extract it to a USB stick that is formatted as FAT/FAT32.\n\n%lsAfter you create the Rescue Disk, click Next to verify that it has been correctly created.</string> + <string lang="en" key="RESCUE_DISK_EFI_EXTRACT_INFO_NO_CHECK">The Rescue Disk ZIP image has been created and stored in this file:\n%s\n\nNow you should either extract the image to a USB stick that is formatted as FAT/FAT32 or move it to a safe location for later use.\n\n%lsClick Next to continue.</string> + <string lang="en" key="RESCUE_DISK_EFI_EXTRACT_INFO_NOTE">IMPORTANT: Note that the zip file must be extracted directly to the root of the USB stick. For example, if the drive letter of the USB stick is E: then extracting the zip file should create a folder E:\\EFI on the USB stick.\n\n</string> + <string lang="en" key="RESCUE_DISK_EFI_CHECK_FAILED">Cannot verify that the Rescue Disk has been correctly extracted.\n\nIf you have extracted the Rescue Disk, please eject and reinsert the USB stick; then click Next to try again. If this does not help, please try another USB stick and/or another ZIP software.\n\nIf you have not extracted the Rescue Disk yet, please do so, and then click Next.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created before you started this wizard, please note that such Rescue Disk cannot be used, because it was created for a different master key. You need to extract the newly generated Rescue Disk ZIP image.</string> + <string lang="en" key="RESCUE_DISK_EFI_NON_WIZARD_CHECK_FAILED">Cannot verify that the Rescue Disk has been correctly extracted.\n\nIf you have extracted the Rescue Disk image to a USB stick, please eject it and reinsert it; then try again. If this does not help, please try other ZIP software and/or medium.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created for a different master key, password, salt, etc., please note that such Rescue Disk will always fail this verification. To create a new Rescue Disk fully compatible with your current configuration, select 'System' > 'Create Rescue Disk'.</string> + <string lang="en" key="RESCUE_DISK_EFI_NON_WIZARD_CREATION">The Rescue Disk image has been created and stored in this file:\n%s\n\nNow you need to extract the Rescue Disk image to a USB stick that is formatted as FAT/FAT32.\n\nIMPORTANT: Note that the zip file must be extracted directly to the root of the USB stick. For example, if the drive letter of the USB stick is E: then extracting the zip file should create a folder E:\\EFI on the USB stick.\n\nAfter you create the Rescue Disk, select 'System' > 'Verify Rescue Disk' to verify that it has been correctly created.</string> </localization> <!-- XML Schema --> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> diff --git a/src/Common/Password.c b/src/Common/Password.c index 2c9e77c3..b0f44384 100644 --- a/src/Common/Password.c +++ b/src/Common/Password.c @@ -437,9 +437,51 @@ int ChangePwd (const wchar_t *lpszVolume, Password *oldPassword, int old_pkcs5, && (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) != 0 && (cryptoInfo->HeaderFlags & ~TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0) { - nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, cryptoInfo->VolumeSize.Value, !backupHeader, backupHeader); + PCRYPTO_INFO dummyInfo = NULL; + LARGE_INTEGER hiddenOffset; + + nStatus = WriteRandomDataToReservedHeaderAreas (hwndDlg, dev, cryptoInfo, cryptoInfo->VolumeSize.Value, !backupHeader, backupHeader, FALSE); + if (nStatus != ERR_SUCCESS) + goto error; + + // write fake hidden volume header to protect against attacks that use statistical entropy + // analysis to detect presence of hidden volumes + hiddenOffset.QuadPart = backupHeader ? cryptoInfo->VolumeSize.Value + TC_VOLUME_HEADER_GROUP_SIZE + TC_HIDDEN_VOLUME_HEADER_OFFSET: TC_HIDDEN_VOLUME_HEADER_OFFSET; + + nStatus = CreateVolumeHeaderInMemory (hwndDlg, FALSE, + buffer, + cryptoInfo->ea, + cryptoInfo->mode, + NULL, + 0, + 0, + NULL, + &dummyInfo, + cryptoInfo->VolumeSize.Value, + cryptoInfo->VolumeSize.Value, + cryptoInfo->EncryptedAreaStart.Value, + cryptoInfo->EncryptedAreaLength.Value, + truecryptMode? 0 : cryptoInfo->RequiredProgramVersion, + cryptoInfo->HeaderFlags, + cryptoInfo->SectorSize, + wipePass < wipePassCount - 1); + if (nStatus != ERR_SUCCESS) goto error; + + crypto_close (dummyInfo); + + if (!SetFilePointerEx ((HANDLE) dev, hiddenOffset, NULL, FILE_BEGIN)) + { + nStatus = ERR_OS_ERROR; + goto error; + } + + if (!WriteEffectiveVolumeHeader (bDevice, dev, buffer)) + { + nStatus = ERR_OS_ERROR; + goto error; + } } FlushFileBuffers (dev); diff --git a/src/Common/Password.h b/src/Common/Password.h index 8818804e..9ef2f035 100644 --- a/src/Common/Password.h +++ b/src/Common/Password.h @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -36,14 +36,14 @@ typedef struct char Pad[3]; // keep 64-bit alignment } Password; -#if defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) +#if defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) && !defined(_UEFI) void VerifyPasswordAndUpdate ( HWND hwndDlg , HWND hButton , HWND hPassword , HWND hVerify , unsigned char *szPassword , char *szVerify, BOOL keyFilesEnabled ); -BOOL CheckPasswordLength (HWND hwndDlg, unsigned __int32 passwordLength, int pim, BOOL bForBoot, BOOL bSkipPasswordWarning, BOOL bSkipPimWarning); -BOOL CheckPasswordCharEncoding (HWND hPassword, Password *ptrPw); +BOOL CheckPasswordLength (HWND hwndDlg, unsigned __int32 passwordLength, int pim, BOOL bForBoot, BOOL bSkipPasswordWarning, BOOL bSkipPimWarning); +BOOL CheckPasswordCharEncoding (HWND hPassword, Password *ptrPw); int ChangePwd (const wchar_t *lpszVolume, Password *oldPassword, int old_pkcs5, int old_pim, BOOL truecryptMode, Password *newPassword, int pkcs5, int pim, int wipePassCount, HWND hwndDlg); -#endif // defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) +#endif // defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) && !defined(_UEFI) #ifdef __cplusplus } diff --git a/src/Common/Pkcs5.c b/src/Common/Pkcs5.c index 8bc828ef..3dbfd322 100644 --- a/src/Common/Pkcs5.c +++ b/src/Common/Pkcs5.c @@ -1,24 +1,26 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source code distribution packages. */ #include "Tcdefs.h" - +#if !defined(_UEFI) #include <memory.h> #include <stdlib.h> +#endif #include "Rmd160.h" #ifndef TC_WINDOWS_BOOT #include "Sha2.h" #include "Whirlpool.h" +#include "cpu.h" #include "misc.h" #else #pragma optimize ("t", on) @@ -150,7 +152,7 @@ static void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, u char* k = hmac->k; char* u = hmac->u; uint32 c; - int i; + int i; #ifdef TC_WINDOWS_BOOT /* In bootloader mode, least significant bit of iterations is a boolean (TRUE for boot derivation mode, FALSE otherwise) @@ -169,7 +171,7 @@ static void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, u /* iteration 1 */ memcpy (k, salt, salt_len); /* salt */ - + /* big-endian block number */ memset (&k[salt_len], 0, 3); k[salt_len + 3] = (char) b; @@ -191,7 +193,7 @@ static void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, u void derive_key_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen) -{ +{ hmac_sha256_ctx hmac; sha256_ctx* ctx; char* buf = hmac.k; @@ -315,7 +317,7 @@ void hmac_sha512 char *k, /* secret key */ int lk, /* length of the key in bytes */ char *d, /* data and also output buffer of at least 64 bytes */ - int ld /* length of data in bytes */ + int ld /* length of data in bytes */ ) { hmac_sha512_ctx hmac; @@ -521,7 +523,7 @@ void hmac_ripemd160 (char *key, int keylen, char *input_digest, int len) /* If the key is longer than the hash algorithm block size, let key = ripemd160(key), as per HMAC specifications. */ - if (keylen > RIPEMD160_BLOCKSIZE) + if (keylen > RIPEMD160_BLOCKSIZE) { RMD160_CTX tctx; @@ -533,14 +535,14 @@ void hmac_ripemd160 (char *key, int keylen, char *input_digest, int len) keylen = RIPEMD160_DIGESTSIZE; burn (&tctx, sizeof(tctx)); // Prevent leaks - } + } /* perform inner RIPEMD-160 */ ctx = &(hmac.inner_digest_ctx); /* start out by storing key in pads */ memset(k_pad, 0x36, 64); /* XOR key with ipad and opad values */ - for (i=0; i<keylen; i++) + for (i=0; i<keylen; i++) { k_pad[i] ^= key[i]; } @@ -551,7 +553,7 @@ void hmac_ripemd160 (char *key, int keylen, char *input_digest, int len) /* perform outer RIPEMD-160 */ ctx = &(hmac.outer_digest_ctx); memset(k_pad, 0x5c, 64); - for (i=0; i<keylen; i++) + for (i=0; i<keylen; i++) { k_pad[i] ^= key[i]; } @@ -591,7 +593,7 @@ static void derive_u_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len /* iteration 1 */ memcpy (k, salt, salt_len); /* salt */ - + /* big-endian block number */ memset (&k[salt_len], 0, 3); k[salt_len + 3] = (char) b; @@ -612,7 +614,7 @@ static void derive_u_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len } void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen) -{ +{ int b, l, r; hmac_ripemd160_ctx hmac; RMD160_CTX* ctx; @@ -621,7 +623,7 @@ void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, uin unsigned char tk[RIPEMD160_DIGESTSIZE]; /* If the password is longer than the hash algorithm block size, let password = ripemd160(password), as per HMAC specifications. */ - if (pwd_len > RIPEMD160_BLOCKSIZE) + if (pwd_len > RIPEMD160_BLOCKSIZE) { RMD160_CTX tctx; @@ -652,7 +654,7 @@ void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, uin /* start out by storing key in pads */ memset(k_pad, 0x36, 64); /* XOR key with ipad and opad values */ - for (b=0; b<pwd_len; b++) + for (b=0; b<pwd_len; b++) { k_pad[b] ^= pwd[b]; } @@ -663,7 +665,7 @@ void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, uin /* perform outer RIPEMD-160 */ ctx = &(hmac.outer_digest_ctx); memset(k_pad, 0x5c, 64); - for (b=0; b<pwd_len; b++) + for (b=0; b<pwd_len; b++) { k_pad[b] ^= pwd[b]; } @@ -718,7 +720,7 @@ void hmac_whirlpool_internal memcpy (ctx, &(hmac->inner_digest_ctx), sizeof (WHIRLPOOL_CTX)); - WHIRLPOOL_add ((unsigned char *) d, ld * 8, ctx); + WHIRLPOOL_add ((unsigned char *) d, ld, ctx); WHIRLPOOL_finalize (ctx, (unsigned char *) d); @@ -726,7 +728,7 @@ void hmac_whirlpool_internal memcpy (ctx, &(hmac->outer_digest_ctx), sizeof (WHIRLPOOL_CTX)); - WHIRLPOOL_add ((unsigned char *) d, WHIRLPOOL_DIGESTSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) d, WHIRLPOOL_DIGESTSIZE, ctx); WHIRLPOOL_finalize (ctx, (unsigned char *) d); } @@ -744,6 +746,12 @@ void hmac_whirlpool char* buf = hmac.k; int b; char key[WHIRLPOOL_DIGESTSIZE]; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasISSE()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif /* If the key is longer than the hash algorithm block size, let key = whirlpool(key), as per HMAC specifications. */ if (lk > WHIRLPOOL_BLOCKSIZE) @@ -751,7 +759,7 @@ void hmac_whirlpool WHIRLPOOL_CTX tctx; WHIRLPOOL_init (&tctx); - WHIRLPOOL_add ((unsigned char *) k, lk * 8, &tctx); + WHIRLPOOL_add ((unsigned char *) k, lk, &tctx); WHIRLPOOL_finalize (&tctx, (unsigned char *) key); k = key; @@ -770,7 +778,7 @@ void hmac_whirlpool buf[b] = (char) (k[b] ^ 0x36); memset (&buf[lk], 0x36, WHIRLPOOL_BLOCKSIZE - lk); - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE, ctx); /**** Precompute HMAC Outer Digest ****/ @@ -781,9 +789,14 @@ void hmac_whirlpool buf[b] = (char) (k[b] ^ 0x5C); memset (&buf[lk], 0x5C, WHIRLPOOL_BLOCKSIZE - lk); - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE, ctx); hmac_whirlpool_internal(k, lk, d, ld, &hmac); + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && HasISSE()) + KeRestoreFloatingPointState (&floatingPointState); +#endif /* Prevent leaks */ burn(&hmac, sizeof(hmac)); } @@ -797,7 +810,7 @@ static void derive_u_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len /* iteration 1 */ memcpy (k, salt, salt_len); /* salt */ /* big-endian block number */ - memset (&k[salt_len], 0, 3); + memset (&k[salt_len], 0, 3); k[salt_len + 3] = (char) b; hmac_whirlpool_internal (pwd, pwd_len, k, salt_len + 4, hmac); @@ -821,6 +834,12 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin char* buf = hmac.k; char key[WHIRLPOOL_DIGESTSIZE]; int b, l, r; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasISSE()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif /* If the password is longer than the hash algorithm block size, let pwd = whirlpool(pwd), as per HMAC specifications. */ if (pwd_len > WHIRLPOOL_BLOCKSIZE) @@ -828,7 +847,7 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin WHIRLPOOL_CTX tctx; WHIRLPOOL_init (&tctx); - WHIRLPOOL_add ((unsigned char *) pwd, pwd_len * 8, &tctx); + WHIRLPOOL_add ((unsigned char *) pwd, pwd_len, &tctx); WHIRLPOOL_finalize (&tctx, (unsigned char *) key); pwd = key; @@ -858,7 +877,7 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin buf[b] = (char) (pwd[b] ^ 0x36); memset (&buf[pwd_len], 0x36, WHIRLPOOL_BLOCKSIZE - pwd_len); - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE, ctx); /**** Precompute HMAC Outer Digest ****/ @@ -869,7 +888,7 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin buf[b] = (char) (pwd[b] ^ 0x5C); memset (&buf[pwd_len], 0x5C, WHIRLPOOL_BLOCKSIZE - pwd_len); - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE, ctx); /* first l - 1 blocks */ for (b = 1; b < l; b++) @@ -883,6 +902,10 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin derive_u_whirlpool (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); memcpy (dk, hmac.u, r); +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && HasISSE()) + KeRestoreFloatingPointState (&floatingPointState); +#endif /* Prevent possible leaks. */ burn (&hmac, sizeof(hmac)); @@ -890,23 +913,242 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin } +typedef struct hmac_streebog_ctx_struct +{ + STREEBOG_CTX ctx; + STREEBOG_CTX inner_digest_ctx; /*pre-computed inner digest context */ + STREEBOG_CTX outer_digest_ctx; /*pre-computed outer digest context */ + CRYPTOPP_ALIGN_DATA(16) char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the Streebog hash */ + char u[STREEBOG_DIGESTSIZE]; +} hmac_streebog_ctx; + +void hmac_streebog_internal +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* input/output data. d pointer is guaranteed to be at least 64-bytes long */ + int ld, /* length of input data in bytes */ + hmac_streebog_ctx* hmac /* HMAC-Whirlpool context which holds temporary variables */ +) +{ + STREEBOG_CTX* ctx = &(hmac->ctx); + + /**** Restore Precomputed Inner Digest Context ****/ + + memcpy (ctx, &(hmac->inner_digest_ctx), sizeof (STREEBOG_CTX)); + + STREEBOG_add (ctx, (unsigned char *) d, ld); + + STREEBOG_finalize (ctx, (unsigned char *) d); + + /**** Restore Precomputed Outer Digest Context ****/ + + memcpy (ctx, &(hmac->outer_digest_ctx), sizeof (STREEBOG_CTX)); + + STREEBOG_add (ctx, (unsigned char *) d, STREEBOG_DIGESTSIZE); + + STREEBOG_finalize (ctx, (unsigned char *) d); +} + +void hmac_streebog +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* input data. d pointer is guaranteed to be at least 32-bytes long */ + int ld /* length of data in bytes */ +) +{ + hmac_streebog_ctx hmac; + STREEBOG_CTX* ctx; + char* buf = hmac.k; + int b; + CRYPTOPP_ALIGN_DATA(16) char key[STREEBOG_DIGESTSIZE]; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasSSE2() || HasSSE41()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + /* If the key is longer than the hash algorithm block size, + let key = streebog(key), as per HMAC specifications. */ + if (lk > STREEBOG_BLOCKSIZE) + { + STREEBOG_CTX tctx; + + STREEBOG_init (&tctx); + STREEBOG_add (&tctx, (unsigned char *) k, lk); + STREEBOG_finalize (&tctx, (unsigned char *) key); + + k = key; + lk = STREEBOG_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } + + /**** Precompute HMAC Inner Digest ****/ + + ctx = &(hmac.inner_digest_ctx); + STREEBOG_init (ctx); + + /* Pad the key for inner digest */ + for (b = 0; b < lk; ++b) + buf[b] = (char) (k[b] ^ 0x36); + memset (&buf[lk], 0x36, STREEBOG_BLOCKSIZE - lk); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /**** Precompute HMAC Outer Digest ****/ + + ctx = &(hmac.outer_digest_ctx); + STREEBOG_init (ctx); + + for (b = 0; b < lk; ++b) + buf[b] = (char) (k[b] ^ 0x5C); + memset (&buf[lk], 0x5C, STREEBOG_BLOCKSIZE - lk); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + hmac_streebog_internal(k, lk, d, ld, &hmac); + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + /* Prevent leaks */ + burn(&hmac, sizeof(hmac)); +} + +static void derive_u_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, int b, hmac_streebog_ctx* hmac) +{ + char* u = hmac->u; + char* k = hmac->k; + uint32 c, i; + + /* iteration 1 */ + memcpy (k, salt, salt_len); /* salt */ + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_streebog_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, STREEBOG_DIGESTSIZE); + + /* remaining iterations */ + for (c = 1; c < iterations; c++) + { + hmac_streebog_internal (pwd, pwd_len, k, STREEBOG_DIGESTSIZE, hmac); + for (i = 0; i < STREEBOG_DIGESTSIZE; i++) + { + u[i] ^= k[i]; + } + } +} + +void derive_key_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen) +{ + hmac_streebog_ctx hmac; + STREEBOG_CTX* ctx; + char* buf = hmac.k; + char key[STREEBOG_DIGESTSIZE]; + int b, l, r; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasSSE2() || HasSSE41()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + /* If the password is longer than the hash algorithm block size, + let pwd = streebog(pwd), as per HMAC specifications. */ + if (pwd_len > STREEBOG_BLOCKSIZE) + { + STREEBOG_CTX tctx; + + STREEBOG_init (&tctx); + STREEBOG_add (&tctx, (unsigned char *) pwd, pwd_len); + STREEBOG_finalize (&tctx, (unsigned char *) key); + + pwd = key; + pwd_len = STREEBOG_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } + + if (dklen % STREEBOG_DIGESTSIZE) + { + l = 1 + dklen / STREEBOG_DIGESTSIZE; + } + else + { + l = dklen / STREEBOG_DIGESTSIZE; + } + + r = dklen - (l - 1) * STREEBOG_DIGESTSIZE; + + /**** Precompute HMAC Inner Digest ****/ + + ctx = &(hmac.inner_digest_ctx); + STREEBOG_init (ctx); + + /* Pad the key for inner digest */ + for (b = 0; b < pwd_len; ++b) + buf[b] = (char) (pwd[b] ^ 0x36); + memset (&buf[pwd_len], 0x36, STREEBOG_BLOCKSIZE - pwd_len); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /**** Precompute HMAC Outer Digest ****/ + + ctx = &(hmac.outer_digest_ctx); + STREEBOG_init (ctx); + + for (b = 0; b < pwd_len; ++b) + buf[b] = (char) (pwd[b] ^ 0x5C); + memset (&buf[pwd_len], 0x5C, STREEBOG_BLOCKSIZE - pwd_len); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /* first l - 1 blocks */ + for (b = 1; b < l; b++) + { + derive_u_streebog (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, STREEBOG_DIGESTSIZE); + dk += STREEBOG_DIGESTSIZE; + } + + /* last block */ + derive_u_streebog (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + + /* Prevent possible leaks. */ + burn (&hmac, sizeof(hmac)); + burn (key, sizeof(key)); +} + wchar_t *get_pkcs5_prf_name (int pkcs5_prf_id) { switch (pkcs5_prf_id) { - case SHA512: + case SHA512: return L"HMAC-SHA-512"; - case SHA256: + case SHA256: return L"HMAC-SHA-256"; - case RIPEMD160: + case RIPEMD160: return L"HMAC-RIPEMD-160"; - case WHIRLPOOL: + case WHIRLPOOL: return L"HMAC-Whirlpool"; - default: + case STREEBOG: + return L"HMAC-STREEBOG"; + + default: return L"(Unknown)"; } } @@ -925,7 +1167,7 @@ int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BO switch (pkcs5_prf_id) { - case RIPEMD160: + case RIPEMD160: if (truecryptMode) return bBoot ? 1000 : 2000; else if (pim == 0) @@ -935,10 +1177,10 @@ int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BO return bBoot? pim * 2048 : 15000 + pim * 1000; } - case SHA512: + case SHA512: return truecryptMode? 1000 : ((pim == 0)? 500000 : 15000 + pim * 1000); - case WHIRLPOOL: + case WHIRLPOOL: return truecryptMode? 1000 : ((pim == 0)? 500000 : 15000 + pim * 1000); case SHA256: @@ -951,7 +1193,17 @@ int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BO return bBoot? pim * 2048 : 15000 + pim * 1000; } - default: + case STREEBOG: + if (truecryptMode) + return 1000; + else if (pim == 0) + return bBoot? 200000 : 500000; + else + { + return bBoot? pim * 2048 : 15000 + pim * 1000; + } + + default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID } return 0; diff --git a/src/Common/Pkcs5.h b/src/Common/Pkcs5.h index 81d9de64..261df85d 100644 --- a/src/Common/Pkcs5.h +++ b/src/Common/Pkcs5.h @@ -37,6 +37,9 @@ void derive_key_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, uint32 void hmac_whirlpool (char *k, int lk, char *d, int ld); void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen); +void hmac_streebog (char *k, int32 lk, char *d, int32 ld); +void derive_key_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen); + int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BOOL bBoot); wchar_t *get_pkcs5_prf_name (int pkcs5_prf_id); #endif diff --git a/src/Common/Random.c b/src/Common/Random.c index 32b38e44..edc6acca 100644 --- a/src/Common/Random.c +++ b/src/Common/Random.c @@ -247,6 +247,7 @@ BOOL Randmix () RMD160_CTX rctx; sha512_ctx sctx; sha256_ctx s256ctx; + STREEBOG_CTX stctx; int poolIndex, digestIndex, digestSize; switch (HashFunction) @@ -267,6 +268,10 @@ BOOL Randmix () digestSize = WHIRLPOOL_DIGESTSIZE; break; + case STREEBOG: + digestSize = STREEBOG_DIGESTSIZE; + break; + default: TC_THROW_FATAL_EXCEPTION; } @@ -299,10 +304,16 @@ BOOL Randmix () case WHIRLPOOL: WHIRLPOOL_init (&wctx); - WHIRLPOOL_add (pRandPool, RNG_POOL_SIZE * 8, &wctx); + WHIRLPOOL_add (pRandPool, RNG_POOL_SIZE, &wctx); WHIRLPOOL_finalize (&wctx, hashOutputBuffer); break; + case STREEBOG: + STREEBOG_init (&stctx); + STREEBOG_add (&stctx, pRandPool, RNG_POOL_SIZE); + STREEBOG_finalize (&stctx, hashOutputBuffer); + break; + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; @@ -335,6 +346,10 @@ BOOL Randmix () burn (&wctx, sizeof(wctx)); break; + case STREEBOG: + burn (&stctx, sizeof(sctx)); + break; + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; diff --git a/src/Common/Resource.h b/src/Common/Resource.h index 03d42664..12907c12 100644 --- a/src/Common/Resource.h +++ b/src/Common/Resource.h @@ -59,6 +59,20 @@ #define IDR_RESCUE_LOADER_AES_SHA2 555 #define IDR_RESCUE_LOADER_SERPENT_SHA2 556 #define IDR_RESCUE_LOADER_TWOFISH_SHA2 557 +#define IDR_BOOT_SECTOR_CAMELLIA 558 +#define IDR_BOOT_LOADER_CAMELLIA 559 +#define IDR_RESCUE_BOOT_SECTOR_CAMELLIA 560 +#define IDR_RESCUE_LOADER_CAMELLIA 561 +#define IDR_BOOT_SECTOR_CAMELLIA_SHA2 562 +#define IDR_BOOT_LOADER_CAMELLIA_SHA2 563 +#define IDR_RESCUE_BOOT_SECTOR_CAMELLIA_SHA2 564 +#define IDR_RESCUE_LOADER_CAMELLIA_SHA2 565 +#define IDR_EFI_DCSBOOT 566 +#define IDR_EFI_DCSINT 567 +#define IDR_EFI_DCSCFG 568 +#define IDR_EFI_LEGACYSPEAKER 569 +#define IDR_EFI_DCSBML 570 +#define IDR_EFI_DCSRE 571 #define IDC_HW_AES_LABEL_LINK 5000 #define IDC_HW_AES 5001 #define IDC_PARALLELIZATION_LABEL_LINK 5002 @@ -197,15 +211,18 @@ #define IDC_KEYFILES_TRY_EMPTY_PASSWORD 5135 #define IDC_ENTROPY_BAR 5136 #define IDT_ENTROPY_BAR 5137 +#define IDT_BENCHMARK 5138 +#define IDC_BENCHMARK_LIST 5139 +#define IDC_BENCHMARK_PREBOOT 5140 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 558 +#define _APS_NEXT_RESOURCE_VALUE 572 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 5138 +#define _APS_NEXT_CONTROL_VALUE 5141 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/Common/SecurityToken.cpp b/src/Common/SecurityToken.cpp index 78a3e6c1..05defe5b 100644 --- a/src/Common/SecurityToken.cpp +++ b/src/Common/SecurityToken.cpp @@ -36,7 +36,7 @@ using namespace std; namespace VeraCrypt { - SecurityTokenKeyfile::SecurityTokenKeyfile (const SecurityTokenKeyfilePath &path, char* pin) + SecurityTokenKeyfile::SecurityTokenKeyfile (const SecurityTokenKeyfilePath &path) { wstring pathStr = path; unsigned long slotId; @@ -52,7 +52,7 @@ namespace VeraCrypt Id = pathStr.substr (keyIdPos + wstring (L"/" TC_SECURITY_TOKEN_KEYFILE_URL_FILE L"/").size()); - vector <SecurityTokenKeyfile> keyfiles = SecurityToken::GetAvailableKeyfiles (&SlotId, Id, pin); + vector <SecurityTokenKeyfile> keyfiles = SecurityToken::GetAvailableKeyfiles (&SlotId, Id); if (keyfiles.empty()) throw SecurityTokenKeyfileNotFound(); @@ -180,7 +180,7 @@ namespace VeraCrypt throw Pkcs11Exception (status); } - vector <SecurityTokenKeyfile> SecurityToken::GetAvailableKeyfiles (CK_SLOT_ID *slotIdFilter, const wstring keyfileIdFilter, char* pin) + vector <SecurityTokenKeyfile> SecurityToken::GetAvailableKeyfiles (CK_SLOT_ID *slotIdFilter, const wstring keyfileIdFilter) { bool unrecognizedTokenPresent = false; vector <SecurityTokenKeyfile> keyfiles; @@ -194,7 +194,7 @@ namespace VeraCrypt try { - LoginUserIfRequired (slotId, pin); + LoginUserIfRequired (slotId); token = GetTokenInfo (slotId); } catch (UserAbort &) @@ -314,12 +314,7 @@ namespace VeraCrypt void SecurityToken::GetKeyfileData (const SecurityTokenKeyfile &keyfile, vector <byte> &keyfileData) { - GetKeyfileData (keyfile, nullptr, keyfileData); - } - - void SecurityToken::GetKeyfileData (const SecurityTokenKeyfile &keyfile, char* pin, vector <byte> &keyfileData) - { - LoginUserIfRequired (keyfile.SlotId, pin); + LoginUserIfRequired (keyfile.SlotId); GetObjectAttribute (keyfile.SlotId, keyfile.Handle, CKA_VALUE, keyfileData); } @@ -438,7 +433,7 @@ namespace VeraCrypt Sessions[slotId].UserLoggedIn = true; } - void SecurityToken::LoginUserIfRequired (CK_SLOT_ID slotId, char* cmdPin) + void SecurityToken::LoginUserIfRequired (CK_SLOT_ID slotId) { CheckLibraryStatus(); CK_RV status; @@ -479,10 +474,6 @@ namespace VeraCrypt if (status != CKR_OK) throw Pkcs11Exception (status); } - else if (cmdPin && cmdPin [0]) - { - Login (slotId, cmdPin); - } else { string pin = tokenInfo.LabelUtf8; @@ -511,12 +502,7 @@ namespace VeraCrypt } else if (error == CKR_PIN_INCORRECT && !(tokenInfo.Flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { - if (cmdPin && cmdPin [0]) - { - // clear wrong PIN - size_t cmdPinLen = strlen (cmdPin); - burn (cmdPin, cmdPinLen); - } + PinCallback->notifyIncorrectPin (); (*WarningCallback) (Pkcs11Exception (CKR_PIN_INCORRECT)); continue; } diff --git a/src/Common/SecurityToken.h b/src/Common/SecurityToken.h index 95d95fc8..32abfcb5 100644 --- a/src/Common/SecurityToken.h +++ b/src/Common/SecurityToken.h @@ -74,7 +74,7 @@ namespace VeraCrypt struct SecurityTokenKeyfile { SecurityTokenKeyfile () : Handle(CK_INVALID_HANDLE), SlotId(CK_UNAVAILABLE_INFORMATION) { Token.SlotId = CK_UNAVAILABLE_INFORMATION; Token.Flags = 0; } - SecurityTokenKeyfile (const SecurityTokenKeyfilePath &path, char* pin = nullptr); + SecurityTokenKeyfile (const SecurityTokenKeyfilePath &path); operator SecurityTokenKeyfilePath () const; @@ -170,6 +170,7 @@ namespace VeraCrypt { virtual ~GetPinFunctor () { } virtual void operator() (string &str) = 0; + virtual void notifyIncorrectPin () = 0; }; struct SendExceptionFunctor @@ -185,9 +186,8 @@ namespace VeraCrypt static void CloseLibrary (); static void CreateKeyfile (CK_SLOT_ID slotId, vector <byte> &keyfileData, const string &name); static void DeleteKeyfile (const SecurityTokenKeyfile &keyfile); - static vector <SecurityTokenKeyfile> GetAvailableKeyfiles (CK_SLOT_ID *slotIdFilter = nullptr, const wstring keyfileIdFilter = wstring(), char* pin = nullptr); + static vector <SecurityTokenKeyfile> GetAvailableKeyfiles (CK_SLOT_ID *slotIdFilter = nullptr, const wstring keyfileIdFilter = wstring()); static void GetKeyfileData (const SecurityTokenKeyfile &keyfile, vector <byte> &keyfileData); - static void GetKeyfileData (const SecurityTokenKeyfile &keyfile, char* pin, vector <byte> &keyfileData); static list <SecurityTokenInfo> GetAvailableTokens (); static SecurityTokenInfo GetTokenInfo (CK_SLOT_ID slotId); #ifdef TC_WINDOWS @@ -206,7 +206,7 @@ namespace VeraCrypt static void GetObjectAttribute (CK_SLOT_ID slotId, CK_OBJECT_HANDLE tokenObject, CK_ATTRIBUTE_TYPE attributeType, vector <byte> &attributeValue); static list <CK_SLOT_ID> GetTokenSlots (); static void Login (CK_SLOT_ID slotId, const char* pin); - static void LoginUserIfRequired (CK_SLOT_ID slotId, char* cmdPin = nullptr); + static void LoginUserIfRequired (CK_SLOT_ID slotId); static void OpenSession (CK_SLOT_ID slotId); static void CheckLibraryStatus (); diff --git a/src/Common/Tcdefs.h b/src/Common/Tcdefs.h index 2d8a6fe3..a58df2e3 100644 --- a/src/Common/Tcdefs.h +++ b/src/Common/Tcdefs.h @@ -14,18 +14,56 @@ #ifndef TCDEFS_H #define TCDEFS_H +#if defined(_UEFI) +#undef _WIN32 +#undef _WIN64 +#undef _DEBUG + +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> + +void* VeraCryptMemAlloc(IN UINTN size); +void VeraCryptMemFree(IN VOID* ptr); + +#define BOOL int +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define max(a,b) ((a)>(b))?(a):(b) +#define min(a,b) ((a)<(b))?(a):(b) + +#ifdef __cplusplus +extern "C" { +#endif +extern unsigned __int64 __cdecl _rotl64(unsigned __int64,int); +extern unsigned __int64 __cdecl _rotr64(unsigned __int64,int); +extern unsigned int __cdecl _rotl(unsigned int,int); +extern unsigned int __cdecl _rotr(unsigned int,int); +extern unsigned char _rotr8(unsigned char value, unsigned char shift); +extern unsigned short _rotr16(unsigned short value, unsigned char shift); +extern unsigned char _rotl8(unsigned char value, unsigned char shift); +extern unsigned short _rotl16(unsigned short value, unsigned char shift); +#ifdef __cplusplus +} +#endif + +#endif // defined(_UEFI) + #define TC_APP_NAME "VeraCrypt" -// Version displayed to user -#define VERSION_STRING "1.17" +// Version displayed to user +#define VERSION_STRING "1.18-BETA9" // Version number to compare against driver #define VERSION_NUM 0x0117 // Release date -#define TC_STR_RELEASE_DATE L"February 13th, 2016" +#define TC_STR_RELEASE_DATE L"May 29th, 2016" #define TC_RELEASE_DATE_YEAR 2016 -#define TC_RELEASE_DATE_MONTH 2 +#define TC_RELEASE_DATE_MONTH 5 #define BYTES_PER_KB 1024LL #define BYTES_PER_MB 1048576LL @@ -55,6 +93,9 @@ typedef unsigned __int64 uint64; #define LL(x) x##ui64 #endif +#pragma warning( disable : 4201 ) // disable: 4201 nonstandard extension used : nameless struct/union +#pragma warning( disable : 4324 ) // disable: 4324 structure was padded due to __declspec(align()) + #else // !_MSC_VER #include <inttypes.h> @@ -123,6 +164,41 @@ typedef union #define __has_builtin(x) 0 // Compatibility with non-clang compilers #endif +#if defined(_UEFI) +typedef UINTN size_t; +typedef uint64 uint_64t; +typedef CHAR16 wchar_t; +typedef int LONG; + +#define wcscpy StrCpy +#define wcslen StrLen +#define wcscmp StrCmp +#define wcscat StrCat + +#define memcpy(dest,source,count) CopyMem(dest,source,(UINTN)(count)) +#define memset(dest,ch,count) SetMem(dest,(UINTN)(count),(UINT8)(ch)) +#define memchr(buf,ch,count) ScanMem8(buf,(UINTN)(count),(UINT8)ch) +#define memcmp(buf1,buf2,count) (int)(CompareMem(buf1,buf2,(UINTN)(count))) + +#define MAX_STRING_SIZE 0x1000 +#define strcat(strDest,strSource) AsciiStrCatS(strDest,MAX_STRING_SIZE,strSource) +#define strchr(str,ch) ScanMem8((VOID *)(str),AsciiStrSize(str),(UINT8)ch) +#define strcmp AsciiStrCmp +#define strncmp(string1,string2,count) (int)(AsciiStrnCmp(string1,string2,(UINTN)(count))) +#define strcpy(strDest,strSource) AsciiStrCpyS(strDest,MAX_STRING_SIZE,strSource) +#define strncpy(strDest,strSource,count) AsciiStrnCpyS(strDest,MAX_STRING_SIZE,strSource,(UINTN)count) +#define strlen(str) (size_t)(AsciiStrnLenS(str,MAX_STRING_SIZE)) +#define strstr AsciiStrStr + +// #define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) +// #define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) +// #define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) +// #define rotl64(x,n) (((x) << n) | ((x) >> (64 - n))) +// #define bswap_32(x) (rotl32((((x) & 0xFF00FF00) >> 8) | (((x) & 0x00FF00FF) << 8), 16U)) +// #define bswap_64(x) rotl64(((((((x & LL(0xFF00FF00FF00FF00)) >> 8) | ((x & LL(0x00FF00FF00FF00FF)) << 8)) & LL(0xFFFF0000FFFF0000)) >> 16) | (((((x & LL(0xFF00FF00FF00FF00)) >> 8) | ((x & LL(0x00FF00FF00FF00FF)) << 8)) & LL(0x0000FFFF0000FFFF)) << 16)), 32U) + +#endif + #ifdef TC_WINDOWS_BOOT # ifdef __cplusplus @@ -133,6 +209,9 @@ void ThrowFatalException (int line); # define TC_THROW_FATAL_EXCEPTION ThrowFatalException (__LINE__) #elif defined (TC_WINDOWS_DRIVER) # define TC_THROW_FATAL_EXCEPTION KeBugCheckEx (SECURITY_SYSTEM, __LINE__, 0, 0, 'VC') +#elif defined(_UEFI) +void ThrowFatalException(int line); +# define TC_THROW_FATAL_EXCEPTION ThrowFatalException (__LINE__) #elif (defined(__clang__) && __has_builtin(__builtin_trap)) \ || (defined(__GNUC__ ) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) \ || (__has_builtin(__builtin_trap)) @@ -170,9 +249,13 @@ typedef int BOOL; #endif #else /* !TC_WINDOWS_DRIVER */ - +#if !defined(_UEFI) #define TCalloc malloc #define TCfree free +#else +#define TCalloc VeraCryptMemAlloc +#define TCfree VeraCryptMemFree +#endif //!defined(_UEFI) #ifdef _WIN32 @@ -198,8 +281,8 @@ typedef int BOOL; #endif #ifdef DEVICE_DRIVER -# if defined (DEBUG) || 0 -# if 1 // DbgPrintEx is not available on Windows 2000 +# if defined (DEBUG) || defined (DEBUG_TRACE) +# if 0 // DbgPrintEx is not available on Windows 2000 # define Dump DbgPrint # else # define Dump(...) DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__) @@ -212,11 +295,13 @@ typedef int BOOL; #endif #if !defined (trace_msg) && !defined (TC_WINDOWS_BOOT) -# ifdef DEBUG +# if defined(DEBUG) || defined (DEBUG_TRACE) # ifdef DEVICE_DRIVER # define trace_msg Dump # elif defined (_WIN32) # define trace_msg(...) do { char msg[2048]; StringCbPrintfA (msg, sizeof (msg), __VA_ARGS__); OutputDebugString (msg); } while (0) +# else +# define trace_msg(...) # endif # define trace_point trace_msg (__FUNCTION__ ":" TC_TO_STRING(__LINE__) "\n") # else @@ -233,7 +318,7 @@ typedef int BOOL; # define TC_WAIT_EVENT(EVENT) WaitForSingleObject (EVENT, INFINITE) #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UEFI) #define burn(mem,size) do { volatile char *burnm = (volatile char *)(mem); size_t burnc = size; RtlSecureZeroMemory (mem, size); while (burnc--) *burnm++ = 0; } while (0) #else #define burn(mem,size) do { volatile char *burnm = (volatile char *)(mem); int burnc = size; while (burnc--) *burnm++ = 0; } while (0) @@ -251,7 +336,7 @@ typedef int BOOL; # define max(a,b) (((a) > (b)) ? (a) : (b)) # endif -# ifdef __cplusplus +# if defined(__cplusplus) && !defined(_UEFI) extern "C" # endif void EraseMemory (void *memory, int size); diff --git a/src/Common/Tests.c b/src/Common/Tests.c index 9d9e8118..c51b6338 100644 --- a/src/Common/Tests.c +++ b/src/Common/Tests.c @@ -19,10 +19,11 @@ #include "Xts.h" #include <string.h> #include "Pkcs5.h" +#include "cpu.h" typedef struct { - unsigned __int8 key1[32]; - unsigned __int8 key2[32]; + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 key1[32]; + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 key2[32]; unsigned __int8 dataUnitNo[8]; unsigned int blockNo; unsigned __int8 plaintext[ENCRYPTION_DATA_UNIT_SIZE]; @@ -348,6 +349,35 @@ TWOFISH_TEST twofish_vectors[TWOFISH_TEST_COUNT] = { 0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA }; +// Camellia ECB test vectors +#define CAMELLIA_TEST_COUNT 2 + +typedef struct { + unsigned char key[32]; + unsigned char plaintext[16]; + unsigned char ciphertext[16]; + } CAMELLIA_TEST; + +CAMELLIA_TEST camellia_vectors[CAMELLIA_TEST_COUNT] = { +{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, + 0x76, 0x54, 0x32, 0x10, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x9A, 0xCC, 0x23, 0x7D, 0xFF, 0x16, 0xD7, 0x6C, 0x20, 0xEF, 0x7C, 0x91, + 0x9E, 0x3A, 0x75, 0x09 +}, +{ + 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, + 0x48, 0x81, 0xFF, 0x48, 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, + 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48, 0xE6, 0x84, 0x42, 0x17, + 0x16, 0xFC, 0x0B, 0x01, 0xAE, 0xB5, 0xC6, 0x76, 0x51, 0x20, 0xF9, 0x5F, + 0xEA, 0x02, 0x47, 0x14, 0xAD, 0x5C, 0x4D, 0x84, 0xEA, 0x02, 0x47, 0x14, + 0xAD, 0x5C, 0x4D, 0x84 +} +}; + + /* Test vectors from FIPS 198a, RFC 4231, RFC 2104, RFC 2202, and other sources. */ char *hmac_sha256_test_keys[] = @@ -446,6 +476,95 @@ char *hmac_whirlpool_test_vectors = "\x6a\xbf\xa4\x02" }; +typedef struct _HashTestVector +{ + const char* hexInput; + const char* hexOutput; + +} HashTestVector; + +typedef int (__cdecl HashFunction) (unsigned char* input, unsigned long inputLen, unsigned char* output); + +unsigned char HexCharToByte (char c) +{ + if (c >= ('0') && c <= ('9')) + return c - ('0'); + else if (c >= ('A') && c <= ('F')) + return c - ('A') + 10; + else if (c >= ('a') && c <= ('f')) + return c - ('a') + 10; + else + return 0xFF; +} + +unsigned long HexStringToByteArray(const char* hexStr, unsigned char* pbData) +{ + unsigned long count = 0; + while (*hexStr) + { + *pbData++ = (HexCharToByte(hexStr[0]) << 4) | HexCharToByte(hexStr[1]); + hexStr += 2; + count++; + } + return count; +} + +BOOL RunHashTest (HashFunction fn, HashTestVector* vector, BOOL bUseSSE) +{ + ALIGN (16) unsigned char input[256]; + unsigned char output[64]; + unsigned char digest[64]; + unsigned long i = 0, inputLen, outputLen, digestLen; + BOOL bRet = TRUE; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (bUseSSE && (HasSSE2() || HasSSE41())) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + while (vector[i].hexInput && vector[i].hexOutput) + { + inputLen = HexStringToByteArray (vector[i].hexInput, input); + outputLen = HexStringToByteArray (vector[i].hexOutput, output); + digestLen = fn (input, inputLen, digest); + if ((digestLen != outputLen) || (0 != memcmp (digest, output, digestLen))) + { + bRet = FALSE; + break; + } + i++; + } + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && bUseSSE && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + + return bRet; +} + + +/* https://www.streebog.net/src/trunk/examples/ */ +HashTestVector Streebog512TestVectors[] = { + /* M1 */ + {"303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132", + "1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48" + }, + /* M2 */ + {"d1e520e2e5f2f0e82c20d1f2f0e8e1eee6e820e2edf3f6e82c20e2e5fef2fa20f120eceef0ff20f1f2f0e5ebe0ece820ede020f5f0e0e1f0fbff20efebfaeafb20c8e3eef0e5e2fb", + "1e88e62226bfca6f9994f1f2d51569e0daf8475a3b0fe61a5300eee46d961376035fe83549ada2b8620fcd7c496ce5b33f0cb9dddc2b6460143b03dabac9fb28" + }, + /* M3 */ + {"", + "8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a" + }, + /* M4 */ + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "b0fd29ac1b0df441769ff3fdb8dc564df67721d6ac06fb28ceffb7bbaa7948c6c014ac999235b58cb26fb60fb112a145d7b4ade9ae566bf2611402c552d20db7" + }, + {NULL, NULL} +}; + unsigned char ks_tmp[MAX_EXPANDED_KEY]; void CipherInit2(int cipher, void* key, void* ks, int key_len) @@ -464,7 +583,18 @@ void CipherInit2(int cipher, void* key, void* ks, int key_len) case TWOFISH: CipherInit(cipher,key,ks); break; - + + case CAMELLIA: + CipherInit(cipher,key,ks); + break; +#if defined(CIPHER_GOST89) + case GOST89: + CipherInit(cipher,key,ks); + break; +#endif // defined(CIPHER_GOST89) + case KUZNYECHIK: + CipherInit(cipher, key, ks); + break; default: /* Unknown/wrong ID */ TC_THROW_FATAL_EXCEPTION; @@ -484,7 +614,7 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) int testCase = 0; int nTestsPerformed = 0; - static unsigned char key1[] = + static CRYPTOPP_ALIGN_DATA(16) unsigned char key1[] = { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26, 0x62, 0x49, 0x77, 0x57, 0x24, 0x70, 0x93, 0x69, 0x99, 0x59, 0x57, 0x49, 0x66, 0x96, 0x76, 0x27, 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95, 0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37, 0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92, @@ -625,6 +755,32 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) break; } } + else if (wcscmp (name, L"Camellia") == 0) + { + switch (testCase) + { + case 0: + if (crc != 0x2436badb) + return FALSE; + nTestsPerformed++; + break; + case 1: + if (crc != 0x247d2272) + return FALSE; + nTestsPerformed++; + break; + case 2: + if (crc != 0x72b49cde) + return FALSE; + nTestsPerformed++; + break; + case 3: + if (crc != 0xb838d2c1) + return FALSE; + nTestsPerformed++; + break; + } + } else if (wcscmp (name, L"AES-Twofish") == 0) { switch (testCase) @@ -819,6 +975,12 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) return FALSE; nTestsPerformed++; } + else if (wcscmp (name, L"Camellia") == 0) + { + if (crc != 0x8176b223) + return FALSE; + nTestsPerformed++; + } else if (wcscmp (name, L"AES-Twofish") == 0) { if (crc != 0x14ce7385) @@ -860,14 +1022,17 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) nTestsPerformed++; } - - return (nTestsPerformed == 80); +#if defined(CIPHER_GOST89) + return (nTestsPerformed == 100); +#else + return (nTestsPerformed == 95); +#endif } static BOOL DoAutoTestAlgorithms (void) { PCRYPTO_INFO ci; - char key[32]; + CRYPTOPP_ALIGN_DATA(16) char key[32]; unsigned char tmp[16]; BOOL bFailed = FALSE; int i; @@ -963,6 +1128,26 @@ static BOOL DoAutoTestAlgorithms (void) } if (i != TWOFISH_TEST_COUNT) bFailed = TRUE; + + /* Camellia */ + + for (i = 0; i < CAMELLIA_TEST_COUNT; i++) + { + int cipher = CAMELLIA; + memcpy(key, camellia_vectors[i].key, 32); + memcpy(tmp, camellia_vectors[i].plaintext, 16); + CipherInit(cipher, key, ks_tmp); + + EncipherBlock(cipher, tmp, ks_tmp); + if (memcmp(camellia_vectors[i].ciphertext, tmp, 16) != 0) + break; + + DecipherBlock(cipher, tmp, ks_tmp); + if (memcmp(camellia_vectors[i].plaintext, tmp, 16) != 0) + break; + } + if (i != CAMELLIA_TEST_COUNT) + bFailed = TRUE; /* PKCS #5 and HMACs */ @@ -1080,6 +1265,50 @@ BOOL test_hmac_whirlpool () return TRUE; } +/* http://www.tc26.ru/methods/recommendation/%D0%A2%D0%9A26%D0%90%D0%9B%D0%93.pdf */ +/* https://tools.ietf.org/html/draft-smyshlyaev-gost-usage-00 */ +/* https://datatracker.ietf.org/doc/rfc7836/?include_text=1 */ +static const unsigned char gost3411_2012_hmac_k1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; +static const unsigned char gost3411_2012_hmac_m1[] = { + 0x01, 0x26, 0xbd, 0xb8, 0x78, 0x00, 0xaf, 0x21, + 0x43, 0x41, 0x45, 0x65, 0x63, 0x78, 0x01, 0x00 +}; +static const unsigned char gost3411_2012_hmac_r1[] = { + 0xA5, 0x9B, 0xAB, 0x22, 0xEC, 0xAE, 0x19, 0xC6, 0x5F, 0xBD, 0xE6, 0xE5, + 0xF4, 0xE9, 0xF5, 0xD8, 0x54, 0x9D, 0x31, 0xF0, 0x37, 0xF9, 0xDF, 0x9B, + 0x90, 0x55, 0x00, 0xE1, 0x71, 0x92, 0x3A, 0x77, 0x3D, 0x5F, 0x15, 0x30, + 0xF2, 0xED, 0x7E, 0x96, 0x4C, 0xB2, 0xEE, 0xDC, 0x29, 0xE9, 0xAD, 0x2F, + 0x3A, 0xFE, 0x93, 0xB2, 0x81, 0x4F, 0x79, 0xF5, 0x00, 0x0F, 0xFC, 0x03, + 0x66, 0xC2, 0x51, 0xE6 +}; + + +BOOL test_hmac_streebog () +{ + ALIGN(16) char digest[64]; /* large enough to hold digets and test vector inputs */ + + memcpy (digest, gost3411_2012_hmac_m1, sizeof (gost3411_2012_hmac_m1)); + hmac_streebog ((char*) gost3411_2012_hmac_k1, sizeof(gost3411_2012_hmac_k1), digest, (int) sizeof (gost3411_2012_hmac_m1)); + if (memcmp (digest, gost3411_2012_hmac_r1, STREEBOG_DIGESTSIZE) != 0) + return FALSE; + + return TRUE; +} + +int __cdecl StreebogHash (unsigned char* input, unsigned long inputLen, unsigned char* output) +{ + STREEBOG_CTX ctx; + STREEBOG_init (&ctx); + STREEBOG_add (&ctx, input, inputLen); + STREEBOG_finalize (&ctx, output); + return STREEBOG_DIGESTSIZE; +} + BOOL test_pkcs5 () { char dk[144]; @@ -1100,6 +1329,14 @@ BOOL test_pkcs5 () if (test_hmac_whirlpool() == FALSE) return FALSE; + /* HMAC-STREEBOG tests */ + if (test_hmac_streebog() == FALSE) + return FALSE; + + /* STREEBOG hash tests */ + if (RunHashTest (StreebogHash, Streebog512TestVectors, TRUE) == FALSE) + return FALSE; + /* PKCS-5 test 1 with HMAC-SHA-256 used as the PRF (https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00) */ derive_key_sha256 ("passwd", 6, "\x73\x61\x6C\x74", 4, 1, dk, 64); if (memcmp (dk, "\x55\xac\x04\x6e\x56\xe3\x08\x9f\xec\x16\x91\xc2\x25\x44\xb6\x05\xf9\x41\x85\x21\x6d\xde\x04\x65\xe6\x8b\x9d\x57\xc2\x0d\xac\xbc\x49\xca\x9c\xcc\xf1\x79\xb6\x45\x99\x16\x64\xb3\x9d\x77\xef\x31\x7c\x71\xb8\x45\xb1\xe3\x0b\xd5\x09\x11\x20\x41\xd3\xa1\x97\x83", 64) != 0) @@ -1151,5 +1388,15 @@ BOOL test_pkcs5 () if (memcmp (dk, "\x50\x7c\x36\x6f\xee\x10\x2e\x9a\xe2\x8a\xd5\x82\x72\x7d\x27\x0f\xe8\x4d\x7f\x68\x7a\xcf\xb5\xe7\x43\x67\xaa\x98\x93\x52\x2b\x09\x6e\x42\xdf\x2c\x59\x4a\x91\x6d\x7e\x10\xae\xb2\x1a\x89\x8f\xb9\x8f\xe6\x31\xa9\xd8\x9f\x98\x26\xf4\xda\xcd\x7d\x65\x65\xde\x10\x95\x91\xb4\x84\x26\xae\x43\xa1\x00\x5b\x1e\xb8\x38\x97\xa4\x1e\x4b\xd2\x65\x64\xbc\xfa\x1f\x35\x85\xdb\x4f\x97\x65\x6f\xbd\x24", 96) != 0) return FALSE; + /* PKCS-5 test 1 with HMAC-STREEBOG used as the PRF */ + derive_key_streebog ("password", 8, "\x12\x34\x56\x78", 4, 5, dk, 4); + if (memcmp (dk, "\xd0\x53\xa2\x30", 4) != 0) + return FALSE; + + /* PKCS-5 test 2 with HMAC-STREEBOG used as the PRF (derives a key longer than the underlying hash) */ + derive_key_streebog ("password", 8, "\x12\x34\x56\x78", 4, 5, dk, 96); + if (memcmp (dk, "\xd0\x53\xa2\x30\x6f\x45\x81\xeb\xbc\x06\x81\xc5\xe7\x53\xa8\x5d\xc7\xf1\x23\x33\x1e\xbe\x64\x2c\x3b\x0f\x26\xd7\x00\xe1\x95\xc9\x65\x26\xb1\x85\xbe\x1e\xe2\xf4\x9b\xfc\x6b\x14\x84\xda\x24\x61\xa0\x1b\x9e\x79\x5c\xee\x69\x6e\xf9\x25\xb1\x1d\xca\xa0\x31\xba\x02\x6f\x9e\x99\x0f\xdb\x25\x01\x5b\xf1\xc7\x10\x19\x53\x3b\x29\x3f\x18\x00\xd6\xfc\x85\x03\xdc\xf2\xe5\xe9\x5a\xb1\x1e\x61\xde", 96) != 0) + return FALSE; + return TRUE; } diff --git a/src/Common/Volumes.c b/src/Common/Volumes.c index 007a1c08..3228aadc 100644 --- a/src/Common/Volumes.c +++ b/src/Common/Volumes.c @@ -12,8 +12,8 @@ code distribution packages. */ #include "Tcdefs.h" - -#ifndef TC_WINDOWS_BOOT +#if !defined(_UEFI) +#if !defined(TC_WINDOWS_BOOT) #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> @@ -28,6 +28,7 @@ #ifndef DEVICE_DRIVER #include "Random.h" #endif +#endif // !defined(_UEFI) #include "Crc.h" #include "Crypto.h" @@ -35,7 +36,7 @@ #include "Volumes.h" #include "Pkcs5.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UEFI) #include <Strsafe.h> #include "../Boot/Windows/BootCommon.h" #endif @@ -171,21 +172,22 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int char header[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo; PCRYPTO_INFO cryptoInfo; - char dk[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) char dk[MASTER_KEYDATA_SIZE]; int enqPkcs5Prf, pkcs5_prf; uint16 headerVersion; int status = ERR_PARAMETER_INCORRECT; int primaryKeyOffset; - + int pkcs5PrfCount = LAST_PRF_ID - FIRST_PRF_ID + 1; +#if !defined(_UEFI) 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; +#endif + size_t queuedWorkItems = 0; // if no PIM specified, use default value if (pim < 0) @@ -212,7 +214,7 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int if (cryptoInfo == NULL) return ERR_OUTOFMEMORY; } - +#if !defined(_UEFI) /* use thread pool only if no PRF was specified */ if ((selected_pkcs5_prf == 0) && (encryptionThreadCount > 1)) { @@ -244,10 +246,11 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int #endif } -#ifndef DEVICE_DRIVER +#if !defined(DEVICE_DRIVER) VirtualLock (&keyInfo, sizeof (keyInfo)); VirtualLock (&dk, sizeof (dk)); #endif +#endif // !defined(_UEFI) crypto_loadkey (&keyInfo, password->Text, (int) password->Length); @@ -264,7 +267,7 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int // skip SHA-256 in case of TrueCrypt mode if (truecryptMode && (enqPkcs5Prf == SHA256)) continue; - +#if !defined(_UEFI) if ((selected_pkcs5_prf == 0) && (encryptionThreadCount > 1)) { // Enqueue key derivation on thread pool @@ -319,6 +322,7 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int KeyReady: ; } else +#endif // !defined(_UEFI) { pkcs5_prf = enqPkcs5Prf; keyInfo.noIterations = get_pkcs5_iteration_count (enqPkcs5Prf, pim, truecryptMode, bBoot); @@ -345,6 +349,10 @@ KeyReady: ; PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; + case STREEBOG: + derive_key_streebog(keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; @@ -564,11 +572,12 @@ ret: burn (&keyInfo, sizeof (keyInfo)); burn (dk, sizeof(dk)); -#ifndef DEVICE_DRIVER +#if !defined(DEVICE_DRIVER) && !defined(_UEFI) VirtualUnlock (&keyInfo, sizeof (keyInfo)); VirtualUnlock (&dk, sizeof (dk)); #endif +#if !defined(_UEFI) if ((selected_pkcs5_prf == 0) && (encryptionThreadCount > 1)) { TC_WAIT_EVENT (noOutstandingWorkItemEvent); @@ -576,16 +585,16 @@ ret: burn (keyDerivationWorkItems, sizeof (KeyDerivationWorkItem) * pkcs5PrfCount); TCfree (keyDerivationWorkItems); -#ifndef DEVICE_DRIVER +#if !defined(DEVICE_DRIVER) CloseHandle (keyDerivationCompletedEvent); CloseHandle (noOutstandingWorkItemEvent); #endif } - +#endif return status; } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UEFI) void ComputeBootloaderFingerprint (byte *bootLoaderBuf, unsigned int bootLoaderSize, byte* fingerprint) { // compute Whirlpool+SHA512 fingerprint of bootloader including MBR @@ -605,16 +614,16 @@ void ComputeBootloaderFingerprint (byte *bootLoaderBuf, unsigned int bootLoaderS WHIRLPOOL_init (&whirlpool); sha512_begin (&sha2); - WHIRLPOOL_add (bootLoaderBuf, TC_BOOT_SECTOR_PIM_VALUE_OFFSET * 8, &whirlpool); + WHIRLPOOL_add (bootLoaderBuf, TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &whirlpool); sha512_hash (bootLoaderBuf, TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &sha2); - WHIRLPOOL_add (bootLoaderBuf + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)) * 8, &whirlpool); + WHIRLPOOL_add (bootLoaderBuf + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)), &whirlpool); sha512_hash (bootLoaderBuf + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)), &sha2); - WHIRLPOOL_add (bootLoaderBuf + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)) * 8, &whirlpool); + WHIRLPOOL_add (bootLoaderBuf + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)), &whirlpool); sha512_hash (bootLoaderBuf + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)), &sha2); - WHIRLPOOL_add (bootLoaderBuf + TC_SECTOR_SIZE_BIOS, (bootLoaderSize - TC_SECTOR_SIZE_BIOS) * 8, &whirlpool); + WHIRLPOOL_add (bootLoaderBuf + TC_SECTOR_SIZE_BIOS, (bootLoaderSize - TC_SECTOR_SIZE_BIOS), &whirlpool); sha512_hash (bootLoaderBuf + TC_SECTOR_SIZE_BIOS, (bootLoaderSize - TC_SECTOR_SIZE_BIOS), &sha2); WHIRLPOOL_finalize (&whirlpool, fingerprint); @@ -667,6 +676,8 @@ int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, int pim, PCR serpent_set_key (dk, cryptoInfo->ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *) cryptoInfo->ks, (const u4byte *) dk); + #elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_set_key (dk, cryptoInfo->ks); #else status = EAInit (dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) @@ -683,6 +694,8 @@ int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, int pim, PCR serpent_set_key (dk + 32, cryptoInfo->ks2); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *)cryptoInfo->ks2, (const u4byte *) (dk + 32)); + #elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_set_key (dk + 32, cryptoInfo->ks2); #else EAInit (dk + 32, cryptoInfo->ks2); #endif @@ -742,6 +755,8 @@ int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, int pim, PCR serpent_set_key (dk, cryptoInfo->ks); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *) cryptoInfo->ks, (const u4byte *) dk); + #elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_set_key (dk, cryptoInfo->ks); #else status = EAInit (dk, cryptoInfo->ks); if (status == ERR_CIPHER_INIT_FAILURE) @@ -759,6 +774,8 @@ int ReadVolumeHeader (BOOL bBoot, char *header, Password *password, int pim, PCR serpent_set_key (dk + 32, cryptoInfo->ks2); #elif defined (TC_WINDOWS_BOOT_TWOFISH) twofish_set_key ((TwofishInstance *)cryptoInfo->ks2, (const u4byte *) (dk + 32)); + #elif defined (TC_WINDOWS_BOOT_CAMELLIA) + camellia_set_key (dk + 32, cryptoInfo->ks2); #else EAInit (dk + 32, cryptoInfo->ks2); #endif @@ -793,15 +810,22 @@ ret: #endif // Creates a volume header in memory +#if defined(_UEFI) +int CreateVolumeHeaderInMemory(BOOL bBoot, char *header, int ea, int mode, Password *password, + int pkcs5_prf, int pim, char *masterKeydata, PCRYPTO_INFO *retInfo, + unsigned __int64 volumeSize, unsigned __int64 hiddenVolumeSize, + unsigned __int64 encryptedAreaStart, unsigned __int64 encryptedAreaLength, uint16 requiredProgramVersion, uint32 headerFlags, uint32 sectorSize, BOOL bWipeMode) +#else int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, int mode, Password *password, int pkcs5_prf, int pim, char *masterKeydata, PCRYPTO_INFO *retInfo, unsigned __int64 volumeSize, unsigned __int64 hiddenVolumeSize, unsigned __int64 encryptedAreaStart, unsigned __int64 encryptedAreaLength, uint16 requiredProgramVersion, uint32 headerFlags, uint32 sectorSize, BOOL bWipeMode) +#endif // !defined(_UEFI) { unsigned char *p = (unsigned char *) header; static CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo; - int nUserKeyLen = password->Length; + int nUserKeyLen = password? password->Length : 0; PCRYPTO_INFO cryptoInfo = crypto_open (); static char dk[MASTER_KEYDATA_SIZE]; int x; @@ -816,9 +840,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, pim = 0; memset (header, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - +#if !defined(_UEFI) VirtualLock (&keyInfo, sizeof (keyInfo)); VirtualLock (&dk, sizeof (dk)); +#endif // !defined(_UEFI) /* Encryption setup */ @@ -835,8 +860,15 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, bytesNeeded = EAGetKeySize (ea) * 2; // Size of primary + secondary key(s) } +#if !defined(_UEFI) if (!RandgetBytes (hwndDlg, keyInfo.master_keydata, bytesNeeded, TRUE)) +#else + if (!RandgetBytes(keyInfo.master_keydata, bytesNeeded, TRUE)) +#endif + { + crypto_close (cryptoInfo); return ERR_CIPHER_INIT_WEAK_KEY; + } } else { @@ -845,9 +877,17 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, } // User key - memcpy (keyInfo.userKey, password->Text, nUserKeyLen); - keyInfo.keyLength = nUserKeyLen; - keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, pim, FALSE, bBoot); + if (password) + { + memcpy (keyInfo.userKey, password->Text, nUserKeyLen); + keyInfo.keyLength = nUserKeyLen; + keyInfo.noIterations = get_pkcs5_iteration_count (pkcs5_prf, pim, FALSE, bBoot); + } + else + { + keyInfo.keyLength = 0; + keyInfo.noIterations = 0; + } // User selected encryption algorithm cryptoInfo->ea = ea; @@ -862,35 +902,64 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, cryptoInfo->mode = mode; // Salt for header key derivation - if (!RandgetBytes (hwndDlg, keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode)) - return ERR_CIPHER_INIT_WEAK_KEY; +#if !defined(_UEFI) + if (!RandgetBytes(hwndDlg, keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode)) +#else + if (!RandgetBytes(keyInfo.salt, PKCS5_SALT_SIZE, !bWipeMode)) +#endif + { + crypto_close (cryptoInfo); + return ERR_CIPHER_INIT_WEAK_KEY; + } - // PBKDF2 (PKCS5) is used to derive primary header key(s) and secondary header key(s) (XTS) from the password/keyfiles - switch (pkcs5_prf) + if (password) { - case SHA512: - derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; - - case SHA256: - derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); - break; - - case RIPEMD160: - derive_key_ripemd160 (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; + // PBKDF2 (PKCS5) is used to derive primary header key(s) and secondary header key(s) (XTS) from the password/keyfiles + switch (pkcs5_prf) + { + case SHA512: + derive_key_sha512 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; - default: - // Unknown/wrong ID - TC_THROW_FATAL_EXCEPTION; + case SHA256: + derive_key_sha256 (keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; + + case RIPEMD160: + derive_key_ripemd160 (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; + + case STREEBOG: + derive_key_streebog(keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; + + default: + // Unknown/wrong ID + crypto_close (cryptoInfo); + TC_THROW_FATAL_EXCEPTION; + } + } + else + { + // generate a random key +#if !defined(_UEFI) + if (!RandgetBytes(hwndDlg, dk, GetMaxPkcs5OutSize(), !bWipeMode)) +#else + if (!RandgetBytes(dk, GetMaxPkcs5OutSize(), !bWipeMode)) +#endif + { + crypto_close (cryptoInfo); + return ERR_CIPHER_INIT_WEAK_KEY; + } } /* Header setup */ @@ -942,6 +1011,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, || sectorSize > TC_MAX_VOLUME_SECTOR_SIZE || sectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0) { + crypto_close (cryptoInfo); TC_THROW_FATAL_EXCEPTION; } @@ -970,11 +1040,17 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, retVal = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) + { + crypto_close (cryptoInfo); return retVal; + } // Mode of operation if (!EAInitMode (cryptoInfo)) + { + crypto_close (cryptoInfo); return ERR_OUTOFMEMORY; + } // Encrypt the entire header (except the salt) @@ -988,7 +1064,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, // Init with the master key(s) retVal = EAInit (cryptoInfo->ea, keyInfo.master_keydata + primaryKeyOffset, cryptoInfo->ks); if (retVal != ERR_SUCCESS) + { + crypto_close (cryptoInfo); return retVal; + } memcpy (cryptoInfo->master_keydata, keyInfo.master_keydata, MASTER_KEYDATA_SIZE); @@ -1002,7 +1081,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, // Mode of operation if (!EAInitMode (cryptoInfo)) + { + crypto_close (cryptoInfo); return ERR_OUTOFMEMORY; + } #ifdef VOLFORMAT @@ -1054,7 +1136,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, return 0; } - +#if !defined(_UEFI) BOOL ReadEffectiveVolumeHeader (BOOL device, HANDLE fileHandle, byte *header, DWORD *bytesRead) { #if TC_VOLUME_HEADER_EFFECTIVE_SIZE > TC_MAX_VOLUME_SECTOR_SIZE @@ -1157,7 +1239,7 @@ BOOL WriteEffectiveVolumeHeader (BOOL device, HANDLE fileHandle, byte *header) // Writes randomly generated data to unused/reserved header areas. // When bPrimaryOnly is TRUE, then only the primary header area (not the backup header area) is filled with random data. // When bBackupOnly is TRUE, only the backup header area (not the primary header area) is filled with random data. -int WriteRandomDataToReservedHeaderAreas (HWND hwndDlg, HANDLE dev, CRYPTO_INFO *cryptoInfo, uint64 dataAreaSize, BOOL bPrimaryOnly, BOOL bBackupOnly) +int WriteRandomDataToReservedHeaderAreas (HWND hwndDlg, HANDLE dev, CRYPTO_INFO *cryptoInfo, uint64 dataAreaSize, BOOL bPrimaryOnly, BOOL bBackupOnly, BOOL bInPlaceEnc) { char temporaryKey[MASTER_KEYDATA_SIZE]; char originalK2[MASTER_KEYDATA_SIZE]; @@ -1216,6 +1298,13 @@ int WriteRandomDataToReservedHeaderAreas (HWND hwndDlg, HANDLE dev, CRYPTO_INFO goto final_seq; } + if (backupHeaders || !bInPlaceEnc) + { + // encrypt random data instead of existing data for better entropy, except in case of primary + // header of an in-place encrypted disk + RandgetBytes (hwndDlg, buf + TC_VOLUME_HEADER_EFFECTIVE_SIZE, sizeof (buf) - TC_VOLUME_HEADER_EFFECTIVE_SIZE, FALSE); + } + EncryptBuffer (buf + TC_VOLUME_HEADER_EFFECTIVE_SIZE, sizeof (buf) - TC_VOLUME_HEADER_EFFECTIVE_SIZE, cryptoInfo); if (!SetFilePointerEx (dev, offset, NULL, FILE_BEGIN)) @@ -1267,4 +1356,5 @@ final_seq: return nStatus; } +#endif // !defined(_UEFI) #endif // !defined (DEVICE_DRIVER) && !defined (TC_WINDOWS_BOOT) diff --git a/src/Common/Volumes.h b/src/Common/Volumes.h index fdd1444f..68ba6720 100644 --- a/src/Common/Volumes.h +++ b/src/Common/Volumes.h @@ -1,11 +1,11 @@ /* Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0, also from the source code of Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -19,7 +19,7 @@ extern "C" { #endif // Volume header version -#define VOLUME_HEADER_VERSION 0x0005 +#define VOLUME_HEADER_VERSION 0x0005 // Version number written to volume header during format; // specifies the minimum program version required to mount the volume @@ -101,7 +101,7 @@ extern "C" { #define HEADER_SALT_OFFSET 0 #define HEADER_ENCRYPTED_DATA_OFFSET PKCS5_SALT_SIZE #define HEADER_MASTER_KEYDATA_OFFSET 256 - + #define TC_HEADER_OFFSET_MAGIC 64 #define TC_HEADER_OFFSET_VERSION 68 #define TC_HEADER_OFFSET_REQUIRED_VERSION 70 @@ -130,20 +130,24 @@ extern BOOL ReadVolumeHeaderRecoveryMode; uint16 GetHeaderField16 (byte *header, int offset); uint32 GetHeaderField32 (byte *header, int offset); UINT64_STRUCT GetHeaderField64 (byte *header, int offset); -#ifdef TC_WINDOWS_BOOT +#if defined(TC_WINDOWS_BOOT) int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo); +#elif defined(_UEFI) +int ReadVolumeHeader(BOOL bBoot, char *encryptedHeader, Password *password, int pkcs5_prf, int pim, BOOL truecryptMode, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo); +int CreateVolumeHeaderInMemory(BOOL bBoot, char *encryptedHeader, int ea, int mode, Password *password, int pkcs5_prf, int pim, char *masterKeydata, PCRYPTO_INFO *retInfo, unsigned __int64 volumeSize, unsigned __int64 hiddenVolumeSize, unsigned __int64 encryptedAreaStart, unsigned __int64 encryptedAreaLength, uint16 requiredProgramVersion, uint32 headerFlags, uint32 sectorSize, BOOL bWipeMode); +BOOL RandgetBytes(unsigned char *buf, int len, BOOL forceSlowPoll); #else int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int pkcs5_prf, int pim, BOOL truecryptMode, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UEFI) void ComputeBootloaderFingerprint (byte *bootLoaderBuf, unsigned int bootLoaderSize, byte* fingerprint); #endif #endif -#if !defined (DEVICE_DRIVER) && !defined (TC_WINDOWS_BOOT) +#if !defined (DEVICE_DRIVER) && !defined (TC_WINDOWS_BOOT) && !defined(_UEFI) int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *encryptedHeader, int ea, int mode, Password *password, int pkcs5_prf, int pim, char *masterKeydata, PCRYPTO_INFO *retInfo, unsigned __int64 volumeSize, unsigned __int64 hiddenVolumeSize, unsigned __int64 encryptedAreaStart, unsigned __int64 encryptedAreaLength, uint16 requiredProgramVersion, uint32 headerFlags, uint32 sectorSize, BOOL bWipeMode); BOOL ReadEffectiveVolumeHeader (BOOL device, HANDLE fileHandle, byte *header, DWORD *bytesRead); BOOL WriteEffectiveVolumeHeader (BOOL device, HANDLE fileHandle, byte *header); -int WriteRandomDataToReservedHeaderAreas (HWND hwndDlg, HANDLE dev, CRYPTO_INFO *cryptoInfo, uint64 dataAreaSize, BOOL bPrimaryOnly, BOOL bBackupOnly); +int WriteRandomDataToReservedHeaderAreas (HWND hwndDlg, HANDLE dev, CRYPTO_INFO *cryptoInfo, uint64 dataAreaSize, BOOL bPrimaryOnly, BOOL bBackupOnly, BOOL bInPlaceEnc); #endif #endif // !TC_HEADER_Volume_VolumeHeader diff --git a/src/Common/XUnzip.cpp b/src/Common/XUnzip.cpp new file mode 100644 index 00000000..913f4408 --- /dev/null +++ b/src/Common/XUnzip.cpp @@ -0,0 +1,4405 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T //+++1.2 +#endif + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <tchar.h> +#include "XUnzip.h" + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1<c2) + return -1; + if (c1>c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackRead<uMaxBack) + { uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offset<us.offset_central_dir+us.size_central_dir) && (err==UNZ_OK)) err=UNZ_BADZIPFILE; + //if (err!=UNZ_OK) {lufclose(fin);return NULL;} + if (err!=UNZ_OK) {lufclose(fin); zopenerror = err; return NULL;} //+++1.2 + + us.file=fin; + us.byte_before_the_zipfile = central_pos+fin->initial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename<fileNameBufferSize) + { + *(szFileName+file_info.size_filename)='\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename>0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extra<extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek!=0) + if (lufseek(s->file,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment<commentBufferSize) + { + *(szComment+file_info.size_file_comment)='\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (lSeek!=0) + if (lufseek(s->file,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;i<uDoCopy;i++) + { *(pfile_in_zip_read_info->stream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) + { + if( pfile_in_zip_read_info->rest_read_uncompressed > 0 ) + { + return iRead; // More to go + } + return Z_OK; // No point returning UNZ_EOF as it is also zero + } + //if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + // if (err==Z_OK) return iRead; + if( err == Z_OK ) + { + if( pfile_in_zip_read_info->rest_read_uncompressed > 0 ) + { + return iRead; // More to go + } + return Z_OK; // Done + } + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(__time32_t timer) +{ + struct tm *tm = _gmtime32(&timer); + SYSTEMTIME st; + + if (tm == NULL) + { + _time32(&timer); + tm = _gmtime32(&timer); + } + + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_file<index) unzGoToNextFile(uf); + unz_file_info ufi; + char fn[MAX_PATH]; + unzGetCurrentFileInfo(uf,&ufi,fn,MAX_PATH,NULL,0,NULL,0); + + // now get the extra header. We do this ourselves, instead of + // calling unzOpenCurrentFile &c., to avoid allocating more than necessary. + unsigned int extralen,iSizeVar; unsigned long offset; + int res = unzlocal_CheckCurrentFileCoherencyHeader(uf,&iSizeVar,&offset,&extralen); + if (res!=UNZ_OK) return ZR_CORRUPT; + if (lufseek(uf->file,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME lt, ft; + DosDateTimeToFileTime(dosdate,dostime,<); + LocalFileTimeToFileTime(<,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4<extralen) + { char etype[3]; etype[0]=extra[epos+0]; etype[1]=extra[epos+1]; etype[2]=0; + int size = extra[epos+2]; + if (strcmp(etype,"UT")!=0) {epos += 4+size; continue;} + int flags = extra[epos+4]; + bool hasmtime = (flags&1)!=0; + bool hasatime = (flags&2)!=0; + bool hasctime = (flags&4)!=0; + epos+=5; + if (hasmtime) + { __time32_t mtime = *(__time32_t*)(extra+epos); epos+=4; + ze->mtime = timet2filetime(mtime); + } + if (hasatime) + { __time32_t atime = *(__time32_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { __time32_t ctime = *(__time32_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file<index) + unzGoToNextFile(uf); + unzOpenCurrentFile(uf); + currentfile=index; + } + int res = unzReadCurrentFile(uf,dst,len); + if (res>0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file<index) + unzGoToNextFile(uf); + ZIPENTRY ze; + Get(index,&ze); + + // zipentry=directory is handled specially + if ((ze.attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + if (flags==ZIP_HANDLE) + return ZR_OK; // don't do anything +#ifdef _UNICODE + TCHAR uname[MAX_PATH]; + GetUnicodeFileName(ze.name, uname, MAX_PATH-1); + EnsureDirectory(rootdir, uname); +#else + EnsureDirectory(rootdir, ze.name); +#endif + return ZR_OK; + } + + // otherwise, we write the zipentry to a file/handle + HANDLE h; + if (flags==ZIP_HANDLE) + h=dst; + else + { + const TCHAR *name = (const TCHAR *)dst; + const TCHAR *c = name; + while (*c) + { + if (*c == _T('/') || *c == _T('\\')) + name = c + 1; + c++; + } + // if it's a relative filename, ensure directories. We do this as a service + // to the caller so they can just unzip straight unto ze.name. + if (name != (const TCHAR *)dst) + { + TCHAR dir[MAX_PATH]; + _tcscpy(dir,(const TCHAR*)dst); + dir[name-(const TCHAR*)dst-1] = _T('\0'); + bool isabsolute = (dir[0]==_T('/') || dir[0]==_T('\\') || dir[1]==_T(':')); + isabsolute |= (_tcsstr(dir,_T("../"))!=0) | (_tcsstr(dir,_T("..\\"))!=0); + if (!isabsolute) + EnsureDirectory(rootdir,dir); + } + h = ::CreateFile((const TCHAR*)dst, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + ze.attr, NULL); + } + + if (h == INVALID_HANDLE_VALUE) + return ZR_NOFILE; + + unzOpenCurrentFile(uf); + BYTE buf[16384]; + bool haderr=false; + + for (;;) + { + int res = unzReadCurrentFile(uf,buf,16384); + if (res<0) + { + haderr=true; + break; + } + if (res==0) + break; + DWORD writ; + BOOL bres = WriteFile(h,buf,res,&writ,NULL); + if (!bres) + { + haderr=true; + break; + } + } + bool settime=false; + DWORD type = GetFileType(h); + if (type==FILE_TYPE_DISK && !haderr) + settime=true; + if (settime) + SetFileTime(h,&ze.ctime,&ze.atime,&ze.mtime); + if (flags!=ZIP_HANDLE) + CloseHandle(h); + if (unzCloseCurrentFile(uf) == UNZ_CRCERROR) + return ZR_CORRUPT; + if (haderr) + return ZR_WRITE; + return ZR_OK; +} + +ZRESULT TUnzip::Close() +{ if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + if (uf!=0) unzClose(uf); uf=0; + return ZR_OK; +} + + + + + +ZRESULT lasterrorU=ZR_OK; + +unsigned int FormatZipMessageU(ZRESULT code, char *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorU; + const char *msg="unknown zip result code"; + switch (code) + { case ZR_OK: msg="Success"; break; + case ZR_NODUPH: msg="Culdn't duplicate handle"; break; + case ZR_NOFILE: msg="Couldn't create/open file"; break; + case ZR_NOALLOC: msg="Failed to allocate memory"; break; + case ZR_WRITE: msg="Error writing to file"; break; + case ZR_NOTFOUND: msg="File not found in the zipfile"; break; + case ZR_MORE: msg="Still more data to unzip"; break; + case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg="Error reading file"; break; + case ZR_ARGS: msg="Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg="Caller: there was a previous error"; break; + case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break; + } + unsigned int mlen=(unsigned int)strlen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/src/Common/XUnzip.h b/src/Common/XUnzip.h new file mode 100644 index 00000000..573dc7d8 --- /dev/null +++ b/src/Common/XUnzip.h @@ -0,0 +1,382 @@ +// XUnzip.h Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XUNZIP_H +#define XUNZIP_H + + +#ifndef XZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +#endif + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenZip() +// +// Purpose: Open an existing zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0 +// +HZIP OpenZip(void *z, unsigned int len, DWORD flags); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE); +// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE); +// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME); +// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened i n any other way, then full random access is possible. +// Note: pipe input is not yet implemented. + + +/////////////////////////////////////////////////////////////////////////////// +// +// GetZipItem() +// +// Purpose: Get information about an item in an open zip archive +// +// Parameters: hz - handle of open zip archive +// index - index number (0 based) of item in zip +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define GetZipItem GetZipItemW +#else +#define GetZipItem GetZipItemA +#endif + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + + +/////////////////////////////////////////////////////////////////////////////// +// +// FindZipItem() +// +// Purpose: Find item by name and return information about it +// +// Parameters: hz - handle of open zip archive +// name - name of file to look for inside zip archive +// ic - TRUE = case insensitive +// index - pointer to index number returned, or -1 +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define FindZipItem FindZipItemW +#else +#define FindZipItem FindZipItemA +#endif + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + + +/////////////////////////////////////////////////////////////////////////////// +// +// UnzipItem() +// +// Purpose: Find item by index and unzip it +// +// Parameters: hz - handle of open zip archive +// index - index number of file to unzip +// dst - target file name of unzipped file +// len - for memory (ZIP_MEMORY. length of buffer; +// otherwise 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE); +// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE); +// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i<numitems; i++) +// { GetZipItem(hz,i,&ze); +// UnzipItem(hz,i,ze.name,0,ZIP_FILENAME); +// } +// CloseZip(hz); +// +// +// HRSRC hrsrc = FindResource(hInstance,MAKEINTRESOURCE(1),RT_RCDATA); +// HANDLE hglob = LoadResource(hInstance,hrsrc); +// void *zipbuf=LockResource(hglob); +// unsigned int ziplen=SizeofResource(hInstance,hrsrc); +// HZIP hz = OpenZip(zipbuf, ziplen, ZIP_MEMORY); +// - unzip to a membuffer - +// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze); +// char *ibuf = new char[ze.unc_size]; +// UnzipItem(hz,i, ibuf, ze.unc_size,ZIP_MEMORY); +// delete[] buf; +// - unzip to a fixed membuff - +// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze); +// char ibuf[1024]; ZIPRESULT zr=ZR_MORE; unsigned long totsize=0; +// while (zr==ZR_MORE) +// { zr = UnzipItem(hz,i, ibuf,1024,ZIP_MEMORY); +// unsigned long bufsize=1024; if (zr==ZR_OK) bufsize=ze.unc_size-totsize; +// totsize+=bufsize; +// } +// - unzip to a pipe - +// HANDLE hthread=CreateWavReaderThread(&hread,&hwrite); +// FindZipItem(hz,"sound.wav",&i,&ze); +// UnzipItem(hz,i, hwrite,0,ZIP_HANDLE); +// CloseHandle(hwrite); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hread); CloseHandle(hthread); +// - finished - +// CloseZip(hz); +// // note: no need to free resources obtained through Find/Load/LockResource +// +// +// SetCurrentDirectory("c:\\docs\\pipedzipstuff"); +// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite); +// CreateZipWriterThread(hwrite); +// HZIP hz = OpenZip(hread,0,ZIP_HANDLE); +// for (int i=0; ; i++) +// { ZIPENTRY ze; ZRESULT res = GetZipItem(hz,i,&ze); +// if (res!=ZE_OK) break; // no more +// UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// } +// CloseZip(hz); +// + + + + +// Now we indulge in a little skullduggery so that the code works whether +// the user has included just zip or both zip and unzip. +// Idea: if header files for both zip and unzip are present, then presumably +// the cpp files for zip and unzip are both present, so we will call +// one or the other of them based on a dynamic choice. If the header file +// for only one is present, then we will bind to that particular one. +HZIP OpenZipU(void *z,unsigned int len,DWORD flags); +ZRESULT CloseZipU(HZIP hz); +unsigned int FormatZipMessageU(ZRESULT code, char *buf,unsigned int len); +bool IsZipHandleU(HZIP hz); +#define OpenZip OpenZipU + +#ifdef XZIP_H +#undef CloseZip +#define CloseZip(hz) (IsZipHandleU(hz)?CloseZipU(hz):CloseZipZ(hz)) +#else +#define CloseZip CloseZipU +#define FormatZipMessage FormatZipMessageU +#endif + + +#endif //XUNZIP_H diff --git a/src/Common/XZip.cpp b/src/Common/XZip.cpp new file mode 100644 index 00000000..9ce6de5d --- /dev/null +++ b/src/Common/XZip.cpp @@ -0,0 +1,3215 @@ +// XZip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Fixed UTC problem +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T //+++1.2 +#endif + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <tchar.h> +#include <time.h> +#include "xzip.h" + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +typedef unsigned char uch; // unsigned 8-bit value +typedef unsigned short ush; // unsigned 16-bit value +typedef unsigned long ulg; // unsigned 32-bit value +typedef size_t extent; // file size +typedef unsigned Pos; // must be at least 32 bits +typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing + +#ifndef EOF +#define EOF (-1) +#endif + + +// Error return values. The values 0..4 and 12..18 follow the conventions +// of PKZIP. The values 4..10 are all assigned to "insufficient memory" +// by PKZIP, so the codes 5..10 are used here for other purposes. +#define ZE_MISS -1 // used by procname(), zipbare() +#define ZE_OK 0 // success +#define ZE_EOF 2 // unexpected end of zip file +#define ZE_FORM 3 // zip file structure error +#define ZE_MEM 4 // out of memory +#define ZE_LOGIC 5 // internal logic error +#define ZE_BIG 6 // entry too large to split +#define ZE_NOTE 7 // invalid comment format +#define ZE_TEST 8 // zip test (-T) failed or out of memory +#define ZE_ABORT 9 // user interrupt or termination +#define ZE_TEMP 10 // error using a temp file +#define ZE_READ 11 // read or seek error +#define ZE_NONE 12 // nothing to do +#define ZE_NAME 13 // missing or empty zip file +#define ZE_WRITE 14 // error writing to a file +#define ZE_CREAT 15 // couldn't open to write +#define ZE_PARMS 16 // bad command line +#define ZE_OPEN 18 // could not open a specified file to read +#define ZE_MAXERR 18 // the highest error number + + +// internal file attribute +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#define BEST -1 // Use best method (deflation or store) +#define STORE 0 // Store method +#define DEFLATE 8 // Deflation method + +#define CRCVAL_INITIAL 0L + +// MSDOS file or directory attributes +#define MSDOS_HIDDEN_ATTR 0x02 +#define MSDOS_DIR_ATTR 0x10 + +// Lengths of headers after signatures in bytes +#define LOCHEAD 26 +#define CENHEAD 42 +#define ENDHEAD 18 + +// Definitions for extra field handling: +#define EB_HEADSIZE 4 /* length of a extra field block header */ +#define EB_LEN 2 /* offset of data length field in header */ +#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */ +#define EB_UT_FLAGS 0 /* byte offset of Flags field */ +#define EB_UT_TIME1 1 /* byte offset of 1st time value */ +#define EB_UT_FL_MTIME (1 << 0) /* mtime present */ +#define EB_UT_FL_ATIME (1 << 1) /* atime present */ +#define EB_UT_FL_CTIME (1 << 2) /* ctime present */ +#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n)) +#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3)) +#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1)) + + +// Macros for writing machine integers to little-endian format +#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);} +#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} + + +// -- Structure of a ZIP file -- +// Signatures for zip file information headers +#define LOCSIG 0x04034b50L +#define CENSIG 0x02014b50L +#define ENDSIG 0x06054b50L +#define EXTLOCSIG 0x08074b50L + + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + + +#define WSIZE (0x8000) +// Maximum window size = 32K. If you are really short of memory, compile +// with a smaller WSIZE but this reduces the compression ratio for files +// of size > WSIZE. WSIZE must be a power of two in the current implementation. +// + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +// Minimum amount of lookahead, except at the end of the input file. +// See deflate.c for comments about the MIN_MATCH+1. +// + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +// In order to simplify the code, particularly on 16 bit machines, match +// distances are limited to MAX_DIST instead of WSIZE. +// + + + + + +// =========================================================================== +// Constants +// + +#define MAX_BITS 15 +// All codes must not exceed MAX_BITS bits + +#define MAX_BL_BITS 7 +// Bit length codes must not exceed MAX_BL_BITS bits + +#define LENGTH_CODES 29 +// number of length codes, not counting the special END_BLOCK code + +#define LITERALS 256 +// number of literal bytes 0..255 + +#define END_BLOCK 256 +// end of block literal code + +#define L_CODES (LITERALS+1+LENGTH_CODES) +// number of Literal or Length codes, including the END_BLOCK code + +#define D_CODES 30 +// number of distance codes + +#define BL_CODES 19 +// number of codes used to transfer the bit lengths + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define LIT_BUFSIZE 0x8000 +#define DIST_BUFSIZE LIT_BUFSIZE +// Sizes of match buffers for literals/lengths and distances. There are +// 4 reasons for limiting LIT_BUFSIZE to 64K: +// - frequencies can be kept in 16 bit counters +// - if compression is not successful for the first block, all input data is +// still in the window so we can still emit a stored block even when input +// comes from standard input. (This can also be done for all blocks if +// LIT_BUFSIZE is not greater than 32K.) +// - if compression is not successful for a file smaller than 64K, we can +// even emit a stored file instead of a stored block (saving 5 bytes). +// - creating new Huffman trees less frequently may not provide fast +// adaptation to changes in the input data statistics. (Take for +// example a binary file with poorly compressible code followed by +// a highly compressible string table.) Smaller buffer sizes give +// fast adaptation but have of course the overhead of transmitting trees +// more frequently. +// - I can't count above 4 +// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save +// memory at the expense of compression). Some optimizations would be possible +// if we rely on DIST_BUFSIZE == LIT_BUFSIZE. +// + +#define REP_3_6 16 +// repeat previous bit length 3-6 times (2 bits of repeat count) + +#define REPZ_3_10 17 +// repeat a zero length 3-10 times (3 bits of repeat count) + +#define REPZ_11_138 18 +// repeat a zero length 11-138 times (7 bits of repeat count) + +#define HEAP_SIZE (2*L_CODES+1) +// maximum heap size + + +// =========================================================================== +// Local data used by the "bit string" routines. +// + +#define Buf_size (8 * 2*sizeof(char)) +// Number of bits used within bi_buf. (bi_buf may be implemented on +// more than 16 bits on some systems.) + +// Output a 16 bit value to the bit stream, lower (oldest) byte first +#if 0 // ----------------------------------------------------------- +#define PUTSHORT(state,w) \ +{ \ + if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ +} +#endif // ----------------------------------------------------------- + +//+++1.2 +#define PUTSHORT(state,w) \ +{ \ + if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + if (state.bs.out_offset < state.bs.out_size-1) \ + { \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ + }\ +} + +#if 0 // ----------------------------------------------------------- +#define PUTBYTE(state,b) \ +{ \ + if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} +#endif // ----------------------------------------------------------- + +//+++1.2 +#define PUTBYTE(state,b) \ +{ \ + if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + if (state.bs.out_offset < state.bs.out_size) \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} + +// DEFLATE.CPP HEADER + +#define HASH_BITS 15 +// For portability to 16 bit machines, do not use values above 15. + +#define HASH_SIZE (unsigned)(1<<HASH_BITS) +#define HASH_MASK (HASH_SIZE-1) +#define WMASK (WSIZE-1) +// HASH_SIZE and WSIZE must be powers of two + +#define NIL 0 +// Tail of hash chains + +#define FAST 4 +#define SLOW 2 +// speed options for the general purpose bit flag + +#define TOO_FAR 4096 +// Matches of length 3 are discarded if their distance exceeds TOO_FAR + + + +#define EQUAL 0 +// result of memcmp for equal strings + + +// =========================================================================== +// Local data used by the "longest match" routines. + +#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH) +// Number of bits by which ins_h and del_h must be shifted at each +// input step. It must be such that after MIN_MATCH steps, the oldest +// byte no longer takes part in the hash key, that is: +// H_SHIFT * MIN_MATCH >= HASH_BITS + +#define max_insert_length max_lazy_match +// Insert new strings in the hash table only if the match length +// is not greater than this length. This saves time but degrades compression. +// max_insert_length is used only for compression levels <= 3. + + + +const int extra_lbits[LENGTH_CODES] // extra bits for each length code + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +const int extra_dbits[D_CODES] // extra bits for each distance code + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +const int extra_blbits[BL_CODES]// extra bits for each bit length code + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +// The lengths of the bit length codes are sent in order of decreasing +// probability, to avoid transmitting the lengths for unused bit length codes. + + +typedef struct config { + ush good_length; // reduce lazy search above this match length + ush max_lazy; // do not perform lazy search above this match length + ush nice_length; // quit search above this match length + ush max_chain; +} config; + +// Values for max_lazy_match, good_match, nice_match and max_chain_length, +// depending on the desired pack level (0..9). The values given below have +// been tuned to exclude worst case performance for pathological files. +// Better values may be found for specific files. +// + +const config configuration_table[10] = { +// good lazy nice chain + {0, 0, 0, 0}, // 0 store only + {4, 4, 8, 4}, // 1 maximum speed, no lazy matches + {4, 5, 16, 8}, // 2 + {4, 6, 32, 32}, // 3 + {4, 4, 16, 16}, // 4 lazy matches */ + {8, 16, 32, 32}, // 5 + {8, 16, 128, 128}, // 6 + {8, 32, 128, 256}, // 7 + {32, 128, 258, 1024}, // 8 + {32, 258, 258, 4096}};// 9 maximum compression */ + +// Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 +// For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning. + + + + + +// Data structure describing a single value and its code string. +typedef struct ct_data { + union { + ush freq; // frequency count + ush code; // bit string + } fc; + union { + ush dad; // father node in Huffman tree + ush len; // length of bit string + } dl; +} ct_data; + +typedef struct tree_desc +{ + ct_data *dyn_tree; // the dynamic tree + ct_data *static_tree; // corresponding static tree or NULL + const int *extra_bits; // extra bits for each code or NULL + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + int max_code; // largest code with non zero frequency +} tree_desc; + + +class TTreeState +{ +public: + TTreeState(); + + ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree + ct_data dyn_dtree[2*D_CODES+1]; // distance tree + ct_data static_ltree[L_CODES+2]; // the static literal tree... + // ... Since the bit lengths are imposed, there is no need for the L_CODES + // extra codes used during heap construction. However the codes 286 and 287 + // are needed to build a canonical tree (see ct_init below). + ct_data static_dtree[D_CODES]; // the static distance tree... + // ... (Actually a trivial tree since all codes use 5 bits.) + ct_data bl_tree[2*BL_CODES+1]; // Huffman tree for the bit lengths + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS+1]; // number of codes at each bit length for an optimal tree + + int heap[2*L_CODES+1]; // heap used to build the Huffman trees + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + uch depth[2*L_CODES+1]; + // Depth of each subtree used as tie breaker for trees of equal frequency + + uch length_code[MAX_MATCH-MIN_MATCH+1]; + // length code for each normalized match length (0 == MIN_MATCH) + + uch dist_code[512]; + // distance codes. The first 256 values correspond to the distances + // 3 .. 258, the last 256 values correspond to the top 8 bits of + // the 15 bit distances. + + int base_length[LENGTH_CODES]; + // First normalized length for each code (0 = MIN_MATCH) + + int base_dist[D_CODES]; + // First normalized distance for each code (0 = distance of 1) + + uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths + ush far d_buf[DIST_BUFSIZE]; // buffer for distances + + uch flag_buf[(LIT_BUFSIZE/8)]; + // flag_buf is a bit array distinguishing literals from lengths in + // l_buf, and thus indicating the presence or absence of a distance. + + unsigned last_lit; // running index in l_buf + unsigned last_dist; // running index in d_buf + unsigned last_flags; // running index in flag_buf + uch flags; // current flags not yet saved in flag_buf + uch flag_bit; // current bit used in flags + // bits are filled in flags starting at bit 0 (least significant). + // Note: these flags are overkill in the current code since we don't + // take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + + ulg opt_len; // bit length of current block with optimal trees + ulg static_len; // bit length of current block with static trees + + ulg cmpr_bytelen; // total byte length of compressed file + ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen' + + ulg input_len; // total byte length of input file + // input_len is for debugging only since we can get it by other means. + + ush *file_type; // pointer to UNKNOWN, BINARY or ASCII +// int *file_method; // pointer to DEFLATE or STORE +}; + +TTreeState::TTreeState() +{ + tree_desc a = {dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; l_desc = a; + tree_desc b = {dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; d_desc = b; + tree_desc c = {bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; bl_desc = c; + last_lit = 0; + last_dist = 0; + last_flags = 0; + + memset(dyn_ltree, 0, sizeof(dyn_ltree)); + memset(dyn_dtree, 0, sizeof(dyn_dtree)); + memset(static_ltree, 0, sizeof(static_ltree)); + memset(static_dtree, 0, sizeof(static_dtree)); + memset(bl_tree, 0, sizeof(bl_tree)); + memset(bl_count, 0, sizeof(bl_count)); + memset(heap, 0, sizeof(heap)); + heap_len = 0; + heap_max = 0; + + memset(depth, 0, sizeof(depth)); + memset(length_code, 0, sizeof(length_code)); + memset(dist_code, 0, sizeof(dist_code)); + memset(base_length, 0, sizeof(base_length)); + memset(base_dist, 0, sizeof(base_dist)); + memset(l_buf, 0, sizeof(l_buf)); + memset(d_buf, 0, sizeof(d_buf)); + memset(flag_buf, 0, sizeof(flag_buf)); + + last_lit = 0; + last_dist = 0; + last_flags = 0; + flags = 0; + flag_bit = 0; + opt_len = 0; + static_len = 0; + cmpr_bytelen = 0; + cmpr_len_bits = 0; + input_len = 0; + file_type = 0; +} + +class TBitState +{ +public: + TBitState() + { + flush_flg = 0; + bi_buf = 0; + bi_valid = 0; + out_buf = 0; + out_offset = 0; + out_size = 0; + bits_sent = 0; + } + + int flush_flg; + // + unsigned bi_buf; + // Output buffer. bits are inserted starting at the bottom (least significant + // bits). The width of bi_buf must be at least 16 bits. + int bi_valid; + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + char *out_buf; + // Current output buffer. + unsigned out_offset; + // Current offset in output buffer. + // On 16 bit machines, the buffer is limited to 64K. + unsigned out_size; + // Size of current output buffer + ulg bits_sent; // bit length of the compressed data only needed for debugging??? +}; + + +class TDeflateState +{ +public: + TDeflateState() + { + memset(window, 0, sizeof(window)); + memset(prev, 0, sizeof(prev)); + memset(head, 0, sizeof(head)); + window_size = 0; + block_start = 0; + sliding = 0; + ins_h = 0; + prev_length = 0; + strstart = 0; + match_start = 0; + eofile = 0; + lookahead = 0; + max_chain_length = 0; + max_lazy_match = 0; + good_match = 0; + nice_match = 0; + } + + uch window[2L*WSIZE]; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least WSIZE + // bytes. With this organization, matches are limited to a distance of + // WSIZE-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would + // be less efficient since the data would have to be copied WSIZE/CBSZ times) + Pos prev[WSIZE]; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + Pos head[HASH_SIZE]; + // Heads of the hash chains or NIL. If your compiler thinks that + // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC. + + ulg window_size; + // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + // input file length plus MIN_LOOKAHEAD. + + long block_start; + // window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int sliding; + // Set to false when the input file is already in memory + + unsigned ins_h; // hash index of string to be inserted + + unsigned int prev_length; + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + + unsigned strstart; // start of string to insert + unsigned match_start; // start of matching string + int eofile; // flag set at end of input file + unsigned lookahead; // number of valid bytes ahead in window + + unsigned max_chain_length; + // To speed up deflation, hash chains are never searched beyond this length. + // A higher limit improves compression ratio but degrades the speed. + + unsigned int max_lazy_match; + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + + unsigned good_match; + // Use a faster search when the previous match is longer than this + + int nice_match; // Stop searching when current match exceeds this +}; + + +typedef struct iztimes { + __time32_t atime,mtime,ctime; +} iztimes; // access, modify, create times + +typedef struct zlist { + ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are + ulg tim, crc, siz, len; + extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD + ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD + ulg atx, off; + char name[MAX_PATH]; // File name in zip file + char *extra; // Extra field (set only if ext != 0) + char *cextra; // Extra in central (set only if cext != 0) + char *comment; // Comment (set only if com != 0) + char iname[MAX_PATH]; // Internal file name after cleanup + char zname[MAX_PATH]; // External version of internal name + int mark; // Marker for files to operate on + int trash; // Marker for files to delete + int dosflag; // Set to force MSDOS file attributes + struct zlist far *nxt; // Pointer to next header in list +} TZipFileInfo; + + +class TState; +typedef unsigned (*READFUNC)(TState &state, char *buf,unsigned size); +typedef unsigned (*FLUSHFUNC)(void *param, const char *buf, unsigned *size); +typedef unsigned (*WRITEFUNC)(void *param, const char *buf, unsigned size); + +class TState +{ +public: + TState() //+++1.2 + { + param = 0; + level = 0; + seekable = FALSE; + readfunc = 0; + flush_outbuf = 0; + err = 0; + } + + void *param; + int level; + bool seekable; + READFUNC readfunc; + FLUSHFUNC flush_outbuf; + TTreeState ts; + TBitState bs; + TDeflateState ds; + const char *err; +}; + +void Assert(TState &state,bool cond, const char *msg) +{ if (cond) return; + state.err=msg; +} +void __cdecl Trace(const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} +void __cdecl Tracec(bool ,const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} + +// =========================================================================== +// Local (static) routines in this file. +// + +void init_block (TState &); +void pqdownheap (TState &,ct_data *tree, int k); +void gen_bitlen (TState &,tree_desc *desc); +void gen_codes (TState &state,ct_data *tree, int max_code); +void build_tree (TState &,tree_desc *desc); +void scan_tree (TState &,ct_data *tree, int max_code); +void send_tree (TState &state,ct_data *tree, int max_code); +int build_bl_tree (TState &); +void send_all_trees (TState &state,int lcodes, int dcodes, int blcodes); +void compress_block (TState &state,ct_data *ltree, ct_data *dtree); +void set_file_type (TState &); +void send_bits (TState &state, int value, int length); +unsigned bi_reverse (unsigned code, int len); +void bi_windup (TState &state); +void copy_block (TState &state,char *buf, unsigned len, int header); + + +#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len) +// Send a code of the given tree. c and tree must not have side effects + +// alternatively... +//#define send_code(state, c, tree) +// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c)); +// send_bits(state, tree[c].fc.code, tree[c].dl.len); } + +#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)]) +// Mapping from a distance to a distance code. dist is the distance - 1 and +// must not have side effects. dist_code[256] and dist_code[257] are never used. + +#define Max(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(TState &state, ush *attr) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + state.ts.file_type = attr; + //state.ts.file_method = method; + state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L; + state.ts.input_len = 0L; + + if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + state.ts.base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + state.ts.length_code[length++] = (uch)code; + } + } + Assert(state,length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + state.ts.length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + state.ts.base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + state.ts.dist_code[dist++] = (uch)code; + } + } + Assert(state,dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + state.ts.base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + state.ts.dist_code[256 + dist++] = (uch)code; + } + } + Assert(state,dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + n = 0; + while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++; + while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++; + while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + /* fc.codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(state,(ct_data *)state.ts.static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + state.ts.static_dtree[n].dl.len = 5; + state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(state); +} + +/* =========================================================================== + * Initialize a new block. + */ +void init_block(TState &state) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0; + for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0; + for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0; + + state.ts.dyn_ltree[END_BLOCK].fc.freq = 1; + state.ts.opt_len = state.ts.static_len = 0L; + state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0; + state.ts.flags = 0; state.ts.flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = state.ts.heap[SMALLEST]; \ + state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \ + pqdownheap(state,tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].fc.freq < tree[m].fc.freq || \ + (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +void pqdownheap(TState &state,ct_data *tree, int k) +{ + int v = state.ts.heap[k]; + int j = k << 1; /* left son of k */ + int htemp; /* required because of bug in SASC compiler */ + + while (j <= state.ts.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j+1], state.ts.heap[j])) j++; + + /* Exit if v is smaller than both sons */ + htemp = state.ts.heap[j]; + if (smaller(tree, v, htemp)) break; + + /* Exchange v with the smallest son */ + state.ts.heap[k] = htemp; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + state.ts.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +void gen_bitlen(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + const int *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */ + + for (h = state.ts.heap_max+1; h < HEAP_SIZE; h++) { + n = state.ts.heap[h]; + bits = tree[tree[n].dl.dad].dl.len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].dl.len = (ush)bits; + /* We overwrite tree[n].dl.dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + state.ts.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].fc.freq; + state.ts.opt_len += (ulg)f * (bits + xbits); + if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits); + } + if (overflow == 0) return; + + Trace("\nbit length overflow\n"); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (state.ts.bl_count[bits] == 0) bits--; + state.ts.bl_count[bits]--; /* move one leaf down the tree */ + state.ts.bl_count[bits+1] += (ush)2; /* move one overflow item as its brother */ + state.ts.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = state.ts.bl_count[bits]; + while (n != 0) { + m = state.ts.heap[--h]; + if (m > max_code) continue; + if (tree[m].dl.len != (ush)bits) { + Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits); + state.ts.opt_len += ((long)bits-(long)tree[m].dl.len)*(long)tree[m].fc.freq; + tree[m].dl.len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +void gen_codes (TState &state, ct_data *tree, int max_code) +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (ush)((code + state.ts.bl_count[bits-1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(state,code + state.ts.bl_count[MAX_BITS]-1 == (1<< ((ush) MAX_BITS)) - 1, + "inconsistent bit counts"); + Trace("\ngen_codes: max_code %d ", max_code); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].dl.len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len); + + //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +void build_tree(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc.freq != 0) { + state.ts.heap[++state.ts.heap_len] = max_code = n; + state.ts.depth[n] = 0; + } else { + tree[n].dl.len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (state.ts.heap_len < 2) { + int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[newcp].fc.freq = 1; + state.ts.depth[newcp] = 0; + state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = state.ts.heap_len/2; n >= 1; n--) pqdownheap(state,tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */ + + state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */ + state.ts.heap[--state.ts.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq); + state.ts.depth[node] = (uch) (Max(state.ts.depth[n], state.ts.depth[m]) + 1); + tree[n].dl.dad = tree[m].dl.dad = (ush)node; + /* and insert the new node in the heap */ + state.ts.heap[SMALLEST] = node++; + pqdownheap(state,tree, SMALLEST); + + } while (state.ts.heap_len >= 2); + + state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(state,(tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes (state,(ct_data *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +void scan_tree (TState &state,ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].dl.len = (ush)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count); + } else if (curlen != 0) { + if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++; + state.ts.bl_tree[REP_3_6].fc.freq++; + } else if (count <= 10) { + state.ts.bl_tree[REPZ_3_10].fc.freq++; + } else { + state.ts.bl_tree[REPZ_11_138].fc.freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +void send_tree (TState &state, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].dl.len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(state, curlen, state.ts.bl_tree); count--; + } + Assert(state,count >= 3 && count <= 6, " 3_6?"); + send_code(state,REP_3_6, state.ts.bl_tree); send_bits(state,count-3, 2); + + } else if (count <= 10) { + send_code(state,REPZ_3_10, state.ts.bl_tree); send_bits(state,count-3, 3); + + } else { + send_code(state,REPZ_11_138, state.ts.bl_tree); send_bits(state,count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +int build_bl_tree(TState &state) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(state,(ct_data *)state.ts.dyn_ltree, state.ts.l_desc.max_code); + scan_tree(state,(ct_data *)state.ts.dyn_dtree, state.ts.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(state,(tree_desc *)(&state.ts.bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + state.ts.opt_len += 3*(max_blindex+1) + 5+5+4; + Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +void send_all_trees(TState &state,int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(state,lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(state,lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Trace("\nbl counts: "); + send_bits(state,lcodes-257, 5); + /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */ + send_bits(state,dcodes-1, 5); + send_bits(state,blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Trace("\nbl code %2d ", bl_order[rank]); + send_bits(state,state.ts.bl_tree[bl_order[rank]].dl.len, 3); + } + Trace("\nbl tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_ltree, lcodes-1); /* send the literal tree */ + Trace("\nlit tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_dtree, dcodes-1); /* send the distance tree */ + Trace("\ndist tree: sent %ld", state.bs.bits_sent); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length (in bytes) for the file so far. + */ +ulg flush_block(TState &state,char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state); + + /* Construct the literal and distance trees */ + build_tree(state,(tree_desc *)(&state.ts.l_desc)); + Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + build_tree(state,(tree_desc *)(&state.ts.d_desc)); + Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(state); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (state.ts.opt_len+3+7)>>3; + static_lenb = (state.ts.static_len+3+7)>>3; + state.ts.input_len += stored_len; /* for debugging only */ + + Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len, + state.ts.last_lit, state.ts.last_dist); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + // Originally, zip allowed the file to be transformed from a compressed + // into a stored file in the case where compression failed, there + // was only one block, and it was allowed to change. I've removed this + // possibility since the code's cleaner if no changes are allowed. + //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L + // && state.ts.cmpr_len_bits == 0L && state.seekable) + //{ // && state.ts.file_method != NULL + // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: + // Assert(state,buf!=NULL,"block vanished"); + // copy_block(state,buf, (unsigned)stored_len, 0); // without header + // state.ts.cmpr_bytelen = stored_len; + // Assert(state,false,"unimplemented *state.ts.file_method = STORE;"); + // //*state.ts.file_method = STORE; + //} + //else + if (stored_len+4 <= opt_lenb && buf != (char*)NULL) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(state,(STORED_BLOCK<<1)+eof, 3); /* send block type */ + state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4; + state.ts.cmpr_len_bits = 0L; + + copy_block(state,buf, (unsigned)stored_len, 1); /* with header */ + } + else if (static_lenb == opt_lenb) { + send_bits(state,(STATIC_TREES<<1)+eof, 3); + compress_block(state,(ct_data *)state.ts.static_ltree, (ct_data *)state.ts.static_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.static_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + else { + send_bits(state,(DYN_TREES<<1)+eof, 3); + send_all_trees(state,state.ts.l_desc.max_code+1, state.ts.d_desc.max_code+1, max_blindex+1); + compress_block(state,(ct_data *)state.ts.dyn_ltree, (ct_data *)state.ts.dyn_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.opt_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + Assert(state,((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size"); + init_block(state); + + if (eof) { + // Assert(state,input_len == isize, "bad input size"); + bi_windup(state); + state.ts.cmpr_len_bits += 7; /* align on byte boundary */ + } + Trace("\n"); + + return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (TState &state,int dist, int lc) +{ + state.ts.l_buf[state.ts.last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + state.ts.dyn_ltree[lc].fc.freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert(state,(ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + state.ts.dyn_ltree[state.ts.length_code[lc]+LITERALS+1].fc.freq++; + state.ts.dyn_dtree[d_code(dist)].fc.freq++; + + state.ts.d_buf[state.ts.last_dist++] = (ush)dist; + state.ts.flags |= state.ts.flag_bit; + } + state.ts.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((state.ts.last_lit & 7) == 0) { + state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags; + state.ts.flags = 0, state.ts.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)state.ts.last_lit*8L; + ulg in_length = (ulg)state.ds.strstart-state.ds.block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + state.ts.last_lit, state.ts.last_dist, in_length, out_length, + 100L - out_length*100L/in_length); + if (state.ts.last_dist < state.ts.last_lit/2 && out_length < in_length/2) return 1; + } + return (state.ts.last_lit == LIT_BUFSIZE-1 || state.ts.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +void compress_block(TState &state,ct_data *ltree, ct_data *dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (state.ts.last_lit != 0) do { + if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++]; + lc = state.ts.l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(state,lc, ltree); /* send a literal byte */ + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = state.ts.length_code[lc]; + send_code(state,code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= state.ts.base_length[code]; + send_bits(state,lc, extra); /* send the extra length bits */ + } + dist = state.ts.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert(state,code < D_CODES, "bad d_code"); + + send_code(state,code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= state.ts.base_dist[code]; + send_bits(state,dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < state.ts.last_lit); + + send_code(state,END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +void set_file_type(TState &state) +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (TState &state,char *tgt_buf, unsigned tgt_size, int flsh_allowed) +{ + state.bs.out_buf = tgt_buf; + state.bs.out_size = tgt_size; + state.bs.out_offset = 0; + state.bs.flush_flg = flsh_allowed; + + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = 0L; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(TState &state,int value, int length) +{ + Assert(state,length > 0 && length <= 15, "invalid length"); + state.bs.bits_sent += (ulg)length; + /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and + * (Buf_size - bi_valid) bits from value to flush the filled bi_buf, + * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid)) + * unused bits in bi_buf. + */ + state.bs.bi_buf |= (value << state.bs.bi_valid); + state.bs.bi_valid += length; + if (state.bs.bi_valid > (int)Buf_size) { + PUTSHORT(state,state.bs.bi_buf); + state.bs.bi_valid -= Buf_size; + state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid); + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup(TState &state) +{ + if (state.bs.bi_valid > 8) { + PUTSHORT(state,state.bs.bi_buf); + } else if (state.bs.bi_valid > 0) { + PUTBYTE(state,state.bs.bi_buf); + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + } + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = (state.bs.bits_sent+7) & ~7; +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(TState &state, char *block, unsigned len, int header) +{ + bi_windup(state); /* align on byte boundary */ + + if (header) { + PUTSHORT(state,(ush)len); + PUTSHORT(state,(ush)~len); + state.bs.bits_sent += 2*16; + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + state.bs.out_offset = len; + state.flush_outbuf(state.param,block, &state.bs.out_offset); + } else if (state.bs.out_offset + len > state.bs.out_size) { + Assert(state,false,"output buffer too small for in-memory compression"); + } else { + memcpy(state.bs.out_buf + state.bs.out_offset, block, len); + state.bs.out_offset += len; + } + state.bs.bits_sent += (ulg)len<<3; +} + + + + + + + + +/* =========================================================================== + * Prototypes for functions. + */ + +void fill_window (TState &state); +ulg deflate_fast (TState &state); + +int longest_match (TState &state,IPos cur_match); + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) + +/* =========================================================================== + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#define INSERT_STRING(s, match_head) \ + (UPDATE_HASH(state.ds.ins_h, state.ds.window[(s) + (MIN_MATCH-1)]), \ + state.ds.prev[(s) & WMASK] = match_head = state.ds.head[state.ds.ins_h], \ + state.ds.head[state.ds.ins_h] = (s)) + +/* =========================================================================== + * Initialize the "longest match" routines for a new file + * + * IN assertion: window_size is > 0 if the input file is already read or + * mmap'ed in the window[] array, 0 otherwise. In the first case, + * window_size is sufficient to contain the whole input file plus + * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end + * of window[] when looking for matches towards the end). + */ +void lm_init (TState &state, int pack_level, ush *flags) +{ + register unsigned j; + + Assert(state,pack_level>=1 && pack_level<=8,"bad pack level"); + + /* Do not slide the window if the whole input is already in memory + * (window_size > 0) + */ + state.ds.sliding = 0; + if (state.ds.window_size == 0L) { + state.ds.sliding = 1; + state.ds.window_size = (ulg)2L*WSIZE; + } + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + state.ds.head[HASH_SIZE-1] = NIL; + memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE-1)*sizeof(*state.ds.head)); + + /* Set the default configuration parameters: + */ + state.ds.max_lazy_match = configuration_table[pack_level].max_lazy; + state.ds.good_match = configuration_table[pack_level].good_length; + state.ds.nice_match = configuration_table[pack_level].nice_length; + state.ds.max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level <= 2) { + *flags |= FAST; + } else if (pack_level >= 8) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + state.ds.strstart = 0; + state.ds.block_start = 0L; + + j = WSIZE; + j <<= 1; // Can read 64K in one step + state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j); + + if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) { + state.ds.eofile = 1, state.ds.lookahead = 0; + return; + } + state.ds.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + + state.ds.ins_h = 0; + for (j=0; j<MIN_MATCH-1; j++) UPDATE_HASH(state.ds.ins_h, state.ds.window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ +// For 80x86 and 680x0 and ARM, an optimized version is in match.asm or +// match.S. The code is functionally equivalent, so you can use the C version +// if desired. Which I do so desire! +int longest_match(TState &state,IPos cur_match) +{ + unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */ + register uch far *scan = state.ds.window + state.ds.strstart; /* current string */ + register uch far *match; /* matched string */ + register int len; /* length of current match */ + int best_len = state.ds.prev_length; /* best match length so far */ + IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + Assert(state,HASH_BITS>=8 && MAX_MATCH==258,"Code too clever"); + + + + register uch far *strend = state.ds.window + state.ds.strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (state.ds.prev_length >= state.ds.good_match) { + chain_length >>= 2; + } + + Assert(state,state.ds.strstart <= state.ds.window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(state,cur_match < state.ds.strstart, "no future"); + match = state.ds.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(state,scan <= state.ds.window+(unsigned)(state.ds.window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + + if (len > best_len) { + state.ds.match_start = cur_match; + best_len = len; + if (len >= state.ds.nice_match) break; + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; + } + } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + + +#define check_match(state,start, match, length) +// or alternatively... +//void check_match(TState &state,IPos start, IPos match, int length) +//{ // check that the match is indeed a match +// if (memcmp((char*)state.ds.window + match, +// (char*)state.ds.window + start, length) != EQUAL) { +// fprintf(stderr, +// " start %d, match %d, length %d\n", +// start, match, length); +// error("invalid match"); +// } +// if (state.verbose > 1) { +// fprintf(stderr,"\\[%d,%d]", start-match, length); +// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0); +// } +//} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or eofile is set; file reads are + * performed for at least two bytes (required for the translate_eol option). + */ +void fill_window(TState &state) +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart); + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* For MMAP or BIG_MEM, the whole input file is already in memory so + * we must not perform sliding. We must however call (*read_buf)() in + * order to compute the crc, update lookahead and possibly set eofile. + */ + } else if (state.ds.strstart >= WSIZE+MAX_DIST && state.ds.sliding) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)state.ds.window, (char*)state.ds.window+WSIZE, (unsigned)WSIZE); + state.ds.match_start -= WSIZE; + state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + state.ds.block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = state.ds.head[n]; + state.ds.head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = state.ds.prev[n]; + state.ds.prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + if (state.ds.eofile) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the MMAP or BIG_MEM case (not yet supported in gzip), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(state,more >= 2, "more < 2"); + + n = state.readfunc(state, (char*)state.ds.window+state.ds.strstart+state.ds.lookahead, more); + + if (n == 0 || n == (unsigned)EOF) { + state.ds.eofile = 1; + } else { + state.ds.lookahead += n; + } + } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(state,eof) \ + flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \ + (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof)) + +/* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +ulg deflate_fast(TState &state) +{ + IPos hash_head = NIL; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + state.ds.prev_length = MIN_MATCH-1; + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(state,state.ds.strstart, state.ds.match_start, match_length); + + flush = ct_tally(state,state.ds.strstart-state.ds.match_start, match_length - MIN_MATCH); + + state.ds.lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= state.ds.max_insert_length + && state.ds.lookahead >= MIN_MATCH) { + match_length--; /* string at strstart already in hash table */ + do { + state.ds.strstart++; + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--match_length != 0); + state.ds.strstart++; + } else { + state.ds.strstart += match_length; + match_length = 0; + state.ds.ins_h = state.ds.window[state.ds.strstart]; + UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart+1]); + Assert(state,MIN_MATCH==3,"Call UPDATE_HASH() MIN_MATCH-3 more times"); + } + } else { + /* No match, output a literal byte */ + flush = ct_tally (state,0, state.ds.window[state.ds.strstart]); + state.ds.lookahead--; + state.ds.strstart++; + } + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + return FLUSH_BLOCK(state,1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate(TState &state) +{ + IPos hash_head = NIL; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ + + if (state.level <= 3) return deflate_fast(state); /* optimized for speed */ + + /* Process the input block. */ + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + state.ds.prev_length = match_length, prev_match = state.ds.match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match && + state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && state.ds.strstart-state.ds.match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) { + unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH; + check_match(state,state.ds.strstart-1, prev_match, state.ds.prev_length); + flush = ct_tally(state,state.ds.strstart-1-prev_match, state.ds.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + state.ds.lookahead -= state.ds.prev_length-1; + state.ds.prev_length -= 2; + do { + if (++state.ds.strstart <= max_insert) { + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } + } while (--state.ds.prev_length != 0); + state.ds.strstart++; + match_available = 0; + match_length = MIN_MATCH-1; + + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally (state,0, state.ds.window[state.ds.strstart-1])) { + FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + } + state.ds.strstart++; + state.ds.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + state.ds.strstart++; + state.ds.lookahead--; + } +// Assert(state,strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + if (match_available) ct_tally (state,0, state.ds.window[state.ds.strstart-1]); + + return FLUSH_BLOCK(state,1); /* eof */ +} + + + + + + + + + + + + +int putlocal(struct zlist far *z, WRITEFUNC wfunc,void *param) +{ // Write a local header described by *z to file *f. Return a ZE_ error code. + PUTLG(LOCSIG, f); + PUTSH(z->ver, f); + PUTSH(z->lflg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->ext, f); + size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam); + if (res!=z->nam) return ZE_TEMP; + if (z->ext) + { res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext); + if (res!=z->ext) return ZE_TEMP; + } + return ZE_OK; +} + +int putextended(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write an extended local header described by *z to file *f. Returns a ZE_ code + PUTLG(EXTLOCSIG, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + return ZE_OK; +} + +int putcentral(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write a central header entry of *z to file *f. Returns a ZE_ code. + PUTLG(CENSIG, f); + PUTSH(z->vem, f); + PUTSH(z->ver, f); + PUTSH(z->flg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->cext, f); + PUTSH(z->com, f); + PUTSH(z->dsk, f); + PUTSH(z->att, f); + PUTLG(z->atx, f); + PUTLG(z->off, f); + if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam || + (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) || + (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com)) + return ZE_TEMP; + return ZE_OK; +} + + +int putend(int n, ulg s, ulg c, extent m, char *z, WRITEFUNC wfunc, void *param) +{ // write the end of the central-directory-data to file *f. + PUTLG(ENDSIG, f); + PUTSH(0, f); + PUTSH(0, f); + PUTSH(n, f); + PUTSH(n, f); + PUTLG(s, f); + PUTLG(c, f); + PUTSH(m, f); + // Write the comment, if any + if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP; + return ZE_OK; +} + + + + + + +const ulg crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) +#define DO1(buf) crc = CRC32(crc, *buf++) +#define DO2(buf) DO1(buf); DO1(buf) +#define DO4(buf) DO2(buf); DO2(buf) +#define DO8(buf) DO4(buf); DO4(buf) + +ulg crc32(ulg crc, const uch *buf, extent len) +{ if (buf==NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {DO8(buf); len -= 8;} + if (len) do {DO1(buf);} while (--len); + return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines) +} + + + + + + + + +bool HasZipSuffix(const char *fn) +{ const char *ext = fn+strlen(fn); + while (ext>fn && *ext!='.') ext--; + if (ext==fn && *ext!='.') return false; + if (stricmp(ext,".Z")==0) return true; + if (stricmp(ext,".zip")==0) return true; + if (stricmp(ext,".zoo")==0) return true; + if (stricmp(ext,".arc")==0) return true; + if (stricmp(ext,".lzh")==0) return true; + if (stricmp(ext,".arj")==0) return true; + if (stricmp(ext,".gz")==0) return true; + if (stricmp(ext,".tgz")==0) return true; + return false; +} + + +__time32_t filetime2timet(const FILETIME ft) +{ SYSTEMTIME st; FileTimeToSystemTime(&ft,&st); + if (st.wYear<1970) {st.wYear=1970; st.wMonth=1; st.wDay=1;} + if (st.wYear>=2038) {st.wYear=2037; st.wMonth=12; st.wDay=31;} + struct tm tm; + tm.tm_sec = st.wSecond; + tm.tm_min = st.wMinute; + tm.tm_hour = st.wHour; + tm.tm_mday = st.wDay; + tm.tm_mon = st.wMonth-1; + tm.tm_year = st.wYear-1900; + tm.tm_isdst = 0; + __time32_t t = _mktime32(&tm); + return t; +} + + +ZRESULT GetFileInfo(HANDLE hf, ulg *attr, long *size, iztimes *times, ulg *timestamp) +{ + DWORD type=GetFileType(hf); + if (type!=FILE_TYPE_DISK) + return ZR_NOTINITED; + // The handle must be a handle to a file + // The date and time is returned in a long with the date most significant to allow + // unsigned integer comparison of absolute times. The attributes have two + // high bytes unix attr, and two low bytes a mapping of that to DOS attr. + //struct stat s; int res=stat(fn,&s); if (res!=0) return false; + // translate windows file attributes into zip ones. + BY_HANDLE_FILE_INFORMATION bhi; + BOOL res=GetFileInformationByHandle(hf,&bhi); + if (!res) + return ZR_NOFILE; + + // +++1.3 + /// Convert times from UTC to local time. MSDN says that FILETIME is local + /// for FAT file system and UTC for NTFS system, but tests show that both FAT and NTFS + /// return UTC time. + { + // Get time zone difference + SYSTEMTIME stUTC, stLocal; + GetSystemTime(&stUTC); + GetLocalTime(&stLocal); // could be a few milliseconds difference, but should we care? + FILETIME ftUTC, ftLocal; + SystemTimeToFileTime(&stUTC, &ftUTC); + SystemTimeToFileTime(&stLocal, &ftLocal); + LONG64 uiUTC, uiLocal; + memcpy (&uiUTC, &ftUTC, min(sizeof(LONG64), sizeof(FILETIME))); // use 'min' as safeguard, however both sizes should be the same: 64-bit + memcpy (&uiLocal, &ftLocal, min(sizeof(LONG64), sizeof(FILETIME))); + LONG64 uiTimeDiff = uiUTC - uiLocal; + + // apply difference + FILETIME* pFileTimes[3] = { &bhi.ftLastWriteTime, &bhi.ftLastAccessTime, &bhi.ftCreationTime }; + for (int i=0; i<3; i++){ + LONG64 uiUTC_file; + memcpy (&uiUTC_file, pFileTimes[i], min(sizeof(LONG64), sizeof(FILETIME))); + LONG64 uiLocal_file = uiUTC_file - uiTimeDiff; + memcpy (pFileTimes[i], &uiLocal_file, min(sizeof(LONG64), sizeof(FILETIME))); + } + } + + DWORD fa=bhi.dwFileAttributes; + ulg a=0; + // Zip uses the lower word for its interpretation of windows stuff + if (fa&FILE_ATTRIBUTE_READONLY) a|=0x01; + if (fa&FILE_ATTRIBUTE_HIDDEN) a|=0x02; + if (fa&FILE_ATTRIBUTE_SYSTEM) a|=0x04; + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x10; + if (fa&FILE_ATTRIBUTE_ARCHIVE) a|=0x20; + // It uses the upper word for standard unix attr, which we must manually construct + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x40000000; // directory + else a|=0x80000000; // normal file + a|=0x01000000; // readable + if (fa&FILE_ATTRIBUTE_READONLY) {} + else a|=0x00800000; // writeable + // now just a small heuristic to check if it's an executable: + DWORD red, hsize=GetFileSize(hf,NULL); if (hsize>40) + { SetFilePointer(hf,0,NULL,FILE_BEGIN); unsigned short magic; ReadFile(hf,&magic,sizeof(magic),&red,NULL); + SetFilePointer(hf,36,NULL,FILE_BEGIN); unsigned long hpos; ReadFile(hf,&hpos,sizeof(hpos),&red,NULL); + if (magic==0x54AD && hsize>hpos+4+20+28) + { SetFilePointer(hf,hpos,NULL,FILE_BEGIN); unsigned long signature; ReadFile(hf,&signature,sizeof(signature),&red,NULL); + if (signature==IMAGE_DOS_SIGNATURE || signature==IMAGE_OS2_SIGNATURE + || signature==IMAGE_OS2_SIGNATURE_LE || signature==IMAGE_NT_SIGNATURE) + { a |= 0x00400000; // executable + } + } + } + // + if (attr!=NULL) *attr = a; + if (size!=NULL) *size = hsize; + if (times!=NULL) + { // time_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970. + // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601 + times->atime = filetime2timet(bhi.ftLastAccessTime); + times->mtime = filetime2timet(bhi.ftLastWriteTime); + times->ctime = filetime2timet(bhi.ftCreationTime); + } + if (timestamp!=NULL) + { WORD dosdate,dostime; + FileTimeToDosDateTime(&bhi.ftLastWriteTime,&dosdate,&dostime); + *timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + } + return ZR_OK; +} + + + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class TZip +{ public: + TZip() : hfout(0),hmapout(0),zfis(0),obuf(0),hfin(0),writ(0),oerr(false),hasputcen(false),ooffset(0) {} + ~TZip() {} + + // These variables say about the file we're writing into + // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile + HANDLE hfout; // if valid, we'll write here (for files or pipes) + HANDLE hmapout; // otherwise, we'll write here (for memmap) + unsigned ooffset; // for hfout, this is where the pointer was initially + ZRESULT oerr; // did a write operation give rise to an error? + unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks + bool ocanseek; // can we seek? + char *obuf; // this is where we've locked mmap to view. + unsigned int opos; // current pos in the mmap + unsigned int mapsize; // the size of the map we created + bool hasputcen; // have we yet placed the central directory? + // + TZipFileInfo *zfis; // each file gets added onto this list, for writing the table at the end + + ZRESULT Create(void *z,unsigned int len,DWORD flags); + static unsigned sflush(void *param,const char *buf, unsigned *size); + static unsigned swrite(void *param,const char *buf, unsigned size); + unsigned int write(const char *buf,unsigned int size); + bool oseek(unsigned int pos); + ZRESULT GetMemory(void **pbuf, unsigned long *plen); + ZRESULT Close(); + + // some variables to do with the file currently being read: + // I haven't done it object-orientedly here, just put them all + // together, since OO didn't seem to make the design any clearer. + ulg attr; iztimes times; ulg timestamp; // all open_* methods set these + bool iseekable; long isize,ired; // size is not set until close() on pips + ulg crc; // crc is not set until close(). iwrit is cumulative + HANDLE hfin; bool selfclosehf; // for input files and pipes + const char *bufin; unsigned int lenin,posin; // for memory + // and a variable for what we've done with the input: (i.e. compressed it!) + ulg csize; // compressed size, set by the compression routines + // and this is used by some of the compression routines + char buf[16384]; + + + ZRESULT open_file(const TCHAR *fn); + ZRESULT open_handle(HANDLE hf,unsigned int len); + ZRESULT open_mem(void *src,unsigned int len); + ZRESULT open_dir(); + static unsigned sread(TState &s,char *buf,unsigned size); + unsigned read(char *buf, unsigned size); + ZRESULT iclose(); + + ZRESULT ideflate(TZipFileInfo *zfi); + ZRESULT istore(); + + ZRESULT Add(const char *odstzn, void *src,unsigned int len, DWORD flags); + ZRESULT AddCentral(); + +}; + +ZRESULT TZip::Create(void *z,unsigned int len,DWORD flags) +{ + if (hfout!=0 || hmapout!=0 || obuf!=0 || writ!=0 || oerr!=ZR_OK || hasputcen) + return ZR_NOTINITED; + // + if (flags==ZIP_HANDLE) + { + HANDLE hf = (HANDLE)z; + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&hfout,0,FALSE,DUPLICATE_SAME_ACCESS); + if (!res) + return ZR_NODUPH; + // now we have our own hfout, which we must close. And the caller will close hf + DWORD type = GetFileType(hfout); + ocanseek = (type==FILE_TYPE_DISK); + if (type==FILE_TYPE_DISK) + ooffset=SetFilePointer(hfout,0,NULL,FILE_CURRENT); + else + ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_FILENAME) + { +#ifdef _UNICODE + const TCHAR *fn = (const TCHAR*)z; + hfout = CreateFileW(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#else + const char *fn = (const char*)z; + hfout = CreateFileA(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#endif + + if (hfout==INVALID_HANDLE_VALUE) + { + hfout=0; + return ZR_NOFILE; + } + ocanseek=true; + ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_MEMORY) + { + unsigned int size = len; + if (size==0) + return ZR_MEMSIZE; + if (z!=0) + obuf=(char*)z; + else + { + hmapout = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,size,NULL); + if (hmapout==NULL) + return ZR_NOALLOC; + obuf = (char*)MapViewOfFile(hmapout,FILE_MAP_ALL_ACCESS,0,0,size); + if (obuf==0) + { + CloseHandle(hmapout); + hmapout=0; + return ZR_NOALLOC; + } + } + ocanseek=true; + opos=0; + mapsize=size; + return ZR_OK; + } + else + return ZR_ARGS; +} + + +unsigned TZip::sflush(void *param,const char *buf, unsigned *size) +{ // static + if (*size==0) return 0; + TZip *zip = (TZip*)param; + unsigned int writ = zip->write(buf,*size); + if (writ!=0) *size=0; + return writ; +} +unsigned TZip::swrite(void *param,const char *buf, unsigned size) +{ // static + if (size==0) return 0; + TZip *zip=(TZip*)param; return zip->write(buf,size); +} + +#if 0 // ----------------------------------------------------------- +unsigned int TZip::write(const char *buf,unsigned int size) +{ if (obuf!=0) + { if (opos+size>=mapsize) {oerr=ZR_MEMSIZE; return 0;} + memcpy(obuf+opos, buf, size); + opos+=size; + return size; + } + else if (hfout!=0) + { DWORD writ; WriteFile(hfout,buf,size,&writ,NULL); + return writ; + } + oerr=ZR_NOTINITED; return 0; +} +#endif // ----------------------------------------------------------- + +//+++1.2 +unsigned int TZip::write(const char *buf, unsigned int size) +{ + if (obuf != 0) + { + if (opos+size >= mapsize) + { + int newmapsize = 2*mapsize>opos+size?2*mapsize:opos+size; + HANDLE hmapout2 = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,newmapsize,NULL); + if (hmapout2 == NULL) + return ZR_NOALLOC; + char *obuf2 = NULL; // this is where we've locked mmap to view. + + obuf2 = (char*)MapViewOfFile(hmapout2,FILE_MAP_ALL_ACCESS,0,0,newmapsize); + if (obuf2 == 0) + { + CloseHandle(hmapout2); + hmapout2 = 0; + return ZR_NOALLOC; + } + + memcpy(obuf2, obuf, mapsize); + + UnmapViewOfFile(obuf); + CloseHandle(hmapout); + + mapsize = newmapsize; + obuf = obuf2; + hmapout = hmapout2; + } + memcpy(obuf+opos, buf, size); + opos += size; + return size; + } + else if (hfout!=0) + { + DWORD writ = 0; + WriteFile(hfout,buf,size,&writ,NULL); + return writ; + } + oerr = ZR_NOTINITED; + return 0; +} + + +bool TZip::oseek(unsigned int pos) +{ if (!ocanseek) {oerr=ZR_SEEK; return false;} + if (obuf!=0) + { if (pos>=mapsize) {oerr=ZR_MEMSIZE; return false;} + opos=pos; + return true; + } + else if (hfout!=0) + { SetFilePointer(hfout,pos+ooffset,NULL,FILE_BEGIN); + return true; + } + oerr=ZR_NOTINITED; return 0; +} + +ZRESULT TZip::GetMemory(void **pbuf, unsigned long *plen) +{ // When the user calls GetMemory, they're presumably at the end + // of all their adding. In any case, we have to add the central + // directory now, otherwise the memory we tell them won't be complete. + if (!hasputcen) AddCentral(); hasputcen=true; + if (pbuf!=NULL) *pbuf=(void*)obuf; + if (plen!=NULL) *plen=writ; + if (obuf==NULL) return ZR_NOTMMAP; + return ZR_OK; +} + +ZRESULT TZip::Close() +{ // if the directory hadn't already been added through a call to GetMemory, + // then we do it now + ZRESULT res=ZR_OK; if (!hasputcen) res=AddCentral(); hasputcen=true; + if (obuf!=0 && hmapout!=0) UnmapViewOfFile(obuf); obuf=0; + if (hmapout!=0) CloseHandle(hmapout); hmapout=0; + if (hfout!=0) CloseHandle(hfout); hfout=0; + return res; +} + + + + +ZRESULT TZip::open_file(const TCHAR *fn) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (fn==0) return ZR_ARGS; + HANDLE hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); + if (hf==INVALID_HANDLE_VALUE) return ZR_NOFILE; + ZRESULT res = open_handle(hf,0); + if (res!=ZR_OK) {CloseHandle(hf); return res;} + selfclosehf=true; + return ZR_OK; +} +ZRESULT TZip::open_handle(HANDLE hf,unsigned int len) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (hf==0 || hf==INVALID_HANDLE_VALUE) return ZR_ARGS; + DWORD type = GetFileType(hf); + if (type==FILE_TYPE_DISK) + { ZRESULT res = GetFileInfo(hf,&attr,&isize,×,×tamp); + if (res!=ZR_OK) return res; + SetFilePointer(hf,0,NULL,FILE_BEGIN); // because GetFileInfo will have screwed it up + iseekable=true; hfin=hf; + return ZR_OK; + } + else + { attr= 0x80000000; // just a normal file + isize = -1; // can't know size until at the end + if (len!=0) isize=len; // unless we were told explicitly! + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + hfin=hf; + return ZR_OK; + } +} +ZRESULT TZip::open_mem(void *src,unsigned int len) +{ hfin=0; bufin=(const char*)src; selfclosehf=false; crc=CRCVAL_INITIAL; ired=0; csize=0; ired=0; + lenin=len; posin=0; + if (src==0 || len==0) return ZR_ARGS; + attr= 0x80000000; // just a normal file + isize = len; + iseekable=true; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} +ZRESULT TZip::open_dir() +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + attr= 0x41C00010; // a readable writable directory, and again directory + isize = 0; + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} + +unsigned TZip::sread(TState &s,char *buf,unsigned size) +{ // static + TZip *zip = (TZip*)s.param; + return zip->read(buf,size); +} + +unsigned TZip::read(char *buf, unsigned size) +{ if (bufin!=0) + { if (posin>=lenin) return 0; // end of input + ulg red = lenin-posin; + if (red>size) red=size; + memcpy(buf, bufin+posin, red); + posin += red; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else if (hfin!=0) + { DWORD red; + BOOL ok = ReadFile(hfin,buf,size,&red,NULL); + if (!ok) return 0; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else {oerr=ZR_NOTINITED; return 0;} +} + +ZRESULT TZip::iclose() +{ if (selfclosehf && hfin!=0) CloseHandle(hfin); hfin=0; + bool mismatch = (isize!=-1 && isize!=ired); + isize=ired; // and crc has been being updated anyway + if (mismatch) return ZR_MISSIZE; + else return ZR_OK; +} + + + +#if 0 // ----------------------------------------------------------- +ZRESULT TZip::ideflate(TZipFileInfo *zfi) +{ TState state; + state.readfunc=sread; state.flush_outbuf=sflush; + state.param=this; state.level=8; state.seekable=iseekable; state.err=NULL; + // the following line will make ct_init realise it has to perform the init + state.ts.static_dtree[0].dl.len = 0; + // It would be nicer if I could figure out precisely which data had to + // be initted each time, and which didn't, but that's kind of difficult. + // Maybe for the next version... + // + bi_init(state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(state,&zfi->att); + lm_init(state,state.level, &zfi->flg); + ulg sz = deflate(state); + csize=sz; + if (state.err!=NULL) return ZR_FLATE; + else return ZR_OK; +} +#endif // ----------------------------------------------------------- + +//+++1.2 +// create state object on heap +ZRESULT TZip::ideflate(TZipFileInfo *zfi) +{ + ZRESULT zr = ZR_OK; + TState* state=new TState(); + (*state).readfunc=sread; (*state).flush_outbuf=sflush; + (*state).param=this; (*state).level=8; (*state).seekable=iseekable; (*state).err=NULL; + // the following line will make ct_init realise it has to perform the init + (*state).ts.static_dtree[0].dl.len = 0; + // It would be nicer if I could figure out precisely which data had to + // be initted each time, and which didn't, but that's kind of difficult. + // Maybe for the next version... + // + bi_init(*state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(*state,&zfi->att); + lm_init(*state,(*state).level, &zfi->flg); + ulg sz = deflate(*state); + csize=sz; + if ((*state).err!=NULL) + { + zr = ZR_FLATE; + } + delete state; + return zr; +} + +ZRESULT TZip::istore() +{ ulg size=0; + for (;;) + { unsigned int cin=read(buf,16384); if (cin<=0 || cin==(unsigned int)EOF) break; + unsigned int cout = write(buf,cin); if (cout!=cin) return ZR_MISSIZE; + size += cin; + } + csize=size; + return ZR_OK; +} + + + + +ZRESULT TZip::Add(const char *odstzn, void *src,unsigned int len, DWORD flags) +{ + if (oerr) + return ZR_FAILED; + if (hasputcen) + return ZR_ENDED; + + // zip has its own notion of what its names should look like: i.e. dir/file.stuff + char dstzn[MAX_PATH]; + strcpy(dstzn, odstzn); + if (*dstzn == 0) + return ZR_ARGS; + char *d=dstzn; + while (*d != 0) + { + if (*d == '\\') + *d = '/'; d++; + } + bool isdir = (flags==ZIP_FOLDER); + bool needs_trailing_slash = (isdir && dstzn[strlen(dstzn)-1]!='/'); + int method=DEFLATE; + if (isdir || HasZipSuffix(dstzn)) + method=STORE; + + // now open whatever was our input source: + ZRESULT openres; + if (flags==ZIP_FILENAME) + openres=open_file((const TCHAR*)src); + else if (flags==ZIP_HANDLE) + openres=open_handle((HANDLE)src,len); + else if (flags==ZIP_MEMORY) + openres=open_mem(src,len); + else if (flags==ZIP_FOLDER) + openres=open_dir(); + else return ZR_ARGS; + if (openres!=ZR_OK) + return openres; + + // A zip "entry" consists of a local header (which includes the file name), + // then the compressed data, and possibly an extended local header. + + // Initialize the local header + TZipFileInfo zfi; zfi.nxt=NULL; + strcpy(zfi.name,""); + strcpy(zfi.iname,dstzn); + zfi.nam=strlen(zfi.iname); + if (needs_trailing_slash) + { + strcat(zfi.iname,"/"); + zfi.nam++; + } + strcpy(zfi.zname,""); + zfi.extra=NULL; zfi.ext=0; // extra header to go after this compressed data, and its length + zfi.cextra=NULL; zfi.cext=0; // extra header to go in the central end-of-zip directory, and its length + zfi.comment=NULL; zfi.com=0; // comment, and its length + zfi.mark = 1; + zfi.dosflag = 0; + zfi.att = (ush)BINARY; + zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3 + zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it + zfi.tim = timestamp; + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfi.crc = 0; // to be updated later + zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it. + zfi.lflg = zfi.flg; // to be updated later + zfi.how = (ush)method; // to be updated later + zfi.siz = (ulg)(method==STORE && isize>=0 ? isize : 0); // to be updated later + zfi.len = (ulg)(isize); // to be updated later + zfi.dsk = 0; + zfi.atx = attr; + zfi.off = writ+ooffset; // offset within file of the start of this local record + // stuff the 'times' structure into zfi.extra + char xloc[EB_L_UT_SIZE]; + zfi.extra=xloc; + zfi.ext=EB_L_UT_SIZE; + char xcen[EB_C_UT_SIZE]; + zfi.cextra=xcen; + zfi.cext=EB_C_UT_SIZE; + xloc[0] = 'U'; + xloc[1] = 'T'; + xloc[2] = EB_UT_LEN(3); // length of data part of e.f. + xloc[3] = 0; + xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME; + xloc[5] = (char)(times.mtime); + xloc[6] = (char)(times.mtime >> 8); + xloc[7] = (char)(times.mtime >> 16); + xloc[8] = (char)(times.mtime >> 24); + xloc[9] = (char)(times.atime); + xloc[10] = (char)(times.atime >> 8); + xloc[11] = (char)(times.atime >> 16); + xloc[12] = (char)(times.atime >> 24); + xloc[13] = (char)(times.ctime); + xloc[14] = (char)(times.ctime >> 8); + xloc[15] = (char)(times.ctime >> 16); + xloc[16] = (char)(times.ctime >> 24); + memcpy(zfi.cextra,zfi.extra,EB_C_UT_SIZE); + zfi.cextra[EB_LEN] = EB_UT_LEN(1); + + + // (1) Start by writing the local header: + int r = putlocal(&zfi,swrite,this); + if (r!=ZE_OK) + { + iclose(); + return ZR_WRITE; + } + writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext; + if (oerr!=ZR_OK) + { + iclose(); + return oerr; + } + + //(2) Write deflated/stored file to zip file + ZRESULT writeres=ZR_OK; + if (!isdir && method==DEFLATE) + writeres=ideflate(&zfi); + else if (!isdir && method==STORE) + writeres=istore(); + else if (isdir) + csize=0; + iclose(); + writ += csize; + if (oerr!=ZR_OK) + return oerr; + if (writeres!=ZR_OK) + return ZR_WRITE; + + // (3) Either rewrite the local header with correct information... + bool first_header_has_size_right = (zfi.siz==csize); + zfi.crc = crc; + zfi.siz = csize; + zfi.len = isize; + if (ocanseek) + { + zfi.how = (ush)method; + if ((zfi.flg & 1) == 0) + zfi.flg &= ~8; // clear the extended local header flag + zfi.lflg = zfi.flg; + // rewrite the local header: + if (!oseek(zfi.off-ooffset)) + return ZR_SEEK; + if ((r = putlocal(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + if (!oseek(writ)) + return ZR_SEEK; + } + else + { + // (4) ... or put an updated header at the end + if (zfi.how != (ush) method) + return ZR_NOCHANGE; + if (method==STORE && !first_header_has_size_right) + return ZR_NOCHANGE; + if ((r = putextended(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + writ += 16L; + zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index + } + if (oerr!=ZR_OK) + return oerr; + + // Keep a copy of the zipfileinfo, for our end-of-zip directory + char *cextra = new char[zfi.cext]; + memcpy(cextra,zfi.cextra,zfi.cext); zfi.cextra=cextra; + TZipFileInfo *pzfi = new TZipFileInfo; + memcpy(pzfi,&zfi,sizeof(zfi)); + if (zfis==NULL) + zfis=pzfi; + else + { + TZipFileInfo *z=zfis; + while (z->nxt!=NULL) + z=z->nxt; + z->nxt=pzfi; + } + return ZR_OK; +} + +ZRESULT TZip::AddCentral() +{ // write central directory + int numentries = 0; + ulg pos_at_start_of_central = writ; + //ulg tot_unc_size=0, tot_compressed_size=0; + bool okay=true; + for (TZipFileInfo *zfi=zfis; zfi!=NULL; ) + { if (okay) + { int res = putcentral(zfi, swrite,this); + if (res!=ZE_OK) okay=false; + } + writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com; + //tot_unc_size += zfi->len; + //tot_compressed_size += zfi->siz; + numentries++; + // + TZipFileInfo *zfinext = zfi->nxt; + if (zfi->cextra!=0) delete[] zfi->cextra; + delete zfi; + zfi = zfinext; + } + ulg center_size = writ - pos_at_start_of_central; + if (okay) + { int res = putend(numentries, center_size, pos_at_start_of_central+ooffset, 0, NULL, swrite,this); + if (res!=ZE_OK) okay=false; + writ += 4 + ENDHEAD + 0; + } + if (!okay) return ZR_WRITE; + return ZR_OK; +} + + + + + +ZRESULT lasterrorZ=ZR_OK; + +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorZ; + const char *msg="unknown zip result code"; + switch (code) + { case ZR_OK: msg="Success"; break; + case ZR_NODUPH: msg="Culdn't duplicate handle"; break; + case ZR_NOFILE: msg="Couldn't create/open file"; break; + case ZR_NOALLOC: msg="Failed to allocate memory"; break; + case ZR_WRITE: msg="Error writing to file"; break; + case ZR_NOTFOUND: msg="File not found in the zipfile"; break; + case ZR_MORE: msg="Still more data to unzip"; break; + case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg="Error reading file"; break; + case ZR_ARGS: msg="Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg="Caller: there was a previous error"; break; + case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break; + } + unsigned int mlen=(unsigned int)strlen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + + +typedef struct +{ DWORD flag; + TZip *zip; +} TZipHandleData; + + +HZIP CreateZipZ(void *z,unsigned int len,DWORD flags) +{ + tzset(); + TZip *zip = new TZip(); + lasterrorZ = zip->Create(z,len,flags); + if (lasterrorZ != ZR_OK) + { + delete zip; + return 0; + } + TZipHandleData *han = new TZipHandleData; + han->flag = 2; + han->zip = zip; + return (HZIP)han; +} + +ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags) +{ + if (hz == 0) + { + lasterrorZ = ZR_ARGS; + return ZR_ARGS; + } + + if (dstzn == NULL) + { + lasterrorZ = ZR_ARGS; + return ZR_ARGS; + } + + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag != 2) + { + lasterrorZ = ZR_ZMODE; + return ZR_ZMODE; + } + TZip *zip = han->zip; + + + char szDest[MAX_PATH*2]; + memset(szDest, 0, sizeof(szDest)); + +#ifdef _UNICODE + // need to convert Unicode dest to ANSI + int nActualChars = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + (LPCWSTR) dstzn, // wide-character string + -1, // number of chars in string + szDest, // buffer for new string + MAX_PATH*2, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + if (nActualChars == 0) + return ZR_ARGS; +#else + strcpy(szDest, dstzn); +#endif + + lasterrorZ = zip->Add(szDest, src, len, flags); + + return lasterrorZ; +} + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len) +{ if (hz==0) {if (buf!=0) *buf=0; if (len!=0) *len=0; lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->GetMemory(buf,len); + return lasterrorZ; +} + +ZRESULT CloseZipZ(HZIP hz) +{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->Close(); + delete zip; + delete han; + return lasterrorZ; +} + +bool IsZipHandleZ(HZIP hz) +{ if (hz==0) return true; + TZipHandleData *han = (TZipHandleData*)hz; + return (han->flag==2); +} + +//+++1.2 +/** +* Added by Renaud Deysine. This fonctionnality was missing in API +* @brief Add a folder to the zip file. Empty folders will also be added. +* This method add recursively the content of a directory +* @param AbsolutePath like "C:\\Windows" or "C:\\Windows\" +* @param DirToAdd like "System32" +* +*/ +BOOL AddFolderContent(HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd) +{ + HANDLE hFind; // file handle + WIN32_FIND_DATA FindFileData; + TCHAR PathToSearchInto [MAX_PATH] = {0}; + + if (NULL != DirToAdd) + { + ZipAdd(hZip, DirToAdd, 0, 0, ZIP_FOLDER); + } + + // Construct the path to search into "C:\\Windows\\System32\\*" + _tcscpy(PathToSearchInto, AbsolutePath); + _tcscat(PathToSearchInto, _T("\\")); + _tcscat(PathToSearchInto, DirToAdd); + _tcscat(PathToSearchInto, _T("\\*")); + + hFind = FindFirstFile(PathToSearchInto,&FindFileData); // find the first file + if(hFind == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + bool bSearch = true; + while(bSearch) // until we finds an entry + { + if(FindNextFile(hFind,&FindFileData)) + { + // Don't care about . and .. + //if(IsDots(FindFileData.cFileName)) + if ((_tcscmp(FindFileData.cFileName, _T(".")) == 0) || + (_tcscmp(FindFileData.cFileName, _T("..")) == 0)) + continue; + + // We have found a directory + if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + TCHAR RelativePathNewDirFound[MAX_PATH] = {0}; + _tcscat(RelativePathNewDirFound, DirToAdd); + _tcscat(RelativePathNewDirFound, _T("\\")); + _tcscat(RelativePathNewDirFound, FindFileData.cFileName); + + // Recursive call with the new directory found + if (AddFolderContent(hZip, AbsolutePath, RelativePathNewDirFound)== FALSE) + { + return FALSE ; + } + + } + // We have found a file + else + { + // Add the found file to the zip file + TCHAR RelativePathNewFileFound[MAX_PATH] = {0}; + _tcscpy(RelativePathNewFileFound, DirToAdd); + _tcscat(RelativePathNewFileFound, _T("\\")); + _tcscat(RelativePathNewFileFound, FindFileData.cFileName); + + TCHAR AbsoluteSourceFile[MAX_PATH] = { 0 }; + _tcscpy(AbsoluteSourceFile, AbsolutePath); + _tcscat(AbsoluteSourceFile, RelativePathNewFileFound); + + if (ZipAdd(hZip, RelativePathNewFileFound, AbsoluteSourceFile, 0, ZIP_FILENAME) != ZR_OK) + { + return FALSE; + } + } + + }//FindNextFile + else + { + if(GetLastError() == ERROR_NO_MORE_FILES) // no more files there + bSearch = false; + else { + // some error occured, close the handle and return FALSE + FindClose(hFind); + return FALSE; + } + } + }//while + + FindClose(hFind); // closing file handle + return true; + +} + diff --git a/src/Common/XZip.h b/src/Common/XZip.h new file mode 100644 index 00000000..b1d43a92 --- /dev/null +++ b/src/Common/XZip.h @@ -0,0 +1,323 @@ +// XZip.h Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XZIP_H +#define XZIP_H + +// ZIP functions -- for creating zip files +// This file is a repackaged form of the Info-Zip source code available +// at www.info-zip.org. The original copyright notice may be found in +// zip.cpp. The repackaging was done by Lucian Wischik to simplify its +// use in Windows/C++. + +#ifndef XUNZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that is being created +#endif + +typedef DWORD ZRESULT; // result codes from any of the zip functions. Listed later. + +// flag values passed to some functions +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 +#define ZIP_FOLDER 4 + + +/////////////////////////////////////////////////////////////////////////////// +// +// CreateZip() +// +// Purpose: Create a zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive created ok, otherwise 0 +// +HZIP CreateZip(void *z, unsigned int len, DWORD flags); +// CreateZip - call this to start the creation of a zip file. +// As the zip is being created, it will be stored somewhere: +// to a pipe: CreateZip(hpipe_write, 0,ZIP_HANDLE); +// in a file (by handle): CreateZip(hfile, 0,ZIP_HANDLE); +// in a file (by name): CreateZip("c:\\test.zip", 0,ZIP_FILENAME); +// in memory: CreateZip(buf, len,ZIP_MEMORY); +// or in pagefile memory: CreateZip(0, len,ZIP_MEMORY); +// The final case stores it in memory backed by the system paging file, +// where the zip may not exceed len bytes. This is a bit friendlier than +// allocating memory with new[]: it won't lead to fragmentation, and the +// memory won't be touched unless needed. +// Note: because pipes don't allow random access, the structure of a zipfile +// created into a pipe is slightly different from that created into a file +// or memory. In particular, the compressed-size of the item cannot be +// stored in the zipfile until after the item itself. (Also, for an item added +// itself via a pipe, the uncompressed-size might not either be known until +// after.) This is not normally a problem. But if you try to unzip via a pipe +// as well, then the unzipper will not know these things about the item until +// after it has been unzipped. Therefore: for unzippers which don't just write +// each item to disk or to a pipe, but instead pre-allocate memory space into +// which to unzip them, then either you have to create the zip not to a pipe, +// or you have to add items not from a pipe, or at least when adding items +// from a pipe you have to specify the length. + + +/////////////////////////////////////////////////////////////////////////////// +// +// ZipAdd() +// +// Purpose: Add a file to a zip archive +// +// Parameters: hz - handle to an open zip archive +// dstzn - name used inside the zip archive to identify the file +// src - for a file (ZIP_FILENAME) this specifies the filename +// to be added to the archive; for other uses, see below +// len - for memory (ZIP_MEMORY) this specifies the buffer +// length; for other uses, this should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags); +// ZipAdd - call this for each file to be added to the zip. +// dstzn is the name that the file will be stored as in the zip file. +// The file to be added to the zip can come +// from a pipe: ZipAdd(hz,"file.dat", hpipe_read,0,ZIP_HANDLE); +// from a file: ZipAdd(hz,"file.dat", hfile,0,ZIP_HANDLE); +// from a fname: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat",0,ZIP_FILENAME); +// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len,ZIP_MEMORY); +// (folder): ZipAdd(hz,"subdir", 0,0,ZIP_FOLDER); +// Note: if adding an item from a pipe, and if also creating the zip file itself +// to a pipe, then you might wish to pass a non-zero length to the ZipAdd +// function. This will let the zipfile store the items size ahead of the +// compressed item itself, which in turn makes it easier when unzipping the +// zipfile into a pipe. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len); +// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,ZIP_MEMORY), +// then this function will return information about that memory block. +// buf will receive a pointer to its start, and len its length. +// Note: you can't add any more after calling this. + + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + +// e.g. +// +// (1) Traditional use, creating a zipfile from existing files +// HZIP hz = CreateZip("c:\\temp.zip",0,ZIP_FILENAME); +// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME); +// ZipAdd(hz,"src2.bmp", "c:\\src2_origfn.bmp",0,ZIP_FILENAME); +// CloseZip(hz); +// +// (2) Memory use, creating an auto-allocated mem-based zip file from various sources +// HZIP hz = CreateZip(0,100000,ZIP_MEMORY); +// // adding a conventional file... +// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME); +// // adding something from memory... +// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F); +// ZipAdd(hz,"file.dat", buf,1000,ZIP_MEMORY); +// // adding something from a pipe... +// HANDLE hread,hwrite; CreatePipe(&hread,&write,NULL,0); +// HANDLE hthread = CreateThread(ThreadFunc,(void*)hwrite); +// ZipAdd(hz,"unz3.dat", hread,0,ZIP_HANDLE); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); CloseHandle(hread); +// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat) +// { HANDLE hwrite = (HANDLE)dat; +// char buf[1000]={17}; +// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL); +// CloseHandle(hwrite); +// return 0; +// } +// // and now that the zip is created, let's do something with it: +// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen); +// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,CREATE_ALWAYS); +// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL); +// CloseHandle(hfz); +// CloseZip(hz); +// +// (3) Handle use, for file handles and pipes +// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite); +// HANDLE hthread = CreateThread(ZipReceiverThread,(void*)hread); +// HZIP hz = ZipCreate(hzwrite,ZIP_HANDLE); +// // ... add to it +// CloseZip(hz); +// CloseHandle(hzwrite); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); +// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat) +// { HANDLE hread = (HANDLE)dat; +// char buf[1000]; +// while (true) +// { DWORD red; ReadFile(hread,buf,1000,&red,NULL); +// // ... and do something with this zip data we're receiving +// if (red==0) break; +// } +// CloseHandle(hread); +// return 0; +// } +// + + +// Now we indulge in a little skullduggery so that the code works whether +// the user has included just zip or both zip and unzip. +// Idea: if header files for both zip and unzip are present, then presumably +// the cpp files for zip and unzip are both present, so we will call +// one or the other of them based on a dynamic choice. If the header file +// for only one is present, then we will bind to that particular one. +HZIP CreateZipZ(void *z,unsigned int len,DWORD flags); +ZRESULT CloseZipZ(HZIP hz); +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len); +bool IsZipHandleZ(HZIP hz); +BOOL AddFolderContent(HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd); + +#define CreateZip CreateZipZ + +#ifdef XUNZIP_H +#undef CloseZip +#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz)) +#else +#define CloseZip CloseZipZ +#define FormatZipMessage FormatZipMessageZ +#endif + + +#endif //XZIP_H diff --git a/src/Common/Xml.c b/src/Common/Xml.c index d733dac1..6abbed6d 100644 --- a/src/Common/Xml.c +++ b/src/Common/Xml.c @@ -9,9 +9,13 @@ contained in the file License.txt included in VeraCrypt binary and source code distribution packages. */ - +#if !defined(_UEFI) #include <windows.h> #include <stdio.h> +#else +#include "Tcdefs.h" +#pragma warning( disable : 4706 ) // assignment within conditional expression +#endif #include "Xml.h" @@ -59,7 +63,7 @@ char *XmlFindElement (char *xmlNode, char *nodeName) } -char *XmlFindElementByAttributeValue (char *xml, char *nodeName, char *attrName, char *attrValue) +char *XmlFindElementByAttributeValue (char *xml, char *nodeName, const char *attrName, const char *attrValue) { char attr[2048]; @@ -76,7 +80,7 @@ char *XmlFindElementByAttributeValue (char *xml, char *nodeName, char *attrName, } -char *XmlGetAttributeText (char *xmlNode, char *xmlAttrName, char *xmlAttrValue, int xmlAttrValueSize) +char *XmlGetAttributeText (char *xmlNode, const char *xmlAttrName, char *xmlAttrValue, int xmlAttrValueSize) { char *t = xmlNode; char *e = xmlNode; @@ -105,7 +109,7 @@ char *XmlGetAttributeText (char *xmlNode, char *xmlAttrName, char *xmlAttrValue, if (t == NULL || t > e) return NULL; - t = strchr (t, '"') + 1; + t = ((char*)strchr (t, '"')) + 1; e = strchr (t, '"'); l = (int)(e - t); if (e == NULL || l > xmlAttrValueSize) return NULL; @@ -128,9 +132,10 @@ char *XmlGetNodeText (char *xmlNode, char *xmlText, int xmlTextSize) if (t[0] != '<') return NULL; - t = strchr (t, '>') + 1; - if (t == (char *)1) return NULL; + t = (char*) strchr (t, '>'); + if (t == NULL) return NULL; + t++; e = strchr (e, '<'); if (e == NULL) return NULL; @@ -255,7 +260,8 @@ wchar_t *XmlQuoteTextW (const wchar_t *textSrc, wchar_t *textDst, int textDstMax return textDst; } - +#if !defined(_UEFI) +#pragma warning( default : 4706 ) int XmlWriteHeader (FILE *file) { return fputws (L"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<VeraCrypt>", file); @@ -266,3 +272,4 @@ int XmlWriteFooter (FILE *file) { return fputws (L"\n</VeraCrypt>", file); } +#endif !defined(_UEFI) diff --git a/src/Common/Xml.h b/src/Common/Xml.h index 3dfb58e1..ceae2bf1 100644 --- a/src/Common/Xml.h +++ b/src/Common/Xml.h @@ -3,7 +3,7 @@ Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed by the TrueCrypt License 3.0. - Modifications and additions to the original source code (contained in this file) + Modifications and additions to the original source code (contained in this file) and all other portions of this file are Copyright (c) 2013-2016 IDRIX and are governed by the Apache License 2.0 the full text of which is contained in the file License.txt included in VeraCrypt binary and source @@ -16,14 +16,17 @@ extern "C" { char *XmlNextNode (char *xmlNode); char *XmlFindElement (char *xmlNode, char *nodeName); -char *XmlGetAttributeText (char *xmlNode, char *xmlAttrName, char *xmlAttrValue, int xmlAttrValueSize); +char *XmlGetAttributeText (char *xmlNode, const char *xmlAttrName, char *xmlAttrValue, int xmlAttrValueSize); char *XmlGetNodeText (char *xmlNode, char *xmlText, int xmlTextSize); -int XmlWriteHeader (FILE *file); -int XmlWriteFooter (FILE *file); -char *XmlFindElementByAttributeValue (char *xml, char *nodeName, char *attrName, char *attrValue); +char *XmlFindElementByAttributeValue (char *xml, char *nodeName, const char *attrName, const char *attrValue); char *XmlQuoteText (const char *textSrc, char *textDst, int textDstMaxSize); wchar_t *XmlQuoteTextW (const wchar_t *textSrc, wchar_t *textDst, int textDstMaxSize); +#if !defined(_UEFI) +int XmlWriteHeader (FILE *file); +int XmlWriteFooter (FILE *file); +#endif !defined(_UEFI) + #ifdef __cplusplus } #endif |