# Reconstruction notes — DES/UNDES V1.1.0 ## Confidence The main program logic is now reconstructed with high confidence. The supplied `cryptl99.zip` is especially useful because it contains the exact CRYPTLIB 0.99 API and libdes 3.14 source that was linked into the executables. This is **not** the original source text. Original variable names, comments, whitespace and file decomposition were not retained in the executable. Names such as `firstTime`, `passwordBlock`, `negativeMarker`, and the split into `des_common.c`/`.h` are descriptive reconstruction choices. ## Compiler and memory model Both programs are 16-bit MS-DOS MZ executables and contain the runtime string: `Turbo-C - Copyright (c) 1988 Borland Intl.` The generated code uses far data pointers extensively, and `malloc()` returns a segment:offset pointer. That is consistent with a Turbo C large-memory-model build. ## Exact crypto dependency The executables contain: `libdes v 3.14 - eay` The supplied CRYPTLIB archive contains the same `libdes/version.h` string, plus `crypt.h`, `crypt.c`, `lib_3des.c` and the relevant libdes sources. This is the actual dependency family used by the executables rather than merely a later compatible library. ## Important correction: it is 3DES The machine code assigns value 3 to both the algorithm and mode variables. According to the supplied `crypt.h`: - `CRYPT_ALGO_3DES` = 3 - `CRYPT_MODE_CBC` = 3 Therefore the encryption is **two-key 3DES-CBC**, despite the executable being called `des.exe`. `crypt.c` defines the recommended 3DES-CBC parameters as: - key size: 112 bits = 14 bytes - IV size: 32 bits = 4 bytes - DES block size: 64 bits = 8 bytes The main routine obtains both sizes through `queryAlgoModeInformation()`. ## Password handling The command-line password is copied directly into a 14-byte key buffer and is zero-padded if shorter. Characters after byte 14 do not influence encryption. There is no password hash or KDF in the DES/UNDES layer. CRYPTLIB's `des3InitKey()` then constructs the two DES key schedules from that user-key buffer. `des_check_key` in the bundled libdes defaults to zero, so parity/weak-key checking is not enabled by default. ## File layout For a non-empty encrypted input the file is: ```text Offset Size Meaning 0 4 file version: 0x00002775 4 4 CRYPTLIB-recommended CBC IV 8 256 encrypted password-check block 264 16384 encrypted data block 1 16648 16384 encrypted data block 2 ... ... ``` For an empty input, `des.exe` writes only the four-byte version. Because no `encryptBuffer()` call with data occurs, no IV or password-check block is written. ## Data-block layout before encryption Each 16384-byte encrypted data block consists of: ```text 0..3 signed 32-bit length marker 4..7 signed 32-bit secondary marker 8..16383 at most 16376 bytes input data ``` For a partial final block, the first field is the positive plaintext byte count. For a full block it is a negative value; `undes.exe` interprets any negative value as exactly 16376 plaintext bytes. The full-block negative value is formed from the absolute value of the first signed 16-bit word of the plaintext and then negated. If that produces zero, `-1` is stored instead. The second field is also a negative marker derived from a signed 16-bit plaintext word. `undes.exe` does not use this second field. The exact purpose of the secondary marker cannot be established from the executables alone; its computation is established, its design intent is not. ## CBC ordering on the first block An unusual but confirmed detail is the first-block ordering. `des.exe` performs the cryptographic operations in this order: 1. encrypt first 16 KiB data block 2. retrieve the now-generated IV 3. encrypt the 256-byte password-check block but writes: 1. IV 2. encrypted password-check block 3. encrypted first data block `undes.exe` reads the stored password block and first data block, then decrypts in cryptographic order: 1. decrypt first data block 2. decrypt password-check block This preserves the CBC state exactly. The apparent storage/processing order mismatch is therefore intentional in the program logic, not a decompiler error. ## Password-check block Before encryption, the first 14 bytes of the 256-byte password-check block are the derived 14-byte key. The executable does not initialise the remaining 242 bytes before encrypting the block. During decryption only the first 14 bytes are compared. ## Uninitialised bytes in the final data block `des.exe` always encrypts the complete 16384-byte buffer. If the final input chunk is shorter than 16376 bytes, the unused tail is not explicitly cleared first. This is also visible in the machine code. UNDES writes only the number of plaintext bytes stored in the first 32-bit field. ## Error handling and robustness Read errors are detected only when `read()` returns `-1`; short reads are not rejected. `undes.exe` does not authenticate ciphertext and does not validate a positive decrypted length against 16376 before passing it to `fwrite()`. These are normal limitations for small DOS-era utility code, but they matter if old encrypted files are corrupted or attacker-controlled. ## Remaining uncertainty The following cannot be recovered exactly without original source/debug symbols: - original variable/function names for the DES/UNDES-specific code; - original comments and formatting; - exact source-file decomposition; - the design rationale for the secondary 32-bit marker; - the symbolic name/formula originally used for file version `0x2775`. The actual program behaviour no longer depends on guessed CRYPTLIB API names.