summaryrefslogtreecommitdiff
path: root/Volume/VolumeHeader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Volume/VolumeHeader.cpp')
-rw-r--r--Volume/VolumeHeader.cpp340
1 files changed, 340 insertions, 0 deletions
diff --git a/Volume/VolumeHeader.cpp b/Volume/VolumeHeader.cpp
new file mode 100644
index 0000000..f31111c
--- /dev/null
+++ b/Volume/VolumeHeader.cpp
@@ -0,0 +1,340 @@
+/*
+ Copyright (c) 2008-2010 TrueCrypt Developers Association. All rights reserved.
+
+ Governed by the TrueCrypt License 3.0 the full text of which is contained in
+ the file License.txt included in TrueCrypt binary and source code distribution
+ packages.
+*/
+
+#include "Crc32.h"
+#include "EncryptionModeXTS.h"
+#include "Pkcs5Kdf.h"
+#include "Pkcs5Kdf.h"
+#include "VolumeHeader.h"
+#include "VolumeException.h"
+#include "Common/Crypto.h"
+
+namespace TrueCrypt
+{
+ VolumeHeader::VolumeHeader (uint32 size)
+ {
+ Init();
+ HeaderSize = size;
+ EncryptedHeaderDataSize = size - EncryptedHeaderDataOffset;
+ }
+
+ VolumeHeader::~VolumeHeader ()
+ {
+ Init();
+ }
+
+ void VolumeHeader::Init ()
+ {
+ VolumeKeyAreaCrc32 = 0;
+ VolumeCreationTime = 0;
+ HeaderCreationTime = 0;
+ mVolumeType = VolumeType::Unknown;
+ HiddenVolumeDataSize = 0;
+ VolumeDataSize = 0;
+ EncryptedAreaStart = 0;
+ EncryptedAreaLength = 0;
+ Flags = 0;
+ SectorSize = 0;
+ }
+
+ void VolumeHeader::Create (const BufferPtr &headerBuffer, VolumeHeaderCreationOptions &options)
+ {
+ if (options.DataKey.Size() != options.EA->GetKeySize() * 2 || options.Salt.Size() != GetSaltSize())
+ throw ParameterIncorrect (SRC_POS);
+
+ headerBuffer.Zero();
+
+ HeaderVersion = CurrentHeaderVersion;
+ RequiredMinProgramVersion = CurrentRequiredMinProgramVersion;
+
+ DataAreaKey.Zero();
+ DataAreaKey.CopyFrom (options.DataKey);
+
+ VolumeCreationTime = 0;
+ HiddenVolumeDataSize = (options.Type == VolumeType::Hidden ? options.VolumeDataSize : 0);
+ VolumeDataSize = options.VolumeDataSize;
+
+ EncryptedAreaStart = options.VolumeDataStart;
+ EncryptedAreaLength = options.VolumeDataSize;
+
+ SectorSize = options.SectorSize;
+
+ if (SectorSize < TC_MIN_VOLUME_SECTOR_SIZE
+ || SectorSize > TC_MAX_VOLUME_SECTOR_SIZE
+ || SectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
+ {
+ throw ParameterIncorrect (SRC_POS);
+ }
+
+ EA = options.EA;
+ shared_ptr <EncryptionMode> mode (new EncryptionModeXTS ());
+ EA->SetMode (mode);
+
+ EncryptNew (headerBuffer, options.Salt, options.HeaderKey, options.Kdf);
+ }
+
+ bool VolumeHeader::Decrypt (const ConstBufferPtr &encryptedData, const VolumePassword &password, const Pkcs5KdfList &keyDerivationFunctions, const EncryptionAlgorithmList &encryptionAlgorithms, const EncryptionModeList &encryptionModes)
+ {
+ if (password.Size() < 1)
+ throw PasswordEmpty (SRC_POS);
+
+ ConstBufferPtr salt (encryptedData.GetRange (SaltOffset, SaltSize));
+ SecureBuffer header (EncryptedHeaderDataSize);
+ SecureBuffer headerKey (GetLargestSerializedKeySize());
+
+ foreach (shared_ptr <Pkcs5Kdf> pkcs5, keyDerivationFunctions)
+ {
+ pkcs5->DeriveKey (headerKey, password, salt);
+
+ foreach (shared_ptr <EncryptionMode> mode, encryptionModes)
+ {
+ if (typeid (*mode) != typeid (EncryptionModeXTS))
+ mode->SetKey (headerKey.GetRange (0, mode->GetKeySize()));
+
+ foreach (shared_ptr <EncryptionAlgorithm> ea, encryptionAlgorithms)
+ {
+ if (!ea->IsModeSupported (mode))
+ continue;
+
+ if (typeid (*mode) == typeid (EncryptionModeXTS))
+ {
+ ea->SetKey (headerKey.GetRange (0, ea->GetKeySize()));
+
+ mode = mode->GetNew();
+ mode->SetKey (headerKey.GetRange (ea->GetKeySize(), ea->GetKeySize()));
+ }
+ else
+ {
+ ea->SetKey (headerKey.GetRange (LegacyEncryptionModeKeyAreaSize, ea->GetKeySize()));
+ }
+
+ ea->SetMode (mode);
+
+ header.CopyFrom (encryptedData.GetRange (EncryptedHeaderDataOffset, EncryptedHeaderDataSize));
+ ea->Decrypt (header);
+
+ if (Deserialize (header, ea, mode))
+ {
+ EA = ea;
+ Pkcs5 = pkcs5;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ bool VolumeHeader::Deserialize (const ConstBufferPtr &header, shared_ptr <EncryptionAlgorithm> &ea, shared_ptr <EncryptionMode> &mode)
+ {
+ if (header.Size() != EncryptedHeaderDataSize)
+ throw ParameterIncorrect (SRC_POS);
+
+ if (header[0] != 'T' ||
+ header[1] != 'R' ||
+ header[2] != 'U' ||
+ header[3] != 'E')
+ return false;
+
+ size_t offset = 4;
+ HeaderVersion = DeserializeEntry <uint16> (header, offset);
+
+ if (HeaderVersion < MinAllowedHeaderVersion)
+ return false;
+
+ if (HeaderVersion > CurrentHeaderVersion)
+ throw HigherVersionRequired (SRC_POS);
+
+ if (HeaderVersion >= 4
+ && Crc32::ProcessBuffer (header.GetRange (0, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC))
+ != DeserializeEntryAt <uint32> (header, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC))
+ {
+ return false;
+ }
+
+ RequiredMinProgramVersion = DeserializeEntry <uint16> (header, offset);
+
+ if (RequiredMinProgramVersion > Version::Number())
+ throw HigherVersionRequired (SRC_POS);
+
+ VolumeKeyAreaCrc32 = DeserializeEntry <uint32> (header, offset);
+ VolumeCreationTime = DeserializeEntry <uint64> (header, offset);
+ HeaderCreationTime = DeserializeEntry <uint64> (header, offset);
+ HiddenVolumeDataSize = DeserializeEntry <uint64> (header, offset);
+ mVolumeType = (HiddenVolumeDataSize != 0 ? VolumeType::Hidden : VolumeType::Normal);
+ VolumeDataSize = DeserializeEntry <uint64> (header, offset);
+ EncryptedAreaStart = DeserializeEntry <uint64> (header, offset);
+ EncryptedAreaLength = DeserializeEntry <uint64> (header, offset);
+ Flags = DeserializeEntry <uint32> (header, offset);
+
+ SectorSize = DeserializeEntry <uint32> (header, offset);
+ if (HeaderVersion < 5)
+ SectorSize = TC_SECTOR_SIZE_LEGACY;
+
+ if (SectorSize < TC_MIN_VOLUME_SECTOR_SIZE
+ || SectorSize > TC_MAX_VOLUME_SECTOR_SIZE
+ || SectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
+ {
+ throw ParameterIncorrect (SRC_POS);
+ }
+
+#if !(defined (TC_WINDOWS) || defined (TC_LINUX))
+ if (SectorSize != TC_SECTOR_SIZE_LEGACY)
+ throw UnsupportedSectorSize (SRC_POS);
+#endif
+
+ offset = DataAreaKeyOffset;
+
+ if (VolumeKeyAreaCrc32 != Crc32::ProcessBuffer (header.GetRange (offset, DataKeyAreaMaxSize)))
+ return false;
+
+ DataAreaKey.CopyFrom (header.GetRange (offset, DataKeyAreaMaxSize));
+
+ ea = ea->GetNew();
+ mode = mode->GetNew();
+
+ if (typeid (*mode) == typeid (EncryptionModeXTS))
+ {
+ ea->SetKey (header.GetRange (offset, ea->GetKeySize()));
+ mode->SetKey (header.GetRange (offset + ea->GetKeySize(), ea->GetKeySize()));
+ }
+ else
+ {
+ mode->SetKey (header.GetRange (offset, mode->GetKeySize()));
+ ea->SetKey (header.GetRange (offset + LegacyEncryptionModeKeyAreaSize, ea->GetKeySize()));
+ }
+
+ ea->SetMode (mode);
+
+ return true;
+ }
+
+ template <typename T>
+ T VolumeHeader::DeserializeEntry (const ConstBufferPtr &header, size_t &offset) const
+ {
+ offset += sizeof (T);
+
+ if (offset > header.Size())
+ throw ParameterIncorrect (SRC_POS);
+
+ return Endian::Big (*reinterpret_cast<const T *> (header.Get() + offset - sizeof (T)));
+ }
+
+ template <typename T>
+ T VolumeHeader::DeserializeEntryAt (const ConstBufferPtr &header, const size_t &offset) const
+ {
+ if (offset > header.Size())
+ throw ParameterIncorrect (SRC_POS);
+
+ return Endian::Big (*reinterpret_cast<const T *> (header.Get() + offset));
+ }
+
+ void VolumeHeader::EncryptNew (const BufferPtr &newHeaderBuffer, const ConstBufferPtr &newSalt, const ConstBufferPtr &newHeaderKey, shared_ptr <Pkcs5Kdf> newPkcs5Kdf)
+ {
+ if (newHeaderBuffer.Size() != HeaderSize || newSalt.Size() != SaltSize)
+ throw ParameterIncorrect (SRC_POS);
+
+ shared_ptr <EncryptionMode> mode = EA->GetMode()->GetNew();
+ shared_ptr <EncryptionAlgorithm> ea = EA->GetNew();
+
+ if (typeid (*mode) == typeid (EncryptionModeXTS))
+ {
+ mode->SetKey (newHeaderKey.GetRange (EA->GetKeySize(), EA->GetKeySize()));
+ ea->SetKey (newHeaderKey.GetRange (0, ea->GetKeySize()));
+ }
+ else
+ {
+ mode->SetKey (newHeaderKey.GetRange (0, mode->GetKeySize()));
+ ea->SetKey (newHeaderKey.GetRange (LegacyEncryptionModeKeyAreaSize, ea->GetKeySize()));
+ }
+
+ ea->SetMode (mode);
+
+ newHeaderBuffer.CopyFrom (newSalt);
+
+ BufferPtr headerData = newHeaderBuffer.GetRange (EncryptedHeaderDataOffset, EncryptedHeaderDataSize);
+ Serialize (headerData);
+ ea->Encrypt (headerData);
+
+ if (newPkcs5Kdf)
+ Pkcs5 = newPkcs5Kdf;
+ }
+
+ size_t VolumeHeader::GetLargestSerializedKeySize ()
+ {
+ size_t largestKey = EncryptionAlgorithm::GetLargestKeySize (EncryptionAlgorithm::GetAvailableAlgorithms());
+
+ // XTS mode requires the same key size as the encryption algorithm.
+ // Legacy modes may require larger key than XTS.
+ if (LegacyEncryptionModeKeyAreaSize + largestKey > largestKey * 2)
+ return LegacyEncryptionModeKeyAreaSize + largestKey;
+
+ return largestKey * 2;
+ }
+
+ void VolumeHeader::Serialize (const BufferPtr &header) const
+ {
+ if (header.Size() != EncryptedHeaderDataSize)
+ throw ParameterIncorrect (SRC_POS);
+
+ header.Zero();
+
+ header[0] = 'T';
+ header[1] = 'R';
+ header[2] = 'U';
+ header[3] = 'E';
+ size_t offset = 4;
+
+ header.GetRange (DataAreaKeyOffset, DataAreaKey.Size()).CopyFrom (DataAreaKey);
+
+ uint16 headerVersion = CurrentHeaderVersion;
+ SerializeEntry (headerVersion, header, offset);
+ SerializeEntry (RequiredMinProgramVersion, header, offset);
+ SerializeEntry (Crc32::ProcessBuffer (header.GetRange (DataAreaKeyOffset, DataKeyAreaMaxSize)), header, offset);
+
+ uint64 reserved64 = 0;
+ SerializeEntry (reserved64, header, offset);
+ SerializeEntry (reserved64, header, offset);
+
+ SerializeEntry (HiddenVolumeDataSize, header, offset);
+ SerializeEntry (VolumeDataSize, header, offset);
+ SerializeEntry (EncryptedAreaStart, header, offset);
+ SerializeEntry (EncryptedAreaLength, header, offset);
+ SerializeEntry (Flags, header, offset);
+
+ if (SectorSize < TC_MIN_VOLUME_SECTOR_SIZE
+ || SectorSize > TC_MAX_VOLUME_SECTOR_SIZE
+ || SectorSize % ENCRYPTION_DATA_UNIT_SIZE != 0)
+ {
+ throw ParameterIncorrect (SRC_POS);
+ }
+
+ SerializeEntry (SectorSize, header, offset);
+
+ offset = TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC;
+ SerializeEntry (Crc32::ProcessBuffer (header.GetRange (0, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC)), header, offset);
+ }
+
+ template <typename T>
+ void VolumeHeader::SerializeEntry (const T &entry, const BufferPtr &header, size_t &offset) const
+ {
+ offset += sizeof (T);
+
+ if (offset > header.Size())
+ throw ParameterIncorrect (SRC_POS);
+
+ *reinterpret_cast<T *> (header.Get() + offset - sizeof (T)) = Endian::Big (entry);
+ }
+
+ void VolumeHeader::SetSize (uint32 headerSize)
+ {
+ HeaderSize = headerSize;
+ EncryptedHeaderDataSize = HeaderSize - EncryptedHeaderDataOffset;
+ }
+}