Desktop GUI Status
This release adds a lot to the desktop GUI, but the GUI is not in a finished state and should not be treated as the recommended interface for 1.4.9. It is rough, uneven, and in places incomplete. Several screens landed in this cycle specifically to reach parity with the CLI, and they have had far less real-world use than the command line.
The command-line interface is the stable, fully supported, and recommended way to use this tool. If you depend on this software, drive it from the CLI.
The desktop GUI is being pulled out into its own project and rewritten as a single, unified application. The goal of that rewrite is a GUI that adapts to whatever CLI version it is paired with, instead of the current situation where GUI and CLI are versioned and shipped together and drift apart. Until that rewrite ships, consider the GUI in this release a work in progress. Bugs and missing polish in the GUI are expected; the rewrite — not a patch to the current GUI — is where they will be addressed.
Highlights
- Broad pre-authentication hardening against crafted files, keystores, identity files, and portable USB drives: memory and time ceilings on key derivation, bounded length/count fields, and consistent escaping of untrusted metadata.
- The portable-USB "encrypted workspace" is now genuinely encrypted (it previously derived a key and never used it), and USB integrity verification now detects files added to a drive, including planted autorun payloads.
- Recovery slots are now cryptographically bound to the file, so they cannot be stripped from a file's header without decryption failing.
- Plugins must be signed by default, and built-in trust is now an allowlist.
- Many credentials that were previously only enterable interactively (second password, signer passphrase, recovery codes/passphrases) can now be supplied through dedicated environment variables that are read once and cleared.
- A large batch of terminal-escape / output-injection fixes across every surface that prints untrusted metadata.
- Seven documented-but-unrunnable commands now actually run, and global flags such as
--debugnow work after every subcommand.
Security
Key derivation, decryption, and resource-exhaustion
- Decryption now refuses a crafted file or keystore whose key-derivation cost parameters (Argon2 memory, scrypt
N, balloonspace_cost) would exhaust memory before any password check; peak memory is estimated and refused above an 8 GiB ceiling, overridable per file. - Decryption now enforces a hard KDF time ceiling in addition to the memory ceiling.
- Identity-file unlock now bounds the Argon2 cost parameters read from the file, so a tampered identity cannot exhaust memory before authentication.
verify-usbnow bounds the key-derivation cost it reads from the untrusted drive.- General pre-authentication resource-exhaustion hardening across the decrypt and auto-detection paths.
- The D-Bus
EncryptFilemethod no longer derives keys without password stretching. - The Balloon KDF now fails closed on v14+ metadata that is missing
space_costinstead of silently using a weak fallback. - The legacy no-hash-iterations KDF seed is now built in a wipeable buffer and zeroized after use.
derive-passwordno longer leaves the HSM pepper and derived-key output buffer in process memory.
Cryptographic wrapping and format hygiene
.pqckey files now wrap the long-lived post-quantum private key with Argon2id instead of a weak PBKDF2-SHA256; re-save a key file to move it onto the stronger wrapping.- The weak 10k-iteration PBKDF2 dual-encryption file-password verifier is no longer written or trusted; the file password is authenticated by the keystore's AEAD tag, and a mismatched dual-encryption state fails closed.
- The remote pepper is now sealed with a salted, memory-hard wrap key.
- The plaintext-hash confirmation oracle was removed from the file header (it stored
sha256(plaintext)). - Decryption refuses a file that embeds an unencrypted post-quantum private key.
Recovery slots and portable USB
- Recovery-slot presence is now authenticated. The number of recovery slots is cryptographically bound to the wrapped key, so slots can no longer be stripped from a file's header while normal password decryption silently succeeds. Adding or removing a slot now requires the primary password. Files gain this protection once re-encrypted or once a slot is added/removed on 1.4.9.
- The portable-USB workspace is now genuinely encrypted. It previously advertised an AES-256-GCM workspace but left the directory in cleartext (the key was derived and never used). New
seal/unlockcommands manage a real authenticated vault; re-create the drive to get the encrypted workspace. - Portable-USB integrity verification now records an allowlist of every file in the tool tree plus root autorun files and flags any file added afterward, not just a fixed set of types.
verify-usbnow flags symlinked path components as tampering, refuses non-regular files and named pipes when hashing, and escapes attacker-planted filenames in its output.create-usbno longer overwrites root autorun files, no longer copies private keys onto the drive, no longer leaks drive secrets on the command line, can no longer be tricked into overwriting an arbitrary file, and no longer gives a new drive the weakest key derivation in the tool. The on-drive helper now actually runs, and the drive key is held in a wipeable buffer and wiped on every path.
Plugins and trust
- Unsigned or unverifiable third-party plugins are refused by default; a non built-in plugin must carry a valid signature from an enrolled trust anchor. Opt back in with
OPENSSL_ENCRYPT_PLUGIN_SIGNATURE_POLICY=warn(oroff). - Built-in plugin trust is now an allowlist scoped to the shipped subtree, not a denylist; the third-party plugin directories are no longer auto-trusted.
- The plugin sandbox no longer authorizes a sibling directory that shares a name prefix.
- Trust-anchor enrollment now requires the full key fingerprint instead of accepting a forgeable short key id.
enable-plugin/disable-pluginnow actually take effect (they reported success and changed nothing).
Identity, signatures, and namespacing
- Identity loading now re-derives the fingerprint from the stored keys and fails closed on a mismatch.
- A contact can no longer be stored under one of your own identities' names and later substitute its keys for that name.
- Signature verification now rejects revoked and expired GPG keys.
identity create --hsm onlykeyno longer silently binds the identity to a YubiKey; the selected device is used end to end.- The PIV smartcard PIN now materializes one fewer unwipeable copy in memory.
Credential handling and secret redaction
- The second password (keyed hidden mode) and the signer passphrase can now be supplied through
OPENSSL_ENCRYPT_SECOND_PASSWORDandOPENSSL_ENCRYPT_SIGNER_PASSPHRASE; each is read once and cleared, a blank value is refused, and neither can switch a feature on by itself. - Recovery codes and passphrases can now be supplied through dedicated environment variables, read once and cleared; passing a recovery code on the command line now warns that it is visible in the process list.
- The keyserver bearer token, the
keyserver loginidentifier, and third-party HTTP request logging are now redacted or clamped under--debug. keyserver show-tokenno longer prints part of the bearer token.- Fixed a case where the file password could be printed in cleartext by the
--debugargv dump, and where a leading--defeated credential redaction and could trigger an unintended keyring deletion. tools/list_keystore_keys.pyno longer requires the keystore password on the command line.- Additional secret-redaction follow-ups so a secret cannot surface through an encoding-error message and stays redacted after its environment variable is cleared.
Terminal-escape and output-injection hardening
- Contact metadata (email, fingerprint, creation date) is validated everywhere it enters the program, and every terminal display of it — including data cached by earlier versions — escapes control characters instead of printing them. This closes an attack where a malicious contact file could overwrite the displayed fingerprint used for out-of-band verification.
- The same escaping now applies to the keyserver trust prompt, stored identity files read from disk, several signature-file fields, and keyserver error messages.
infoescapes terminal control characters in untrusted metadata and shell-quotes untrusted metadata in its reconstructed-CLI block.- Decrypt auto-detection escapes and bounds the untrusted file header.
- The desktop GUI escapes recovery-slot id/type in the removal dialog.
Bounds on untrusted input
- FLAC steganography bounds the untrusted
total_samplesfield. - Multi-QR key import bounds the untrusted
totalfield.
Transport, supply chain, and local hardening
- Keyserver login/registration reject non-HTTPS URLs and unconfigured servers.
liboqs,liboqs-python, and RandomX are built from pinned commit SHAs, not mutable tags.- The D-Bus
Properties.Setinterface now authorizes the caller and validates the value. - The legacy GUI no longer reads its KDF settings from a current-working-directory file; it resolves them to the per-user path.
- The GUI writes decrypted output owner-only (0600) rather than world-readable 0644.
- The mTLS client private key is no longer stored in plaintext preferences.
- The steganography password is no longer passed on the child process command line.
- Dependency bumps for published CVEs (pillow, cryptography, nltk, black).
New features
Command line
plugin peppermanagement command group: list stored peppers, test the connection, set up and verify TOTP two-factor authentication, and configure the dead-man's switch. The two-factor secret and backup codes go to stdout only and are redacted from debug output.plugin integritymanagement command group: connection test, verification statistics, and per-file verification with four distinct outcomes (match, mismatch, not-registered, unreachable).- Machine-readable output for
analyze-security,smart-recommendations, andtelemetry status(--output-format json/--json); the human report stays on stderr and the JSON carries no secrets. generate-password --json.--jsonoutput for the recovery-slot commands (list-recovery,recover,add-recovery,remove-recovery); a generated recovery code is written to a file you choose rather than printed.- Environment-variable input paths for the second password, the signer passphrase, and recovery credentials (see the security notes above).
- PIV / PKCS#11 protection for
identity create(--hsm pivand--hsm piv-only), with the token configuration persisted so the identity can be unlocked later without re-supplying the flags. The persisted PKCS#11 module path is loaded only from standard module directories or an explicit allowlist.
Desktop GUI
- Steganography extraction on the Decrypt tab.
- Asymmetric-decryption controls on the Decrypt tab (decryption-identity selector, verify-signature-from, and an explicit opt-in to skip verification).
- Password Generator screen (character and diceware modes).
- Secure Shred screen.
- Rekey screen.
- Recovery Slots screen.
- Live password-strength meter on the Encrypt tab.
- Verify Signature screen.
- Encrypt-tab PQC key file and key-derivation controls.
- Batch Operations tab brought to parity with the Encrypt tab.
- Option to securely delete the source file after encrypting.
- Identity HSM touch and contact key-change controls.
- Telemetry opt-out action in Settings.
- A lint that checks every argv the GUI builds against the real CLI parser, so the GUI cannot ship a command line the CLI would reject.
Removed
- The dead video-steganography surface (flags and code that were exposed but never functional) has been removed.
Notable fixes
- Seven documented commands (
create-usb,verify-usb,list-plugins,plugin-info,enable-plugin,disable-plugin,reload-plugin) that failed immediately with "invalid choice" now run. The routing set is now derived from the parser rather than hand-maintained, so this class of drift cannot recur. - Global flags such as
--debug,--verbose, and--quietnow work after every subcommand, not just about half of them. identity importnow accepts--data-stdinand--alias, so contact import works; the document is read from stdin rather than the command line.identity list --jsonnow exists, so the GUI identity listing works at all.telemetry opt-outnow writes a persistent marker so it is not silently re-enabled by a lingering environment variable;telemetry statusreports the real setting;telemetryreports failure through its exit code.analyze-configno longer crashes on every invocation, scores the flags you actually pass rather than argparse defaults, no longer claims post-quantum protection that is not configured, and accepts repeatable--compliance-frameworks.--parallel-kdfnow actually parallelizes v13/v14 files; the legacy dispatcher is retired so every format derives through one hardened, bounded thread pool.verify-signature --jsonnow answers on every outcome, including the refusal cases that matter most.rekey/decryptwith the output path equal to the input path no longer truncates the input; recovery-slot rewrites can no longer destroy the ciphertext they manage.--pqc-keyfilenow actually saves the key file (readable only by you), refuses a missing path instead of silently ignoring it, and an older code path that stored the post-quantum private key unencrypted has been removed.--guiis reachable viapython -mand starts the intended desktop application rather than the legacy interface.install-dependencies --yesis accepted; combined short options, abbreviated long options, and a leading--are handled correctly by the command scanner.- Recovery passphrases are held to the password policy.
keyserver/keyring fixes:--keyring-storeno longer silently stores nothing,--keyring-removefires only when asked and reports failure honestly, and confirmation questions are visible when stdout is redirected.encrypt --randomno longer crashes and has a safe delivery channel for the generated password; the orphan-password notice now fires on every incomplete encrypt exit and every early-exit path.- Assorted correctness fixes: AES-SIV called with the correct API on the adapter path, the legacy-KDF retry preserves its cause, the metadata validator fallback is reachable and bounded, a hardlinked
--random-password-outdestination is caught by the collision check, and the README's broken CLI examples are corrected. - Desktop GUI fixes: dead
plugincontrols removed or rewired, Whirlpool removed from the GUI, the debug window reachable again, identity listing fixed, the stdin helper drains output before closing, and widget tests no longer spawn the real CLI.
Upgrade and compatibility
- No default on-disk format change in 1.4.9. Files written by 1.4.8 and 1.4.9 are compatible, and files from earlier releases decrypt unchanged.
- Some security fixes apply only to data you re-process on 1.4.9:
- Re-encrypt a file, or add/remove a recovery slot on 1.4.9, to bind its recovery slots against stripping.
- Re-create a portable USB drive to get the genuinely encrypted workspace and the stronger integrity checks.
- Re-save a
.pqckey file to move it onto the Argon2id wrapping. - Re-create an OnlyKey identity if you created one on an earlier release with both an OnlyKey and a YubiKey attached.
- If you load third-party plugins, they must now be signed, or you must set
OPENSSL_ENCRYPT_PLUGIN_SIGNATURE_POLICY=warn(oroff) deliberately.
Verifying this release
The source-integrity manifest is GPG-signed. Verify it with a gpg binary you trust and a key fingerprint you have confirmed out of band (also published in SECURITY.md and openssl_encrypt/docs/SOURCE_INTEGRITY.md) — not with the built-in verifier, which runs code from the same tree it is checking:
gpg --verify openssl_encrypt/integrity/manifest.json.asc openssl_encrypt/integrity/manifest.json
Signing key fingerprint:
D269 D6A5 D6D7 CE52 CE1F C71D C2DF 2905 9ED6 5043