summaryrefslogtreecommitdiff
path: root/Volume/EncryptionModeCBC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Volume/EncryptionModeCBC.cpp')
-rw-r--r--Volume/EncryptionModeCBC.cpp335
1 files changed, 335 insertions, 0 deletions
diff --git a/Volume/EncryptionModeCBC.cpp b/Volume/EncryptionModeCBC.cpp
new file mode 100644
index 0000000..f299b88
--- /dev/null
+++ b/Volume/EncryptionModeCBC.cpp
@@ -0,0 +1,335 @@
+/*
+ Copyright (c) 2008 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 "Platform/Memory.h"
+#include "Common/Crc.h"
+#include "Common/Endian.h"
+#include "EncryptionModeCBC.h"
+
+namespace TrueCrypt
+{
+ void EncryptionModeCBC::Decrypt (byte *data, uint64 length) const
+ {
+ if_debug (ValidateState ());
+ if_debug (ValidateParameters (data, length));
+
+ if (IsOuterCBC (Ciphers))
+ {
+ DecryptBuffer (data, length, Ciphers, (uint32 *) IV.Ptr(), (uint32 *) (IV.Ptr() + WhiteningIVOffset));
+ }
+ else
+ {
+ for (CipherList::const_reverse_iterator iCipherList = Ciphers.rbegin();
+ iCipherList != Ciphers.rend();
+ ++iCipherList)
+ {
+ CipherList cl;
+ cl.push_back (*iCipherList);
+
+ DecryptBuffer (data, length, cl, (uint32 *) IV.Ptr(), (uint32 *) (IV.Ptr() + WhiteningIVOffset));
+ }
+ }
+ }
+
+ void EncryptionModeCBC::DecryptBuffer (byte *data, uint64 length, const CipherList &ciphers, const uint32 *iv, const uint32 *whitening) const
+ {
+ size_t blockSize = ciphers.front()->GetBlockSize();
+ if (blockSize != 8 && blockSize != 16)
+ throw ParameterIncorrect (SRC_POS);
+
+ uint32 *data32 = (uint32 *) data;
+ uint32 bufIV[4];
+ uint32 ct[4];
+ uint64 i;
+
+ bufIV[0] = iv[0];
+ bufIV[1] = iv[1];
+ if (blockSize == 16)
+ {
+ bufIV[2] = iv[2];
+ bufIV[3] = iv[3];
+ }
+
+ for (i = 0; i < length / blockSize; i++)
+ {
+ // Dewhitening
+ data32[0] ^= whitening[0];
+ data32[1] ^= whitening[1];
+ if (blockSize == 16)
+ {
+ data32[2] ^= whitening[0];
+ data32[3] ^= whitening[1];
+ }
+
+ // CBC
+ ct[0] = data32[0];
+ ct[1] = data32[1];
+ if (blockSize == 16)
+ {
+ ct[2] = data32[2];
+ ct[3] = data32[3];
+ }
+
+ for (CipherList::const_reverse_iterator iCipherList = ciphers.rbegin();
+ iCipherList != ciphers.rend();
+ ++iCipherList)
+ {
+ const Cipher &c = **iCipherList;
+
+ if (c.GetBlockSize () != blockSize)
+ throw ParameterIncorrect (SRC_POS);
+
+ c.DecryptBlock ((byte *) data32);
+ }
+
+ // CBC
+ data32[0] ^= bufIV[0];
+ data32[1] ^= bufIV[1];
+ bufIV[0] = ct[0];
+ bufIV[1] = ct[1];
+ if (blockSize == 16)
+ {
+ data32[2] ^= bufIV[2];
+ data32[3] ^= bufIV[3];
+ bufIV[2] = ct[2];
+ bufIV[3] = ct[3];
+ }
+
+ data32 += blockSize / sizeof(*data32);
+ }
+
+ Memory::Erase (bufIV, sizeof (bufIV));
+ Memory::Erase (ct, sizeof (ct));
+ }
+
+ void EncryptionModeCBC::DecryptSectorsCurrentThread (byte *data, uint64 sectorIndex, uint64 sectorCount, size_t sectorSize) const
+ {
+ if_debug (ValidateState ());
+ if_debug (ValidateParameters (data, sectorCount, sectorSize));
+
+ uint32 sectorIV[4];
+ uint32 sectorWhitening[2];
+
+ while (sectorCount--)
+ {
+ if (IsOuterCBC (Ciphers))
+ {
+ InitSectorIVAndWhitening (sectorIndex, Ciphers.front()->GetBlockSize(), (uint64 *) IV.Ptr(), sectorIV, sectorWhitening);
+ DecryptBuffer (data, sectorSize, Ciphers, sectorIV, sectorWhitening);
+ }
+ else
+ {
+ for (CipherList::const_reverse_iterator iCipherList = Ciphers.rbegin();
+ iCipherList != Ciphers.rend();
+ ++iCipherList)
+ {
+ const Cipher &c = **iCipherList;
+ CipherList cl;
+ cl.push_back (*iCipherList);
+
+ InitSectorIVAndWhitening (sectorIndex, c.GetBlockSize(), (uint64 *) IV.Ptr(), sectorIV, sectorWhitening);
+ DecryptBuffer (data, sectorSize, cl, sectorIV, sectorWhitening);
+ }
+ }
+
+ data += sectorSize;
+ sectorIndex++;
+ }
+
+ Memory::Erase (sectorIV, sizeof (sectorIV));
+ Memory::Erase (sectorWhitening, sizeof (sectorWhitening));
+ }
+
+ void EncryptionModeCBC::Encrypt (byte *data, uint64 length) const
+ {
+ if_debug (ValidateState ());
+ if_debug (ValidateParameters (data, length));
+
+ if (IsOuterCBC (Ciphers))
+ {
+ EncryptBuffer (data, length, Ciphers, (uint32 *) IV.Ptr(), (uint32 *) (IV.Ptr() + WhiteningIVOffset));
+ }
+ else
+ {
+ for (CipherList::const_iterator iCipherList = Ciphers.begin();
+ iCipherList != Ciphers.end();
+ ++iCipherList)
+ {
+ CipherList cl;
+ cl.push_back (*iCipherList);
+
+ EncryptBuffer (data, length, cl, (uint32 *) IV.Ptr(), (uint32 *) (IV.Ptr() + WhiteningIVOffset));
+ }
+ }
+ }
+
+ void EncryptionModeCBC::EncryptBuffer (byte *data, uint64 length, const CipherList &ciphers, const uint32 *iv, const uint32 *whitening) const
+ {
+ size_t blockSize = ciphers.front()->GetBlockSize();
+ if (blockSize != 8 && blockSize != 16)
+ throw ParameterIncorrect (SRC_POS);
+
+ uint32 *data32 = (uint32 *) data;
+ uint32 bufIV[4];
+ uint64 i;
+
+ bufIV[0] = iv[0];
+ bufIV[1] = iv[1];
+ if (blockSize == 16)
+ {
+ bufIV[2] = iv[2];
+ bufIV[3] = iv[3];
+ }
+
+ for (i = 0; i < length / blockSize; i++)
+ {
+ data32[0] ^= bufIV[0];
+ data32[1] ^= bufIV[1];
+ if (blockSize == 16)
+ {
+ data32[2] ^= bufIV[2];
+ data32[3] ^= bufIV[3];
+ }
+
+ for (CipherList::const_iterator iCipherList = ciphers.begin();
+ iCipherList != ciphers.end();
+ ++iCipherList)
+ {
+ const Cipher &c = **iCipherList;
+
+ if (c.GetBlockSize () != blockSize)
+ throw ParameterIncorrect (SRC_POS);
+
+ c.EncryptBlock ((byte *) data32);
+ }
+
+ bufIV[0] = data32[0];
+ bufIV[1] = data32[1];
+ if (blockSize == 16)
+ {
+ bufIV[2] = data32[2];
+ bufIV[3] = data32[3];
+ }
+
+ data32[0] ^= whitening[0];
+ data32[1] ^= whitening[1];
+ if (blockSize == 16)
+ {
+ data32[2] ^= whitening[0];
+ data32[3] ^= whitening[1];
+ }
+
+ data32 += blockSize / sizeof(*data32);
+ }
+
+ Memory::Erase (bufIV, sizeof (bufIV));
+ }
+
+ void EncryptionModeCBC::EncryptSectorsCurrentThread (byte *data, uint64 sectorIndex, uint64 sectorCount, size_t sectorSize) const
+ {
+ if_debug (ValidateState ());
+ if_debug (ValidateParameters (data, sectorCount, sectorSize));
+
+ uint32 sectorIV[4];
+ uint32 sectorWhitening[2];
+
+ while (sectorCount--)
+ {
+ if (IsOuterCBC (Ciphers))
+ {
+ InitSectorIVAndWhitening (sectorIndex, Ciphers.front()->GetBlockSize(), (uint64 *) IV.Ptr(), sectorIV, sectorWhitening);
+ EncryptBuffer (data, sectorSize, Ciphers, sectorIV, sectorWhitening);
+ }
+ else
+ {
+ for (CipherList::const_iterator iCipherList = Ciphers.begin();
+ iCipherList != Ciphers.end();
+ ++iCipherList)
+ {
+ const Cipher &c = **iCipherList;
+ CipherList cl;
+ cl.push_back (*iCipherList);
+
+ InitSectorIVAndWhitening (sectorIndex, c.GetBlockSize(), (uint64 *) IV.Ptr(), sectorIV, sectorWhitening);
+ EncryptBuffer (data, sectorSize, cl, sectorIV, sectorWhitening);
+ }
+ }
+
+ data += sectorSize;
+ sectorIndex++;
+ }
+
+ Memory::Erase (sectorIV, sizeof (sectorIV));
+ Memory::Erase (sectorWhitening, sizeof (sectorWhitening));
+ }
+
+ void EncryptionModeCBC::InitSectorIVAndWhitening (uint64 sectorIndex, size_t blockSize, const uint64 *ivSeed, uint32 *iv, uint32 *whitening) const
+ {
+ if (blockSize != 8 && blockSize != 16)
+ throw ParameterIncorrect (SRC_POS);
+
+ uint64 iv64[4];
+ uint32 *iv32 = (uint32 *) iv64;
+
+ iv64[0] = ivSeed[0] ^ Endian::Little (sectorIndex);
+ iv64[1] = ivSeed[1] ^ Endian::Little (sectorIndex);
+ iv64[2] = ivSeed[2] ^ Endian::Little (sectorIndex);
+ if (blockSize == 16)
+ {
+ iv64[3] = ivSeed[3] ^ Endian::Little (sectorIndex);
+ }
+
+ iv[0] = iv32[0];
+ iv[1] = iv32[1];
+
+ if (blockSize == 8)
+ {
+ whitening[0] = Endian::Little ( crc32int ( &iv32[2] ) ^ crc32int ( &iv32[5] ) );
+ whitening[1] = Endian::Little ( crc32int ( &iv32[3] ) ^ crc32int ( &iv32[4] ) );
+ }
+ else
+ {
+ iv[2] = iv32[2];
+ iv[3] = iv32[3];
+
+ whitening[0] = Endian::Little ( crc32int ( &iv32[4] ) ^ crc32int ( &iv32[7] ) );
+ whitening[1] = Endian::Little ( crc32int ( &iv32[5] ) ^ crc32int ( &iv32[6] ) );
+ }
+ }
+
+ bool EncryptionModeCBC::IsOuterCBC (const CipherList &ciphers) const
+ {
+ if (ciphers.size() < 2)
+ return false;
+
+ size_t blockSize = ciphers.front()->GetBlockSize();
+
+ for (CipherList::const_iterator iCipherList = ciphers.begin();
+ iCipherList != ciphers.end();
+ ++iCipherList)
+ {
+ const Cipher &c = **iCipherList;
+ if (c.GetBlockSize() != blockSize)
+ return false;
+ }
+
+ return true;
+ }
+
+ void EncryptionModeCBC::SetKey (const ConstBufferPtr &key)
+ {
+ if (key.Size() != GetKeySize ())
+ throw ParameterIncorrect (SRC_POS);
+
+ if (!KeySet)
+ IV.Allocate (GetKeySize ());
+
+ IV.CopyFrom (key);
+ KeySet = true;
+ }
+}