Security · GHOSTSHELL 0.2.0
Post-quantum where the protocol allows it. Labeled plaintext where it doesn't.
SSH offers four post-quantum hybrid key exchanges ahead of every classical one, led by ML-KEM-768 + X25519, and each tab shows the one the server chose. HTTPS, GhostVault's recovery and sharing, the release signatures and ML-DSA-44 + Ed25519 host keys pair a post-quantum algorithm with a classical one the same way. Where a protocol cannot, GHOSTSHELL says so: Telnet and raw sockets connect after a warning, VNC is marked unencrypted, and RDP hands over to Windows' own client.
01 What protects what
Every connection and store, and what protects it in 0.2.0.
The kind of protection, stated plainly: post-quantum hybrid where GHOSTSHELL pairs ML-KEM, NTRU Prime or ML-DSA with a classical algorithm, symmetric where 256-bit keys do the work, classical where only classical keys or methods are in use, and none where the protocol has no encryption of its own.
| Area | In 0.2.0 | Kind |
|---|---|---|
| SSH key exchange | Four post-quantum hybrids ahead of every classical method: ML-KEM-768 + X25519 first, then ML-KEM-1024 + P-384, ML-KEM-768 + P-256 and sntrup761 + X25519. The terminal and the tab show the one the server chose, with a warning when it is classical mlkem768x25519-sha256 | PQ hybrid |
| SSH ciphers | AES-256-GCM first, then ChaCha20-Poly1305; SHA-1, CBC and 128/192-bit ciphers off by default aes256-gcm@openssh.com | 256-bit symmetric |
| SSH host and user keys, ML-DSA | ML-DSA-44 + Ed25519, asked for first from a server not yet trusted, and usable as your login key; its SSH format is still an IETF draft ssh-mldsa44-ed25519 | PQ hybrid |
| SSH host and user keys, classical | Ed25519, ECDSA and RSA with SHA-2, the keys most servers have today; a server already trusted keeps its key type ssh-ed25519 | Classical |
| SSH through a proxy command | The outside program's own connection is outside every policy, and Windows' ssh.exe (OpenSSH 9.5) has no post-quantum key exchange; a jump host keeps the policy on every hop | Outside |
| HTTPS through Node | X25519MLKEM768 first, then ML-KEM-1024 alone, then the classical groups X25519MLKEM768 | PQ hybrid |
| GhostVault secrets | Passwords, key passphrases and, if you choose, the whole config.yaml: Argon2id, HKDF-SHA-512, AES-256-GCM | 256-bit symmetric |
| GhostVault recovery, escrow, sharing | ML-KEM-1024 + P-384 to encrypt recovery and escrow slots and bundles, ML-DSA-87 + P-384 to sign bundles; ML-KEM-1024 and ML-DSA-87 are the parameter sets CNSA 2.0 names | PQ hybrid |
| GhostLine history, session logs | AES-256-GCM per record, off by default | 256-bit symmetric |
| Releases | From 0.2.0, the installer and the portable zip are signed with the release key, ML-DSA-44 + Ed25519, and this site checks the installer's signature in your browser. This version carries no release signature, so only its SHA-256 vouches for it.This version is signed. | Hash onlyPQ hybrid |
| RDP | Windows' own Remote Desktop Connection, started with a generated .rdp file: the encryption is Windows' TLS, whose hybrid post-quantum groups are off by default | Windows |
| VNC | The embedded viewer, RFB 3.3, 3.7 and 3.8: unencrypted, and labeled that way | None |
| Telnet, raw sockets | Plain text, passwords included; a warning before connecting | None |
| Serial | A console cable on a COM port: no cryptography at all | None |
02 Key exchange
Two locks. Either one holds.
GHOSTSHELL implements the four post-quantum key exchanges published for SSH as RFCs and offers them ahead of every classical method. Each is a hybrid: a post-quantum KEM, ML-KEM or Streamlined NTRU Prime, runs beside a classical elliptic-curve exchange, and both secrets are hashed into the session key. When the server supports one of them, a session recorded today stays sealed as long as either half holds, against a future quantum computer or a flaw in young post-quantum code. When it supports none, the connection is classical and the terminal and the tab say so, or, under Quantum-safe only, it is refused.
kex mlkem768x25519-sha256
mlkem1024nistp384-sha384
mlkem768nistp256-sha256
sntrup761x25519-sha512
sntrup761x25519-sha512@openssh.com
curve25519-sha256
…
hostkey ssh-mldsa44-ed25519
ssh-mldsa44-ed25519@openssh.com
ssh-mldsa-87
ssh-mldsa-65
ssh-mldsa-44
ssh-ed25519
…
cipher aes256-gcm@openssh.com
chacha20-poly1305@openssh.com
…
- Policies
- Quantum-safe only · Quantum-safe preferred (default) · Compatible · Legacy
- Quantum-safe only
- The post-quantum methods and nothing else: a server without one is refused, with what it offers
- Preferred
- Every post-quantum hybrid first, even in a profile saved before that method existed, then the profile's classical methods without SHA-1
- Compatible, Legacy
- The profile's own ticked choices (Compatible) or everything the SSH library supports (Legacy), post-quantum first, for older equipment
- Shown
- After the handshake, before any password prompt, the terminal prints
Post-quantum key exchange: mlkem768x25519-sha256 · host key ssh-ed25519, or a warning that the exchange is classical. The tab carries a badge, and Connection security… lists every hop - Terrapin
- Strict key exchange against CVE-2023-48795, always offered
- Never
- The unencrypted
nonecipher or MAC, under any policy - Every hop
- A jump host gets at least the policy of the connection through it, and tabs share a connection only under the same policy
- Library
- GHOSTSHELL's own build of russh 0.65.1, which fixes the October 2026 russh advisories, with RustCrypto's ml-kem and sntrup-kem
| Method | What it is | Tested against |
|---|---|---|
| mlkem768x25519-sha256 | ML-KEM-768 with X25519, RFC 10042; OpenSSH's default since 10.0 | OpenSSH 10.5 |
| mlkem1024nistp384-sha384 | ML-KEM-1024 with ECDH P-384, RFC 10042. OpenSSH 10.5 does not have it, so it was tested against the project's own server, a rekey and a jump host included, and its ML-KEM-1024 against OpenSSL 3.5 in both directions | Own server |
| mlkem768nistp256-sha256 | ML-KEM-768 with ECDH P-256, RFC 10042, for servers limited to NIST curves; its ML-KEM-768 was also checked against OpenSSL 3.5 | OpenSSH 10.5 |
| sntrup761x25519-sha512 | Streamlined NTRU Prime with X25519, RFC 9941; the RFC's shared-secret test vector and the published sntrup761 test vectors pass | OpenSSH 10.5 |
| sntrup761x25519-sha512@openssh.com | The same method under the name OpenSSH 8.5 to 9.8 use. Under one name or the other, the method was OpenSSH's default from 9.0 until 10.0 | OpenSSH 10.5 |
| mlkem1024-sha384 | ML-KEM-1024 alone, from the CNSA 2.0 SSH profile, a draft that defines the exchange hash two contradictory ways. Not built, so never offered | Not built |
OpenSSH 10.5: the OpenSSH 10.5 client from Git for Windows completed a session with the method, a rekey included, against a test server built on GHOSTSHELL's SSH library, and both sides logged the method; a single differing byte would have changed the exchange hash and failed the connection. Own server: the app's SSH binding against that test server, which runs the same library. The binding completed every method built here against it. No OpenSSH server (sshd) has been tested from the app yet.
- OpenSSH 10.5
- 60 of 60 checks: its client against the test server for every key exchange both have, with ML-DSA and classical host keys, an ML-DSA user key, rekeys, ciphers, MACs and compression, and SSHSIG signatures both ways
- The app's binding
- 94 of 94 against the same server, inside Electron 43.7.0's runtime: every method and key type it implements, keyboard-interactive login, SFTP, port forwarding and a jump host
- OpenSSL 3.5
- 28 of 28 for ML-DSA keys and signatures, both directions; ML-KEM-768 and ML-KEM-1024 in the SSH library's own tests, both directions
- Unit tests
- 245 in the app: 244 pass, and the one skipped covers pure ML-KEM-1024, which is not built
- The message is the 16 × 16 ghost: 256 bits, exactly what ML-KEM carries.
- Each bit becomes 0 or 1,665 on a ring of 3,329 values, plus noise from the key exchange.
- The right key keeps the noise small. Rounding to the nearer pole returns every bit.
- Someone else's key leaves only noise. (ML-KEM would then fail its re-encryption check and hand back an unrelated key.)
A real ML-KEM-768 encapsulation, run in your browser with @noble/post-quantum 0.7.1, the library GhostVault uses (for ML-KEM-1024); the app's SSH key exchange runs ML-KEM inside its SSH library, GHOSTSHELL's build of russh with RustCrypto's ml-kem. Here the message is the ghost instead of random bytes; the decryption is FIPS 203's K-PKE.Decrypt, written out to show each value before rounding. An illustration, not a security tool.
03 Host keys and authentication
Post-quantum signatures, where the server has them.
For a server it has not trusted before, GHOSTSHELL asks for the hybrid ML-DSA-44 + Ed25519 host key first, under both its names, then ML-DSA alone, then Ed25519, ECDSA and RSA with SHA-2. A server already in its known hosts keeps the key type it is trusted with, so it is not asked about again; when a known server presents a key of a new type, the prompt says which type it was trusted with. ML-DSA private keys sign you in like any other key file.
A host key only has to hold during the handshake, so a session recorded today does not depend on it. What ML-DSA keys add is protection against a quantum computer impersonating a server, or you, at connection time, which the classical keys nearly every server has today cannot give. Post-quantum SSH signatures are still IETF drafts, so their names and formats may change.
| Key type | What it is | Tested against |
|---|---|---|
| ssh-mldsa44-ed25519 | ML-DSA-44 and Ed25519, both signing (draft-ietf-sshm-composite-sigs-00), under the name OpenSSH 10.6 enables. Its construction is the one OpenSSH 10.5 checked under the next row's name; OpenSSH 10.6 itself was not available, so this name was tested against the project's own server and OpenSSL 3.5 | Own server |
| ssh-mldsa44-ed25519@openssh.com | The same signature under the name OpenSSH 10.4 and 10.5 use. OpenSSH 10.5 verified the test server's host key signatures with a key its own ssh-keygen made, pinned in known_hosts, and refused an unknown one; a user key from ssh-keygen signed in through OpenSSH and through the app's binding | OpenSSH 10.5 |
| ssh-mldsa-87 ssh-mldsa-65 ssh-mldsa-44 | ML-DSA alone (draft-sfluhrer-ssh-mldsa-08), after the hybrids: post-quantum, with no classical half to fall back on. Tested against the project's own server and OpenSSL 3.5; no other SSH implementation was available | Own server |
| ssh-mldsa87-p384 | ML-DSA-87 with ECDSA P-384, from the composite draft. Not built: the draft gives its ML-DSA-87 signature as 4,896 bytes where FIPS 204 has 4,627, and leaves the ECDSA hash unspecified | Not built |
| ssh-ed25519 ecdsa-sha2-nistp* rsa-sha2-* | The classical keys most servers have; ecdsa-sha2-nistp521 only against the project's own server | OpenSSH 10.5 |
Not tested yet: signing in to an OpenSSH server (sshd) with an ML-DSA key, ML-DSA certificates, and ML-DSA keys held in an SSH agent. Host certificates are checked as bare keys, not as certificates.
04 HTTPS
X25519MLKEM768 first, then ML-KEM-1024, then the classical groups.
HTTPS that GHOSTSHELL makes through Node, for plugin installs and plugins' own requests, offers every post-quantum group its TLS library accepts ahead of the classical ones. That library is Electron 43's BoringSSL. It accepts the hybrid X25519MLKEM768 and MLKEM1024, which is ML-KEM-1024 alone, but neither NIST-curve hybrid (SecP256r1MLKEM768, SecP384r1MLKEM1024), so those two are not offered. The pre-standard X25519Kyber768Draft00 is never offered.
The policy applies from the next connection, in every window and in the main process. An encrypted configuration can be read only once GhostVault is unlocked, and until then the policy last applied stands in. Settings → Security shows the groups and TLS version actually in force, not the stored setting.
Chromium's own network stack, which the app uses for plugin search and update checks, keeps Chromium's defaults: X25519MLKEM768 first, with no way to refuse the classical groups. The toolbox's TLS check shows a server's certificate chain and the key exchange it negotiated, so you can see which one a host picks.
- Quantum-safe only
X25519MLKEM768:MLKEM1024over TLS 1.3; without a post-quantum group, connections fail rather than fall back- Preferred (default)
X25519MLKEM768:MLKEM1024:X25519:P-256:P-384:P-521, TLS 1.2 or later- Legacy
- As preferred, with TLS 1.0 and 1.1 for old internal servers
- Compatible
- BoringSSL's own defaults, classical only in Electron 43: X25519, P-256, P-384
- Tested
- Under Node and under Electron 43.7.0's runtime, over 127.0.0.1: each policy's ClientHello captured on the wire; preferred reached servers limited to each post-quantum group the library accepts, to X25519 and to TLS 1.2, and Quantum-safe only refused X25519-only and TLS 1.2 servers
05 GhostVault
Noise is the lock.
GhostVault seals SSH passwords, key passphrases and, if you choose, the whole config.yaml with Argon2id and AES-256-GCM, symmetric cryptography that a quantum computer would at most halve in strength. The slots that recovery kits and escrow vaults open, and shared bundles, are encrypted with a hybrid of ML-KEM-1024 and ECDH P-384, so opening one without its key takes breaking both, and shared bundles are signed with ML-DSA-87 and ECDSA P-384, a signature counting only if both halves verify.
GV2:Argon2id:AES-256-GCM:HKDF-SHA-512:ML-KEM-1024+P-384:ML-DSA-87+P-384
- Secrets
- Argon2id (RFC 9106: 64 MiB, 3 passes, 4 lanes; 256 MiB at the high setting) → HKDF-SHA-512 → AES-256-GCM. Without a vault, SSH passwords and key passphrases go to Windows Credential Manager
- Recovery, escrow, sharing
- ML-KEM-1024 + ECDH P-384, combined with HKDF-SHA-512 in GHOSTSHELL's own construction, which binds both ciphertexts and both public keys; no third party has analyzed it
- Signatures
- ML-DSA-87 + ECDSA P-384 on shared bundles. A valid signature shows that a bundle is intact, not who sent it; escrow files carry none
- History, logs
- GhostLine history and session logs: AES-256-GCM per record, off by default. A log ends with a sealed marker, so a cut is detected; history has none
- Lock
- Lock now, the timeout (1 minute by default), screen lock and sleep zero-fill the master key. The identity's secret keys and the history and logs keys are JavaScript strings, which cannot be wiped: they stay in memory until it is reused
- New passphrase
- Replaces the master key, so the old passphrase does not open the new file. The identity and keyring are kept, so an old copy of the file and the old passphrase still open history, logs, bundles and escrow slots made later
- Library
- @noble/post-quantum 0.7.1 for ML-KEM and ML-DSA, which passes the NIST test vectors, has not been independently audited and makes no constant-time claim; @noble/hashes 2.4.0 for Argon2id; BoringSSL for the classical halves
- Tested
- Unit tests for the primitives, the Argon2id limits, tamper detection, passphrase change, rotation, escrow, bundles and the record files; the flows in the running app were last run for 0.1.0
06 Releases
One release key, with two halves.
From 0.2.0, every installer and portable zip is signed with GHOSTSHELL's release key, which pairs ML-DSA-44 (FIPS 204) with Ed25519 in OpenSSH's signature format. Both halves sign, and a signature counts only when both verify, so it cannot be forged while either algorithm holds. This site checks the installer's signature in your browser, against the key it was built with; the file is never uploaded.
- Key type
- ML-DSA-44 + Ed25519 ·
ssh-mldsa44-ed25519@openssh.com - Fingerprint
SHA256:wcsMR20+ZSY6dRrHBgco/FFH9pOnyjqRh8A5u2s+C0M- Principal
releases@ghostshell.app- Namespace
ghostshell-release: a signature this key made for anything else never counts as a release- Checked by
- ssh-keygen 10.5 when a release is signed, this site's build, and your browser when you drop the installer on the Download page. ssh-keygen, OpenSSL 3.5 and @noble agree on every test signature, the rejected ones included
- ssh-keygen
- OpenSSH 10.4 and 10.5 check this key type. 10.6 renamed it and will not, so a new release key will follow once a 10.6 build can sign
- Not covered
- The installer is not Authenticode-signed, and the app's updater checks no signature
GHOSTSHELL 0.2.0 carries no release signature yet, so only its SHA-256 vouches for the file.This release is signed with this key. The key, the signature and how to check them
07 Limits
What this does not protect.
- Servers without post-quantum key exchangeA server that supports none of the four methods gets classical key exchange under every policy but Quantum-safe only, which refuses it. The terminal and the tab say so, because traffic recorded from that session could be decrypted later by a quantum computer.
- SSH host and user keysWith the Ed25519, ECDSA and RSA keys most servers have, authentication is classical: a quantum computer could impersonate the server or you at connection time. ML-DSA keys on both ends close that, under IETF drafts. Host certificates are checked as bare keys.
- What was testedOpenSSH 10.5 was tested as the client, against a server built on GHOSTSHELL's SSH library; the app has not yet been tested against an OpenSSH server (sshd).
mlkem1024nistp384-sha384, the OpenSSH 10.6 name of the ML-DSA hybrid and ML-DSA alone were tested only against the project's own server and OpenSSL. - Proxy commands, WinSCPA proxy command is an outside program, and its own connection is outside the policy: Windows'
ssh.exe(OpenSSH 9.5) has no post-quantum key exchange. A jump host (ProxyJump) keeps the policy on every hop. WinSCP and container tools make their own connections too. - RDPWindows' Remote Desktop client carries the session over Windows' TLS, whose hybrid post-quantum groups are off by default; GHOSTSHELL adds no layer of its own.
- VNCUnencrypted, and labeled that way: the embedded viewer has no VeNCrypt or TLS.
- Telnet, raw socketsPlain text, passwords included; GHOSTSHELL warns before it connects.
- SerialConsole cables over COM ports: no cryptography at all.
- The librariesThe post-quantum libraries GHOSTSHELL builds on for SSH and GhostVault have had no independent audit: RustCrypto's ml-kem, ml-dsa and sntrup-kem in the SSH library (sntrup-kem is a release candidate), and @noble/post-quantum in GhostVault, which passes the NIST test vectors and makes no constant-time claim. HTTPS uses Electron's BoringSSL.
- GhostVault in memoryLocking zero-fills the master key, but the identity and keyring keys stay in memory as strings until it is reused, and malware running as you can read an unlocked vault or the passphrase as you type it. A new passphrase keeps the identity and keyring.
- The installerGHOSTSHELL 0.2.0 is not code-signed (Authenticode), so SmartScreen warns.The installer is code-signed (Authenticode). The app's updater checks no signature. The release signature on the Download page shows a file was signed with GHOSTSHELL's release key; the SHA-256 alone shows only that it matches what this site publishes.