How VeraCrypt's Encryption Algorithm Works: XTS Mode and Cascaded Ciphers Explained
VeraCrypt encrypts data at the sector level using a modular architecture that separates block ciphers (AES, Serpent, Twofish) from encryption modes, implementing XTS mode exclusively for disk encryption with support for both single-cipher and cascaded algorithms.
VeraCrypt's encryption algorithm is implemented in the src/Volume directory of the veracrypt/VeraCrypt repository. The design follows a composable pattern that allows combining multiple block ciphers while standardizing on the XTS mode for all on-disk encryption operations.
Modular Architecture: Ciphers and Modes
VeraCrypt separates cipher primitives from encryption modes through three core abstractions defined in src/Volume/EncryptionAlgorithm.cpp, src/Volume/Cipher.cpp, and src/Volume/EncryptionModeXTS.cpp.
EncryptionAlgorithm Class
The EncryptionAlgorithm class acts as a container that manages one or more Cipher objects and a single EncryptionMode. It calculates the total key size by summing individual cipher key requirements and builds human-readable algorithm names. In EncryptionAlgorithm::GetAvailableAlgorithms(), VeraCrypt registers supported configurations:
EncryptionAlgorithmList l;
l.push_back (shared_ptr<EncryptionAlgorithm>(new AES()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new Serpent()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new Twofish()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new Camellia()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new Kuznyechik()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new AESTwofish()));
l.push_back (shared_ptr<EncryptionAlgorithm>(new AESTwofishSerpent()));
Cipher Implementations
Individual ciphers like CipherAES, CipherSerpent, and CipherTwofish inherit from the base Cipher class in src/Volume/Cipher.cpp. Each implements Encrypt(), Decrypt(), and hardware-accelerated variants. The base class handles runtime detection of AES-NI support via HasAESNI(), routing operations to CPU-intrinsic implementations when available.
EncryptionModeXTS
VeraCrypt exclusively uses XTS mode (XEX-based tweaked-codebook mode with ciphertext stealing) for volume encryption. The EncryptionModeXTS class in src/Volume/EncryptionModeXTS.cpp manages per-sector encryption by handling sector numbers as tweaks and coordinating with underlying ciphers.
Algorithm Composition and Key Distribution
VeraCrypt supports both single-cipher algorithms (e.g., AES) and cascaded algorithms (e.g., AES-Twofish-Serpent). When constructing a cascaded algorithm, the constructor populates the Ciphers vector with multiple cipher instances:
// AESTwofish constructor from EncryptionAlgorithm.cpp
Ciphers.push_back (shared_ptr<Cipher>(new CipherTwofish()));
Ciphers.push_back (shared_ptr<Cipher>(new CipherAES()));
SupportedModes.push_back (shared_ptr<EncryptionMode>(new EncryptionModeXTS()));
The total key size equals the sum of all cipher key sizes. In EncryptionAlgorithm::SetKey(), VeraCrypt segments the master key across ciphers using GetRange():
size_t keyOffset = 0;
foreach_ref (Cipher &c, Ciphers)
{
c.SetKey (key.GetRange(keyOffset, c.GetKeySize()));
keyOffset += c.GetKeySize();
}
For AES-Twofish-Serpent with 256-bit keys, this requires 96 bytes of key material (32 bytes × 3 ciphers).
XTS Mode Implementation Details
XTS mode operates on data units (typically 512-byte sectors) using two distinct keys:
- Data key: Encrypts the actual plaintext via the primary cipher
- Tweak key: Encrypts the sector number to generate a whitening value
In EncryptionModeXTS::EncryptBufferXTS(), the implementation performs:
- Derives the tweak from the sector number (
startDataUnitNo) - Encrypts the tweak with the secondary cipher to produce the whitening value
- Iterates through each block, XORing plaintext with the whitening value, encrypting with the primary cipher, then XORing back
- Applies ciphertext stealing for final partial blocks when sector size isn't a multiple of the block size
The high-level EncryptionAlgorithm::Encrypt() method delegates to the mode:
void EncryptionAlgorithm::Encrypt (uint8 *data, uint64 length) const
{
if_debug (ValidateState());
Mode->Encrypt (data, length); // XTS mode drives the cipher
}
Hardware Acceleration
VeraCrypt detects AES-NI support at runtime in src/Volume/Cipher.cpp:
#ifdef TC_AES_HW_CPU
state = HasAESNI() ? true : false;
#endif
When available, CipherAES routes encryption operations to hardware-accelerated implementations, significantly improving throughput for large volumes.
End-to-End Encryption Flow
The complete encryption path for a VeraCrypt volume follows these steps:
- Algorithm Selection: User selects an algorithm (e.g., "AES-Twofish"), instantiating the corresponding
EncryptionAlgorithmobject - Key Derivation: User password and/or keyfiles are processed through PBKDF2 (implemented in
src/Volume/Pkcs5Kdf.cpp) to generate the master key - Key Distribution:
EncryptionAlgorithm::SetKey()splits the master key across the cipher chain - Mode Attachment:
SetMode()attaches anEncryptionModeXTSinstance - Sector Processing: For each read/write operation,
EncryptSectors()orDecryptSectors()invokes XTS mode, which calls the underlyingCipherAESor other ciphers to transform data - Header Storage: Volume metadata including the selected algorithm and encrypted keys are stored via
src/Volume/VolumeHeader.cpp
Practical Code Examples
Single-Cipher Encryption (AES-XTS)
#include "EncryptionAlgorithm.h"
#include "Cipher.h"
int main ()
{
// Initialize AES algorithm
VeraCrypt::shared_ptr<VeraCrypt::EncryptionAlgorithm> algo (new VeraCrypt::AES());
// Prepare 256-bit key (two 128-bit keys for XTS)
uint8 key[32];
// ... populate key from cryptographically secure source ...
// Install key and encrypt sector
algo->SetKey (VeraCrypt::ConstBufferPtr (key, sizeof(key)));
uint8 sector[512] = { /* plaintext data */ };
algo->EncryptSectors (sector, 0, 1, 512); // sector 0, count 1, size 512
// Decrypt verification
algo->DecryptSectors (sector, 0, 1, 512);
}
Cascaded Algorithm (AES-Twofish-Serpent)
// Initialize triple-cascade algorithm
VeraCrypt::shared_ptr<VeraCrypt::EncryptionAlgorithm> algo
(new VeraCrypt::AESTwofishSerpent());
// Total key size: 256 + 256 + 256 = 768 bits (96 bytes)
uint8 key[96];
// ... fill with derived key material from PBKDF2 ...
algo->SetKey (VeraCrypt::ConstBufferPtr (key, sizeof(key)));
// Encrypt 4KB (8 sectors)
uint8 data[4096];
algo->Encrypt (data, sizeof(data));
Summary
- Modular Design: VeraCrypt separates ciphers from modes through
EncryptionAlgorithm,Cipher, andEncryptionModeXTSclasses insrc/Volume/ - XTS Exclusive: Only XTS mode is supported for disk encryption, using dual keys (data and tweak) with per-sector whitening
- Cascading Support: Algorithms can chain multiple ciphers (e.g., AES-Twofish-Serpent), with keys split proportionally across the cipher chain via
SetKey() - Hardware Acceleration: Automatic AES-NI detection in
Cipher.cpproutes to optimized CPU intrinsics when available - Sector-Level Operations: All encryption happens at the sector level (typically 512 bytes) through
EncryptSectors()andDecryptSectors()
Frequently Asked Questions
What encryption algorithm does VeraCrypt use for disk encryption?
VeraCrypt uses the XTS mode exclusively for on-disk encryption, combined with block ciphers such as AES, Serpent, Twofish, Camellia, or Kuznyechik. XTS treats each disk sector as an independent data unit encrypted with a tweak derived from the sector number, preventing patterns from emerging across identical plaintext sectors.
How does VeraCrypt handle multiple ciphers in a cascade?
When using cascaded algorithms like AES-Twofish-Serpent, VeraCrypt initializes multiple Cipher objects in the Ciphers vector within the EncryptionAlgorithm constructor. The master key is divided sequentially across ciphers in SetKey(), with each cipher receiving its required key size (e.g., 32 bytes for AES-256) from the appropriate byte range of the master key.
Why does VeraCrypt use XTS mode instead of CBC or other modes?
VeraCrypt implements XTS mode because it provides tweakable encryption that doesn't require an initialization vector (IV) stored on disk. By deriving the tweak from the sector number, XTS ensures that identical plaintext blocks in different sectors produce different ciphertexts, while remaining deterministic for decryption without additional metadata storage.
Does VeraCrypt support hardware acceleration for encryption?
Yes. VeraCrypt detects AES-NI (Advanced Encryption Standard New Instructions) support at runtime via HasAESNI() in src/Volume/Cipher.cpp. When available, the CipherAES class routes encryption operations to hardware-accelerated implementations, significantly improving throughput while maintaining compatibility with software fallback for systems without AES-NI support.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →