mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2025-11-11 02:58:02 -06:00
1059 lines
24 KiB
C
1059 lines
24 KiB
C
/*
|
|
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
|
|
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 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"
|
|
#include "Crypto.h"
|
|
#include "Xts.h"
|
|
#include "Crc.h"
|
|
#include "Common/Endian.h"
|
|
#include <string.h>
|
|
#ifndef TC_WINDOWS_BOOT
|
|
#include "EncryptionThreadPool.h"
|
|
#endif
|
|
#include "Volumes.h"
|
|
|
|
/* Update the following when adding a new cipher or EA:
|
|
|
|
Crypto.h:
|
|
ID #define
|
|
MAX_EXPANDED_KEY #define
|
|
|
|
Crypto.c:
|
|
Ciphers[]
|
|
EncryptionAlgorithms[]
|
|
CipherInit()
|
|
EncipherBlock()
|
|
DecipherBlock()
|
|
|
|
*/
|
|
|
|
#ifndef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE
|
|
|
|
// Cipher configuration
|
|
static Cipher Ciphers[] =
|
|
{
|
|
// Block Size Key Size Key Schedule Size
|
|
// ID Name (Bytes) (Bytes) (Bytes)
|
|
#ifdef TC_WINDOWS_BOOT
|
|
{ AES, "AES", 16, 32, AES_KS },
|
|
{ SERPENT, "Serpent", 16, 32, 140*4 },
|
|
{ TWOFISH, "Twofish", 16, 32, TWOFISH_KS },
|
|
#else
|
|
{ AES, L"AES", 16, 32, AES_KS },
|
|
{ SERPENT, L"Serpent", 16, 32, 140*4 },
|
|
{ TWOFISH, L"Twofish", 16, 32, TWOFISH_KS },
|
|
#endif
|
|
{ 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
|
|
// Encryption algorithm configuration
|
|
static EncryptionAlgorithm EncryptionAlgorithms[] =
|
|
{
|
|
// Cipher(s) Modes FormatEnabled
|
|
|
|
#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
|
|
|
|
#else // TC_WINDOWS_BOOT
|
|
|
|
// Encryption algorithms available for boot drive encryption
|
|
{ { 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
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
#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 },
|
|
{ 0, 0, 0 }
|
|
};
|
|
#endif
|
|
|
|
/* Return values: 0 = success, ERR_CIPHER_INIT_FAILURE (fatal), ERR_CIPHER_INIT_WEAK_KEY (non-fatal) */
|
|
int CipherInit (int cipher, unsigned char *key, unsigned __int8 *ks)
|
|
{
|
|
int retVal = ERR_SUCCESS;
|
|
|
|
switch (cipher)
|
|
{
|
|
case AES:
|
|
#ifndef TC_WINDOWS_BOOT
|
|
if (aes_encrypt_key256 (key, (aes_encrypt_ctx *) ks) != EXIT_SUCCESS)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
|
|
if (aes_decrypt_key256 (key, (aes_decrypt_ctx *) (ks + sizeof(aes_encrypt_ctx))) != EXIT_SUCCESS)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
#else
|
|
if (aes_set_key (key, (length_type) CipherGetKeySize(AES), (aes_context *) ks) != 0)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
#endif
|
|
break;
|
|
|
|
case SERPENT:
|
|
serpent_set_key (key, ks);
|
|
break;
|
|
|
|
case TWOFISH:
|
|
twofish_set_key ((TwofishInstance *)ks, (const u4byte *)key);
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong cipher ID
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void EncipherBlock(int cipher, void *data, void *ks)
|
|
{
|
|
switch (cipher)
|
|
{
|
|
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())
|
|
aes_hw_cpu_encrypt (ks, data);
|
|
else
|
|
#endif
|
|
aes_encrypt (data, data, ks);
|
|
break;
|
|
|
|
case TWOFISH: twofish_encrypt (ks, data, data); break;
|
|
case SERPENT: serpent_encrypt (data, data, ks); break;
|
|
default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID
|
|
}
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
void EncipherBlocks (int cipher, void *dataPtr, void *ks, size_t blockCount)
|
|
{
|
|
byte *data = dataPtr;
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
KFLOATING_SAVE floatingPointState;
|
|
#endif
|
|
|
|
if (cipher == AES
|
|
&& (blockCount & (32 - 1)) == 0
|
|
&& IsAesHwCpuSupported()
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
&& NT_SUCCESS (KeSaveFloatingPointState (&floatingPointState))
|
|
#endif
|
|
)
|
|
{
|
|
while (blockCount > 0)
|
|
{
|
|
aes_hw_cpu_encrypt_32_blocks (ks, data);
|
|
|
|
data += 32 * 16;
|
|
blockCount -= 32;
|
|
}
|
|
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
KeRestoreFloatingPointState (&floatingPointState);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
size_t blockSize = CipherGetBlockSize (cipher);
|
|
while (blockCount-- > 0)
|
|
{
|
|
EncipherBlock (cipher, data, ks);
|
|
data += blockSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // !TC_WINDOWS_BOOT
|
|
|
|
void DecipherBlock(int cipher, void *data, void *ks)
|
|
{
|
|
switch (cipher)
|
|
{
|
|
case SERPENT: serpent_decrypt (data, data, ks); break;
|
|
case TWOFISH: twofish_decrypt (ks, data, data); break;
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
case AES:
|
|
#if defined (_WIN64) || !defined (TC_WINDOWS_DRIVER)
|
|
if (IsAesHwCpuSupported())
|
|
aes_hw_cpu_decrypt ((byte *) ks + sizeof (aes_encrypt_ctx), data);
|
|
else
|
|
#endif
|
|
aes_decrypt (data, data, (void *) ((char *) ks + sizeof(aes_encrypt_ctx)));
|
|
break;
|
|
|
|
#else
|
|
case AES: aes_decrypt (data, data, ks); break;
|
|
#endif
|
|
default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID
|
|
}
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
void DecipherBlocks (int cipher, void *dataPtr, void *ks, size_t blockCount)
|
|
{
|
|
byte *data = dataPtr;
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
KFLOATING_SAVE floatingPointState;
|
|
#endif
|
|
|
|
if (cipher == AES
|
|
&& (blockCount & (32 - 1)) == 0
|
|
&& IsAesHwCpuSupported()
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
&& NT_SUCCESS (KeSaveFloatingPointState (&floatingPointState))
|
|
#endif
|
|
)
|
|
{
|
|
while (blockCount > 0)
|
|
{
|
|
aes_hw_cpu_decrypt_32_blocks ((byte *) ks + sizeof (aes_encrypt_ctx), data);
|
|
|
|
data += 32 * 16;
|
|
blockCount -= 32;
|
|
}
|
|
|
|
#if defined (TC_WINDOWS_DRIVER) && !defined (_WIN64)
|
|
KeRestoreFloatingPointState (&floatingPointState);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
size_t blockSize = CipherGetBlockSize (cipher);
|
|
while (blockCount-- > 0)
|
|
{
|
|
DecipherBlock (cipher, data, ks);
|
|
data += blockSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // !TC_WINDOWS_BOOT
|
|
|
|
|
|
// Ciphers support
|
|
|
|
Cipher *CipherGet (int id)
|
|
{
|
|
int i;
|
|
for (i = 0; Ciphers[i].Id != 0; i++)
|
|
if (Ciphers[i].Id == id)
|
|
return &Ciphers[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
const wchar_t *CipherGetName (int cipherId)
|
|
{
|
|
Cipher* pCipher = CipherGet (cipherId);
|
|
return pCipher? pCipher -> Name : L"";
|
|
}
|
|
#endif
|
|
|
|
int CipherGetBlockSize (int cipherId)
|
|
{
|
|
#ifdef TC_WINDOWS_BOOT
|
|
return CipherGet (cipherId) -> BlockSize;
|
|
#else
|
|
Cipher* pCipher = CipherGet (cipherId);
|
|
return pCipher? pCipher -> BlockSize : 0;
|
|
#endif
|
|
}
|
|
|
|
int CipherGetKeySize (int cipherId)
|
|
{
|
|
#ifdef TC_WINDOWS_BOOT
|
|
return CipherGet (cipherId) -> KeySize;
|
|
#else
|
|
Cipher* pCipher = CipherGet (cipherId);
|
|
return pCipher? pCipher -> KeySize : 0;
|
|
#endif
|
|
}
|
|
|
|
int CipherGetKeyScheduleSize (int cipherId)
|
|
{
|
|
#ifdef TC_WINDOWS_BOOT
|
|
return CipherGet (cipherId) -> KeyScheduleSize;
|
|
#else
|
|
Cipher* pCipher = CipherGet (cipherId);
|
|
return pCipher? pCipher -> KeyScheduleSize : 0;
|
|
#endif
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
BOOL CipherSupportsIntraDataUnitParallelization (int cipher)
|
|
{
|
|
return cipher == AES && IsAesHwCpuSupported();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// Encryption algorithms support
|
|
|
|
int EAGetFirst ()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Returns number of EAs
|
|
int EAGetCount (void)
|
|
{
|
|
int ea, count = 0;
|
|
|
|
for (ea = EAGetFirst (); ea != 0; ea = EAGetNext (ea))
|
|
{
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int EAGetNext (int previousEA)
|
|
{
|
|
int id = previousEA + 1;
|
|
if (EncryptionAlgorithms[id].Ciphers[0] != 0) return id;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Return values: 0 = success, ERR_CIPHER_INIT_FAILURE (fatal), ERR_CIPHER_INIT_WEAK_KEY (non-fatal)
|
|
int EAInit (int ea, unsigned char *key, unsigned __int8 *ks)
|
|
{
|
|
int c, retVal = ERR_SUCCESS;
|
|
|
|
if (ea == 0)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
|
|
for (c = EAGetFirstCipher (ea); c != 0; c = EAGetNextCipher (ea, c))
|
|
{
|
|
switch (CipherInit (c, key, ks))
|
|
{
|
|
case ERR_CIPHER_INIT_FAILURE:
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
|
|
case ERR_CIPHER_INIT_WEAK_KEY:
|
|
retVal = ERR_CIPHER_INIT_WEAK_KEY; // Non-fatal error
|
|
break;
|
|
}
|
|
|
|
key += CipherGetKeySize (c);
|
|
ks += CipherGetKeyScheduleSize (c);
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
BOOL EAInitMode (PCRYPTO_INFO ci)
|
|
{
|
|
switch (ci->mode)
|
|
{
|
|
case XTS:
|
|
// Secondary key schedule
|
|
if (EAInit (ci->ea, ci->k2, ci->ks2) != ERR_SUCCESS)
|
|
return FALSE;
|
|
|
|
/* 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
|
|
that the size of each of the volumes is 1024 terabytes). */
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void EAGetDisplayName(wchar_t *buf, int ea, int i)
|
|
{
|
|
wcscpy (buf, CipherGetName (i));
|
|
if (i = EAGetPreviousCipher(ea, i))
|
|
{
|
|
wcscat (buf, L"(");
|
|
EAGetDisplayName (&buf[wcslen(buf)], ea, i);
|
|
wcscat (buf, L")");
|
|
}
|
|
}
|
|
|
|
// Returns name of EA, cascaded cipher names are separated by hyphens
|
|
wchar_t *EAGetName (wchar_t *buf, int ea, int guiDisplay)
|
|
{
|
|
if (guiDisplay)
|
|
{
|
|
EAGetDisplayName (buf, ea, EAGetLastCipher(ea));
|
|
}
|
|
else
|
|
{
|
|
int i = EAGetLastCipher(ea);
|
|
wcscpy (buf, (i != 0) ? CipherGetName (i) : L"?");
|
|
|
|
while (i = EAGetPreviousCipher(ea, i))
|
|
{
|
|
wcscat (buf, L"-");
|
|
wcscat (buf, CipherGetName (i));
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
|
|
int EAGetByName (wchar_t *name)
|
|
{
|
|
int ea = EAGetFirst ();
|
|
wchar_t n[128];
|
|
|
|
do
|
|
{
|
|
EAGetName (n, ea, 1);
|
|
if (_wcsicmp (n, name) == 0)
|
|
return ea;
|
|
}
|
|
while (ea = EAGetNext (ea));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif // TC_WINDOWS_BOOT
|
|
|
|
// Returns sum of key sizes of all ciphers of the EA (in bytes)
|
|
int EAGetKeySize (int ea)
|
|
{
|
|
int i = EAGetFirstCipher (ea);
|
|
int size = CipherGetKeySize (i);
|
|
|
|
while (i = EAGetNextCipher (ea, i))
|
|
{
|
|
size += CipherGetKeySize (i);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
// Returns the first mode of operation of EA
|
|
int EAGetFirstMode (int ea)
|
|
{
|
|
return (EncryptionAlgorithms[ea].Modes[0]);
|
|
}
|
|
|
|
|
|
int EAGetNextMode (int ea, int previousModeId)
|
|
{
|
|
int c, i = 0;
|
|
while (c = EncryptionAlgorithms[ea].Modes[i++])
|
|
{
|
|
if (c == previousModeId)
|
|
return EncryptionAlgorithms[ea].Modes[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
// Returns the name of the mode of operation of the whole EA
|
|
wchar_t *EAGetModeName (int ea, int mode, BOOL capitalLetters)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case XTS:
|
|
|
|
return L"XTS";
|
|
|
|
}
|
|
return L"[unknown]";
|
|
}
|
|
|
|
#endif // TC_WINDOWS_BOOT
|
|
|
|
|
|
// Returns sum of key schedule sizes of all ciphers of the EA
|
|
int EAGetKeyScheduleSize (int ea)
|
|
{
|
|
int i = EAGetFirstCipher(ea);
|
|
int size = CipherGetKeyScheduleSize (i);
|
|
|
|
while (i = EAGetNextCipher(ea, i))
|
|
{
|
|
size += CipherGetKeyScheduleSize (i);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
// Returns the largest key size needed by an EA for the specified mode of operation
|
|
int EAGetLargestKeyForMode (int mode)
|
|
{
|
|
int ea, key = 0;
|
|
|
|
for (ea = EAGetFirst (); ea != 0; ea = EAGetNext (ea))
|
|
{
|
|
if (!EAIsModeSupported (ea, mode))
|
|
continue;
|
|
|
|
if (EAGetKeySize (ea) >= key)
|
|
key = EAGetKeySize (ea);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
|
|
// Returns the largest key needed by any EA for any mode
|
|
int EAGetLargestKey ()
|
|
{
|
|
int ea, key = 0;
|
|
|
|
for (ea = EAGetFirst (); ea != 0; ea = EAGetNext (ea))
|
|
{
|
|
if (EAGetKeySize (ea) >= key)
|
|
key = EAGetKeySize (ea);
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
// Returns number of ciphers in EA
|
|
int EAGetCipherCount (int ea)
|
|
{
|
|
int i = 0;
|
|
while (EncryptionAlgorithms[ea].Ciphers[i++]);
|
|
|
|
return i - 1;
|
|
}
|
|
|
|
|
|
int EAGetFirstCipher (int ea)
|
|
{
|
|
return EncryptionAlgorithms[ea].Ciphers[0];
|
|
}
|
|
|
|
|
|
int EAGetLastCipher (int ea)
|
|
{
|
|
int c, i = 0;
|
|
while (c = EncryptionAlgorithms[ea].Ciphers[i++]);
|
|
|
|
return EncryptionAlgorithms[ea].Ciphers[i - 2];
|
|
}
|
|
|
|
|
|
int EAGetNextCipher (int ea, int previousCipherId)
|
|
{
|
|
int c, i = 0;
|
|
while (c = EncryptionAlgorithms[ea].Ciphers[i++])
|
|
{
|
|
if (c == previousCipherId)
|
|
return EncryptionAlgorithms[ea].Ciphers[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EAGetPreviousCipher (int ea, int previousCipherId)
|
|
{
|
|
int c, i = 0;
|
|
|
|
if (EncryptionAlgorithms[ea].Ciphers[i++] == previousCipherId)
|
|
return 0;
|
|
|
|
while (c = EncryptionAlgorithms[ea].Ciphers[i++])
|
|
{
|
|
if (c == previousCipherId)
|
|
return EncryptionAlgorithms[ea].Ciphers[i - 2];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EAIsFormatEnabled (int ea)
|
|
{
|
|
return EncryptionAlgorithms[ea].FormatEnabled;
|
|
}
|
|
|
|
|
|
// Returns TRUE if the mode of operation is supported for the encryption algorithm
|
|
BOOL EAIsModeSupported (int ea, int testedMode)
|
|
{
|
|
int mode;
|
|
|
|
for (mode = EAGetFirstMode (ea); mode != 0; mode = EAGetNextMode (ea, mode))
|
|
{
|
|
if (mode == testedMode)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
Hash *HashGet (int id)
|
|
{
|
|
int i;
|
|
for (i = 0; Hashes[i].Id != 0; i++)
|
|
if (Hashes[i].Id == id)
|
|
return &Hashes[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int HashGetIdByName (wchar_t *name)
|
|
{
|
|
int i;
|
|
for (i = 0; Hashes[i].Id != 0; i++)
|
|
if (wcscmp (Hashes[i].Name, name) == 0)
|
|
return Hashes[i].Id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const wchar_t *HashGetName (int hashId)
|
|
{
|
|
Hash* pHash = HashGet(hashId);
|
|
return pHash? pHash -> Name : L"";
|
|
}
|
|
|
|
void HashGetName2 (wchar_t *buf, int hashId)
|
|
{
|
|
Hash* pHash = HashGet(hashId);
|
|
if (pHash)
|
|
wcscpy(buf, pHash -> Name);
|
|
else
|
|
buf[0] = L'\0';
|
|
}
|
|
|
|
BOOL HashIsDeprecated (int hashId)
|
|
{
|
|
Hash* pHash = HashGet(hashId);
|
|
return pHash? pHash -> Deprecated : FALSE;
|
|
|
|
}
|
|
|
|
BOOL HashForSystemEncryption (int hashId)
|
|
{
|
|
Hash* pHash = HashGet(hashId);
|
|
return pHash? pHash -> SystemEncryption : FALSE;
|
|
|
|
}
|
|
|
|
// Returns the maximum number of bytes necessary to be generated by the PBKDF2 (PKCS #5)
|
|
int GetMaxPkcs5OutSize (void)
|
|
{
|
|
int size = 32;
|
|
|
|
size = max (size, EAGetLargestKeyForMode (XTS) * 2); // Sizes of primary + secondary keys
|
|
|
|
return size;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#endif // TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE
|
|
|
|
|
|
#ifdef TC_WINDOWS_BOOT
|
|
|
|
static byte CryptoInfoBufferInUse = 0;
|
|
CRYPTO_INFO CryptoInfoBuffer;
|
|
|
|
#endif
|
|
|
|
PCRYPTO_INFO crypto_open ()
|
|
{
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
/* Do the crt allocation */
|
|
PCRYPTO_INFO cryptoInfo = (PCRYPTO_INFO) TCalloc (sizeof (CRYPTO_INFO));
|
|
if (cryptoInfo == NULL)
|
|
return NULL;
|
|
|
|
memset (cryptoInfo, 0, sizeof (CRYPTO_INFO));
|
|
|
|
#ifndef DEVICE_DRIVER
|
|
VirtualLock (cryptoInfo, sizeof (CRYPTO_INFO));
|
|
#endif
|
|
|
|
cryptoInfo->ea = -1;
|
|
return cryptoInfo;
|
|
|
|
#else // TC_WINDOWS_BOOT
|
|
|
|
#if 0
|
|
if (CryptoInfoBufferInUse)
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
#endif
|
|
CryptoInfoBufferInUse = 1;
|
|
return &CryptoInfoBuffer;
|
|
|
|
#endif // TC_WINDOWS_BOOT
|
|
}
|
|
|
|
#ifndef TC_WINDOWS_BOOT
|
|
void crypto_loadkey (PKEY_INFO keyInfo, char *lpszUserKey, int nUserKeyLen)
|
|
{
|
|
keyInfo->keyLength = nUserKeyLen;
|
|
burn (keyInfo->userKey, sizeof (keyInfo->userKey));
|
|
memcpy (keyInfo->userKey, lpszUserKey, nUserKeyLen);
|
|
}
|
|
#endif
|
|
|
|
void crypto_close (PCRYPTO_INFO cryptoInfo)
|
|
{
|
|
#ifndef TC_WINDOWS_BOOT
|
|
|
|
if (cryptoInfo != NULL)
|
|
{
|
|
burn (cryptoInfo, sizeof (CRYPTO_INFO));
|
|
#ifndef DEVICE_DRIVER
|
|
VirtualUnlock (cryptoInfo, sizeof (CRYPTO_INFO));
|
|
#endif
|
|
TCfree (cryptoInfo);
|
|
}
|
|
|
|
#else // TC_WINDOWS_BOOT
|
|
|
|
burn (&CryptoInfoBuffer, sizeof (CryptoInfoBuffer));
|
|
CryptoInfoBufferInUse = FALSE;
|
|
|
|
#endif // TC_WINDOWS_BOOT
|
|
}
|
|
|
|
|
|
#ifndef TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE
|
|
|
|
|
|
|
|
// 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
|
|
// by the largest block size used within the cascade)
|
|
void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo)
|
|
{
|
|
switch (cryptoInfo->mode)
|
|
{
|
|
case XTS:
|
|
{
|
|
unsigned __int8 *ks = cryptoInfo->ks;
|
|
unsigned __int8 *ks2 = cryptoInfo->ks2;
|
|
UINT64_STRUCT dataUnitNo;
|
|
int cipher;
|
|
|
|
// When encrypting/decrypting a buffer (typically a volume header) the sequential number
|
|
// of the first XTS data unit in the buffer is always 0 and the start of the buffer is
|
|
// always assumed to be aligned with the start of a data unit.
|
|
dataUnitNo.LowPart = 0;
|
|
dataUnitNo.HighPart = 0;
|
|
|
|
for (cipher = EAGetFirstCipher (cryptoInfo->ea);
|
|
cipher != 0;
|
|
cipher = EAGetNextCipher (cryptoInfo->ea, cipher))
|
|
{
|
|
EncryptBufferXTS (buf, len, &dataUnitNo, 0, ks, ks2, cipher);
|
|
|
|
ks += CipherGetKeyScheduleSize (cipher);
|
|
ks2 += CipherGetKeyScheduleSize (cipher);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
|
|
// buf: data to be encrypted
|
|
// 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
|
|
{
|
|
EncryptionThreadPoolDoWork (EncryptDataUnitsWork, buf, structUnitNo, nbrUnits, ci);
|
|
}
|
|
|
|
void EncryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci)
|
|
#endif // !TC_WINDOWS_BOOT
|
|
{
|
|
int ea = ci->ea;
|
|
unsigned __int8 *ks = ci->ks;
|
|
unsigned __int8 *ks2 = ci->ks2;
|
|
int cipher;
|
|
|
|
switch (ci->mode)
|
|
{
|
|
case XTS:
|
|
for (cipher = EAGetFirstCipher (ea); cipher != 0; cipher = EAGetNextCipher (ea, cipher))
|
|
{
|
|
EncryptBufferXTS (buf,
|
|
nbrUnits * ENCRYPTION_DATA_UNIT_SIZE,
|
|
structUnitNo,
|
|
0,
|
|
ks,
|
|
ks2,
|
|
cipher);
|
|
|
|
ks += CipherGetKeyScheduleSize (cipher);
|
|
ks2 += CipherGetKeyScheduleSize (cipher);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// by the largest block size used within the cascade)
|
|
void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo)
|
|
{
|
|
switch (cryptoInfo->mode)
|
|
{
|
|
case XTS:
|
|
{
|
|
unsigned __int8 *ks = cryptoInfo->ks + EAGetKeyScheduleSize (cryptoInfo->ea);
|
|
unsigned __int8 *ks2 = cryptoInfo->ks2 + EAGetKeyScheduleSize (cryptoInfo->ea);
|
|
UINT64_STRUCT dataUnitNo;
|
|
int cipher;
|
|
|
|
// When encrypting/decrypting a buffer (typically a volume header) the sequential number
|
|
// of the first XTS data unit in the buffer is always 0 and the start of the buffer is
|
|
// always assumed to be aligned with the start of the data unit 0.
|
|
dataUnitNo.LowPart = 0;
|
|
dataUnitNo.HighPart = 0;
|
|
|
|
for (cipher = EAGetLastCipher (cryptoInfo->ea);
|
|
cipher != 0;
|
|
cipher = EAGetPreviousCipher (cryptoInfo->ea, cipher))
|
|
{
|
|
ks -= CipherGetKeyScheduleSize (cipher);
|
|
ks2 -= CipherGetKeyScheduleSize (cipher);
|
|
|
|
DecryptBufferXTS (buf, len, &dataUnitNo, 0, ks, ks2, cipher);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
// buf: data to be decrypted
|
|
// 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
|
|
{
|
|
EncryptionThreadPoolDoWork (DecryptDataUnitsWork, buf, structUnitNo, nbrUnits, ci);
|
|
}
|
|
|
|
void DecryptDataUnitsCurrentThread (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci)
|
|
#endif // !TC_WINDOWS_BOOT
|
|
{
|
|
int ea = ci->ea;
|
|
unsigned __int8 *ks = ci->ks;
|
|
unsigned __int8 *ks2 = ci->ks2;
|
|
int cipher;
|
|
|
|
|
|
switch (ci->mode)
|
|
{
|
|
case XTS:
|
|
ks += EAGetKeyScheduleSize (ea);
|
|
ks2 += EAGetKeyScheduleSize (ea);
|
|
|
|
for (cipher = EAGetLastCipher (ea); cipher != 0; cipher = EAGetPreviousCipher (ea, cipher))
|
|
{
|
|
ks -= CipherGetKeyScheduleSize (cipher);
|
|
ks2 -= CipherGetKeyScheduleSize (cipher);
|
|
|
|
DecryptBufferXTS (buf,
|
|
nbrUnits * ENCRYPTION_DATA_UNIT_SIZE,
|
|
structUnitNo,
|
|
0,
|
|
ks,
|
|
ks2,
|
|
cipher);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
|
|
#else // TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE
|
|
|
|
|
|
#if !defined (TC_WINDOWS_BOOT_AES) && !defined (TC_WINDOWS_BOOT_SERPENT) && !defined (TC_WINDOWS_BOOT_TWOFISH)
|
|
#error No cipher defined
|
|
#endif
|
|
|
|
void EncipherBlock(int cipher, void *data, void *ks)
|
|
{
|
|
#ifdef TC_WINDOWS_BOOT_AES
|
|
if (IsAesHwCpuSupported())
|
|
aes_hw_cpu_encrypt ((byte *) ks, data);
|
|
else
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
void DecipherBlock(int cipher, void *data, void *ks)
|
|
{
|
|
#ifdef TC_WINDOWS_BOOT_AES
|
|
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)));
|
|
#elif defined (TC_WINDOWS_BOOT_SERPENT)
|
|
serpent_decrypt (data, data, ks);
|
|
#elif defined (TC_WINDOWS_BOOT_TWOFISH)
|
|
twofish_decrypt (ks, data, data);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef TC_WINDOWS_BOOT_AES
|
|
|
|
int EAInit (unsigned char *key, unsigned __int8 *ks)
|
|
{
|
|
aes_init();
|
|
|
|
if (aes_encrypt_key256 (key, (aes_encrypt_ctx *) ks) != EXIT_SUCCESS)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
if (aes_decrypt_key256 (key, (aes_decrypt_ctx *) (ks + sizeof (aes_encrypt_ctx))) != EXIT_SUCCESS)
|
|
return ERR_CIPHER_INIT_FAILURE;
|
|
|
|
return ERR_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void EncryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo)
|
|
{
|
|
UINT64_STRUCT dataUnitNo;
|
|
dataUnitNo.LowPart = 0; dataUnitNo.HighPart = 0;
|
|
EncryptBufferXTS (buf, len, &dataUnitNo, 0, cryptoInfo->ks, cryptoInfo->ks2, 1);
|
|
}
|
|
|
|
void EncryptDataUnits (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci)
|
|
{
|
|
EncryptBufferXTS (buf, nbrUnits * ENCRYPTION_DATA_UNIT_SIZE, structUnitNo, 0, ci->ks, ci->ks2, 1);
|
|
}
|
|
|
|
void DecryptBuffer (unsigned __int8 *buf, TC_LARGEST_COMPILER_UINT len, PCRYPTO_INFO cryptoInfo)
|
|
{
|
|
UINT64_STRUCT dataUnitNo;
|
|
dataUnitNo.LowPart = 0; dataUnitNo.HighPart = 0;
|
|
DecryptBufferXTS (buf, len, &dataUnitNo, 0, cryptoInfo->ks, cryptoInfo->ks2, 1);
|
|
}
|
|
|
|
void DecryptDataUnits (unsigned __int8 *buf, const UINT64_STRUCT *structUnitNo, TC_LARGEST_COMPILER_UINT nbrUnits, PCRYPTO_INFO ci)
|
|
{
|
|
DecryptBufferXTS (buf, nbrUnits * ENCRYPTION_DATA_UNIT_SIZE, structUnitNo, 0, ci->ks, ci->ks2, 1);
|
|
}
|
|
|
|
#endif // TC_WINDOWS_BOOT_SINGLE_CIPHER_MODE
|
|
|
|
|
|
#if !defined (TC_WINDOWS_BOOT) || defined (TC_WINDOWS_BOOT_AES)
|
|
|
|
static BOOL HwEncryptionDisabled = FALSE;
|
|
|
|
BOOL IsAesHwCpuSupported ()
|
|
{
|
|
static BOOL state = FALSE;
|
|
static BOOL stateValid = FALSE;
|
|
|
|
if (!stateValid)
|
|
{
|
|
state = is_aes_hw_cpu_supported() ? TRUE : FALSE;
|
|
stateValid = TRUE;
|
|
}
|
|
|
|
return state && !HwEncryptionDisabled;
|
|
}
|
|
|
|
void EnableHwEncryption (BOOL enable)
|
|
{
|
|
#if defined (TC_WINDOWS_BOOT)
|
|
if (enable)
|
|
aes_hw_cpu_enable_sse();
|
|
#endif
|
|
|
|
HwEncryptionDisabled = !enable;
|
|
}
|
|
|
|
BOOL IsHwEncryptionEnabled ()
|
|
{
|
|
return !HwEncryptionDisabled;
|
|
}
|
|
|
|
#endif // !TC_WINDOWS_BOOT
|