VeraCrypt RAM Encryption Mechanism
Introduction
VeraCrypt RAM Encryption aims to protect disk encryption keys stored in volatile memory against certain types of attacks. The primary objectives of this mechanism are:
- To protect against cold boot attacks.
- To add an obfuscation layer to significantly complicate the recovery of encryption master keys from memory dumps, be it live or offline.
Implementation Overview
Here's a summary of how RAM encryption is achieved:
- At Windows startup, the VeraCrypt driver allocates a 1MiB memory region. If this fails, we device the size by two until allocation succeeds (minimal size being 8KiB). All these variables are allocated in non-paged Kernel memory space.
- This memory region is then populated with random bytes generated by a CSPRNG based on ChaCha20.
- Two random 64-bit integers,
HashSeedMask
andCipherIVMask
, are generated. - For every master key of a volume, the RAM encryption algorithm derives a unique key from a combination of the memory region and unique values extracted from the memory to be encrypted. This ensures a distinct key for each encrypted memory region. The use of location-dependent keys and IVs prevents master keys from being easily extracted from memory dumps.
- The master keys are decrypted for every request, requiring a fast decryption algorithm. For this, ChaCha12 is utilized.
- Once a volume is mounted, its master keys are immediately encrypted using the described algorithm.
- For each I/O request for a volume, the master keys are decrypted only for the duration of that request and then securely wiped.
- Upon volume dismounting, the encrypted master keys are securely removed from memory.
- At Windows shutdown or reboot, the memory region allocated during startup is securely wiped.
Protection against Cold Boot Attacks
The mitigation of cold boot attacks is achieved by utilizing a large memory page for key derivation. This ensures that attackers cannot recover the master key since parts of this large memory area would likely be corrupted and irrecoverable after shutdown. Further details on cold boot attacks and mitigation techniques can be found in the referenced papers:
Incompatibility with Windows Hibernate and Fast Startup
RAM Encryption in VeraCrypt is not compatible with the Windows Hibernate and Fast Startup features. Before activating RAM Encryption, these features will be disabled by VeraCrypt to ensure the security and functionality of the encryption mechanism.
Algorithm Choices
The choice of algorithms was based on a balance between security and performance:
- t1ha2: A non-cryptographic hash function chosen for its impressive speed and ability to achieve GiB/s hashing rates. This is vital since keys are derived from a 1MB memory region for every encryption/decryption request. It also respects the strict avalanche criteria which is crucial for this use case.
- ChaCha12: Chosen over ChaCha20 for performance reasons, it offers sufficient encryption strength while maintaining fast encryption/decryption speeds.
Key Algorithms
Two core algorithms are fundamental to the RAM encryption process:
1. VcGetEncryptionID
Computes a unique ID for the RAM buffer set to be encrypted.
- Input: pCryptoInfo, a CRYPTO_INFO variable to encrypt/decrypt
- Output: A 64-bit integer identifying the pCryptoInfo variable
- Steps:
- Compute the sum of the virtual memory addresses of the fields ks and ks2 of pCryptoInfo: encID = ((uint64) pCryptoInfo->ks) + ((uint64) pCryptoInfo->ks2)
- Return the result
2. VcProtectMemory
Encrypts the RAM buffer using the unique ID generated by VcGetEncryptionID.
- Input:
- encID, unique ID for memory to be encrypted
- pbData, pointer to the memory to be encrypted
- pbKeyDerivationArea, memory area allocated by the driver at startup
- HashSeedMask and CipherIVMask, two 64-bit random integers from startup
- Output:
- None; memory at pbData is encrypted in place
- Steps:
- Derive hashSeed: hashSeed = (((uint64) pbKeyDerivationArea) + encID) ^ HashSeedMask
- Compute 128-bit hash: hash128 = t1h2 (pbKeyDerivationArea,hashSeed).
- Decompose hash128 into two 64-bit integers: hash128 = hash128_1 || hash128_2
- Create a 256-bit key for ChaCha12: chachaKey = hash128_1 || hash128_2 || (hash128_1 OR hash128_2) || (hash128_1 + hash128_2)
- Encrypt chachaKey by itself using ChaCha12 using hashSeed as an IV: ChaCha256Encrypt (chachaKey, hashSeed, chachaKey)
- Derive the 64-bit IV for ChaCha12: chachaIV = (((uint64) pbKeyDerivationArea) + encID) ^ CipherIVMask
- Encrypt memory at pbData using ChaCha12: ChaCha256Encrypt (chachaKey, chachaIV, pbData)
- Securely erase temporary values
It's important to note that, due to ChaCha12 being a stream cipher, encryption and decryption processes are identical, and the VcProtectMemory
function can be used for both.
For a deeper understanding and a look into the codebase, one can visit the VeraCrypt repository and explore the mentioned functions in the src/Common/Crypto.c
file.
Additional Security Measures
Starting from version 1.24, VeraCrypt has integrated a mechanism that detects the insertion of new devices into the system when System Encryption is active. If a new device is inserted, master keys are immediately purged from memory, resulting in a Windows BSOD. This protects against attacks using specialized devices to extract memory from running systems. However, for maximum efficiency, this feature should be paired with RAM encryption.
To enable this feature, navigate to the menu System -> Settings and check the "Clear encryption keys from memory if a new device is inserted" option.
Technical Limitations with Hibernation and Fast Startup
The Windows Hibernate and Fast Startup features save the content of RAM to the hard drive. In the context of VeraCrypt's RAM Encryption, supporting these features presents a significant challenge, namely a chicken-egg problem.
To maintain security, the large memory region used for key derivation in RAM Encryption would have to be stored in an encrypted format, separate from the usual VeraCrypt encryption applied to the current drive. This separate encrypted storage must also be unlockable using the same password as the one used for Pre-Boot Authentication. Moreover, this process must happen early in the boot sequence before filesystem access is available, necessitating the raw storage of encrypted data in specific sectors of a different disk.
While this is technically feasible, the complexity and user-unfriendliness of such a solution make it impractical for standard deployments. Therefore, enabling RAM Encryption necessitates the disabling of the Windows Hibernate and Fast Startup features.