Repository navigation
+ VeraCrypt/TrueCrypt containers: open, mount and create - #808
Merged
Merged
Conversation
Hawkynt
force-pushed
the
feat/veracrypt-volumes
branch
from
November 10, 2026 01:51
c2dd5ac to
3ff4d58
Compare
Serpent and Twofish only encrypted, which was enough for the counter
and feedback modes that used them so far. XTS disk volumes need the
block decryption too.
Serpent.Decrypt runs the 32 rounds backwards: the final key, then per
round the inverse linear transformation and the inverse S-box (the
inverse permutation of the paper's S-box table) before the round key.
Twofish.Decrypt reverses the Feistel network with the round subkeys in
reverse order, swapping the one-bit rotations, and the whitening words
exchanged. Both keep the existing byte order (little-endian words,
NESSIE/LibTomCrypt order). Both now expose a public BlockSize const.
Sourcing: rung 3, the original papers ("Serpent: A Proposal for the
Advanced Encryption Standard", section 2, and "Twofish: A 128-Bit Block
Cipher", section 4.1), the same documents the encryption was written
from. No licence-compatible source needed taking for an inverse this
small.
Verified by decrypting every existing known-answer ciphertext (7
Serpent vectors from the LibTomCrypt self-test, 3 Twofish vectors from
the paper) back to its plaintext, and by random encrypt/decrypt round
trips for all Serpent key lengths 1-32 and Twofish 16/24/32.
Block ciphers VeraCrypt and TrueCrypt volumes may use besides AES, Serpent and Twofish. Each is a sealed class taking the key in its constructor, with in-place Encrypt/Decrypt and a public BlockSize. Camellia (RFC 3713): 128/192/256-bit keys, 16-byte blocks, big-endian as in the RFC. S-box lookups and the P-function are merged into eight 64-bit tables built once; decryption uses the subkey swaps of section 2.3.3. Kuznyechik (GOST R 34.12-2015, RFC 7801): 256-bit key, 16-byte blocks, byte 0 being the leftmost byte of the RFC's hex strings (a_15). L(S(x)) is merged into sixteen 128-bit lookup tables; decryption uses an L^-1 table and Pi^-1. The RFC's printed formula for l lists a_15 twice where a_14 is meant; the coefficients are confirmed by its R and L examples. CAST-128/CAST5 (RFC 2144): 5- to 16-byte keys zero-padded to 128 bits, 12 rounds for keys up to 80 bits, 8-byte big-endian blocks. Sourcing: rung 3, the RFCs. Permissively licensed implementations exist, but the RFCs are short, normative and complete, and give the test vectors, so writing from them was the cleaner route. All tables (Camellia SBOX1 and Sigma1-6, Kuznyechik Pi, Pi^-1 and the l coefficients, CAST-128 S1-S8) were extracted mechanically from the RFC text files rather than retyped. Verified against RFC 3713 Appendix A (all three key sizes), RFC 7801 sections 5.5/5.6 and RFC 2144 Appendix B.1 (128/80/40-bit keys) in both directions, the RFC 2144 B.2 million-iteration maintenance test, and random round trips for every key size.
Symptom: Serpent-XTS disk decryption ran at 2.7 MiB/s, so mounting a Serpent VeraCrypt volume crawled. Root cause: every S-box application looped over the 32 bit columns, looking each 4-bit nibble up in the paper's table - 32 table lookups and bit shuffles per S-box, 32 S-boxes per block. Fix: apply each S-box (and its inverse) to all 32 columns at once with Dag Arne Osvik's boolean circuits, converted statement for statement from Botan's serpent_sbox.h (Simplified BSD, rung 1; notice beside the code). The table form stays as the specification for the key schedule, and a test checks every circuit and its inverse against it. 2.7 -> 18 MiB/s for Serpent-XTS; the NESSIE/LibTomCrypt known-answer vectors still pass.
…eCrypt 1.0
VeraCrypt and TrueCrypt containers were not supported.
- Header: salt + 448 encrypted bytes, opened when magic and both CRCs agree.
Derivation and cipher chain are found by trial like the tools do: every
VeraCrypt PRF (SHA-512/256, Whirlpool, BLAKE2s-256, Streebog-512,
RIPEMD-160, Argon2id) with PIM, the TrueCrypt PRFs back to SHA-1, and
keyfiles. Primary, backup, hidden (64 KiB / TrueCrypt<6 end-1536) and
backup hidden headers.
- Sectors: XTS for AES, Serpent, Twofish, Camellia, Kuznyechik and every
cascade; TrueCrypt 4.x LRW over GF(2^128) and GF(2^64) (Blowfish, CAST5,
3DES); CBC with seeded IVs and CRC whitening, outer-CBC and inner-CBC
cascades. Data offsets/IV bases follow cryptsetup; pre-5.0 headers with
volume size 0 run to the end of the container.
- VeraCryptFormatDescriptor ("VeraCrypt", .hc/.tc): list/extract the
decrypted volume and metadata.ini, mount the filesystem inside read-only
or read-write (Pim/Prf/Cipher/Hidden/Keyfiles parameters), create
VeraCrypt or TrueCrypt 7.1 containers with any XTS chain and PRF, PIM,
keyfiles, random-filled or sparse unused space.
- XTS/LRW XOR all tweaks of a sector around one bulk ECB pass, and the
framework ciphers reuse cached ECB transforms (AES-XTS 13 -> 290 MiB/s).
Sourcing: rung 1 for the header side - cryptsetup lib/tcrypt (LGPL-2.1+)
converted, notice beside the code. Rung 2 for the legacy sector modes, which
cryptsetup leaves to the kernel: TrueCrypt 4.3a's Crypto.c (TrueCrypt
License) was read for docs/VERACRYPT-ON-DISK.md and compiled standalone as
an oracle; its output is committed under ReferenceVectors with provenance.
Verified:
- all 76 cryptsetup TCRYPT test images (TrueCrypt 1-7.1, VeraCrypt, hidden,
every PRF incl. Argon2id/BLAKE2s/Streebog) open, and every one whose data
survived decrypts to its FAT serial DEAD-BABE/CAFE-BABE;
- the 24 TrueCrypt 4.3a legacy vectors (CBC, outer/inner CBC, LRW-128/64)
match both ways - the Blowfish and CBC-cascade images carry no data;
- veracrypt 1.26 creates containers for every cipher/cascade/PRF and we
decrypt what it wrote; it mounts ours (sparse too); cryptsetup tcryptOpen
maps our VeraCrypt and TrueCrypt containers to our plaintext. Kernel-cipher
cases run on CI (new "VeraCrypt oracle" step, pinned + checksummed).
Closes #801
Attribution is a licence term: the converted cryptsetup TCRYPT code and Botan's Serpent circuits carry their notices beside the code, but the NuGet packages did not include them. Pack them under notices/ like the existing Sqx, Chfs, Lfs and PartImage notices.
New public types: Camellia, Kuznyechik, Cast5 (Core) and the TrueCrypt/ VeraCrypt reader, writer and descriptor (FileSystems). Regenerated with RepositoryTemplate's package-readme at v1; the Svr4Bfs namespace block is taken from main because the local run cannot load Svr4BfsWriter.
ProvenanceClaimsAreRecordedTests failed: Serpent.cs says its S-box circuits were converted from Botan, but docs/LICENCE-PROVENANCE.md had no row for it. Add class-A rows for the Botan circuits (BSD-2) and the cryptsetup TCRYPT conversion behind FileFormat.VeraCrypt (LGPL-2.1+), with their revisions and notices.
The VeraCrypt oracle failed its two Argon2 cases on CI: 1.26.24 has no Argon2 key derivation (VeraCrypt added it in 1.26.27), so it could neither create an Argon2 container nor mount ours. 1.26.29 is the release the oracle was run against locally; its .deb checksum is pinned.
Main gained the BitLocker types; recount and relist with the VeraCrypt ones (Svr4Bfs block kept from main, which the local run cannot load).
Hawkynt
force-pushed
the
feat/veracrypt-volumes
branch
from
November 10, 2026 03:03
3ff4d58 to
120248c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
VeraCrypt and TrueCrypt containers can now be opened, mounted and created — back to TrueCrypt 1.0.
Changes
FileFormat.VeraCrypt: header decryption by trial over every PRF (SHA-512/256, Whirlpool, BLAKE2s, Streebog, RIPEMD-160, SHA-1, Argon2id) with PIM and keyfiles; XTS for AES/Serpent/Twofish/Camellia/Kuznyechik and every cascade; TrueCrypt 4.x LRW (GF(2^128) and GF(2^64)) and CBC / outer-CBC / inner-CBC with Blowfish, CAST5, 3DES; backup and hidden headers.VeraCryptFormatDescriptor(.hc/.tc): list/extract, mount read-only/read-write (Pim/Prf/Cipher/Hidden/Keyfiles), create VeraCrypt or TrueCrypt 7.1 containers, optionally sparse.lib/tcrypt(LGPL) converted; legacy sector modes specified from TrueCrypt 4.3a, compiled standalone only as an oracle (Compression.Tests/VeraCrypt/ReferenceVectors). Spec:docs/VERACRYPT-ON-DISK.md. Notices packed.Verification
cryptsetup tcryptOpenmaps our VeraCrypt and TrueCrypt containers. Locally: AES/Kuznyechik cases (kernel modules missing on the dev host); all of them run in CI.Closes #801