github dangel34/PQ-File-Encryption v4.3.4
pqfile v4.3.4

latest release: v4.3.5
one month ago

Full Changelog: v4.3.3...v4.3.4

Security fixes
Eight findings from an external source code review, all confirmed and fixed:

Anonymous multi recipient metadata leak (v8/v9 formats). Real recipient slots were zero padded instead of randomly padded. Since each ML KEM variant has a different ciphertext length, this let an observer recover a recipient's key variant, and in the v9 padded mode, distinguish real recipients from the random dummy slots meant to hide the true count. Slots are now filled with random bytes before the real ciphertext is written, closing the leak. Fully compatible with existing files, no format change needed.

Private key write race and permission downgrade. The core private key writer wrote key bytes to disk before restricting file permissions, leaving a brief window where a permissive umask could expose the file, plus a related issue where overwriting a key reused the same file on disk instead of writing a fresh one. Separately, the desktop app's key rewrite paths (SSH import, expiry stamping, changing a passphrase) could silently downgrade an existing key from owner only permissions to whatever the system default was. Both are now fixed with a create restricted, write, sync, then rename approach that never exposes key bytes at a permissive path.

Language binding file operations were not atomic. The Python, Node.js, and mobile bindings' file based encrypt and decrypt functions wrote directly to the destination file. A decryption that failed partway through could leave a partial, unauthenticated result sitting at the output path instead of leaving it untouched. Both operations now write to a temporary file and only replace the destination after the whole operation succeeds.
Encrypting to zero recipients silently succeeded. Multi recipient encryption accepted an empty recipient list and produced a file that no key could ever decrypt, a silent data loss trap. This now fails immediately with a clear error instead of writing an unusable file.

Encoders did not enforce the same size limits as the reader. It was possible to encrypt with an oversized file length or chunk size that the reader would later refuse, and a chunk size cast could silently truncate to an unexpected value on 64 bit systems. Encoders now check the same bounds as decoders before writing anything.
Typed key objects skipped body length checks. The public and private key wrapper types validated the PEM tag but not the length of the key data itself, so a truncated or padded key could be accepted and only fail later during an actual cryptographic operation. All four key wrapper types now validate length up front.

Passphrase decryption did not cap Argon2 parallelism. Memory and time cost were already capped to prevent a crafted file from forcing excessive resource use, but parallelism was not. This is now capped as well, before the expensive key derivation step runs.

Documentation corrections. Key fingerprints have been 16 bytes for a while, but a few places still described them as 8. The Linux hardware key backend was described as a Secret Service integration when it has actually been the kernel keyring for some time, with different persistence behavior. Both corrected across the README and docs.

Dependency and toolchain updates
Rust toolchain updated from 1.96.0 to 1.98.0, with the one new clippy lint it introduced addressed.
Routine dependency refresh across the workspace and all language binding crates, including a fix for a quadratic time complexity issue in a Node.js development dependency.
GitHub Actions pins refreshed to their latest versions.
cargo semver checks updated locally to keep pace with the newer Rust toolchain's output format.

Notes
No breaking changes. Existing .pqf files and keys continue to work exactly as before; the metadata leak fix only affects newly written v8/v9 files going forward.

Don't miss a new PQ-File-Encryption release

NewReleases is sending notifications on new releases.