Under the hood

Paired authentication

The exact psh/3 identity encodings, transcripts, connection binding, and broker state rules.

Source snapshot 60c72c2bView original
On this page

This specification defines the mandatory Ed25519 and ML-DSA-87 identity profile. It is a psh application protocol construction, not a native composite TLS signature format or an OpenSSH wire format. Both components are required; neither classical-only nor PQ-only authentication is supported. Runtime cryptographic primitives come from pinned OpenSSL 4.1.0, with no additional provider dependency.

Identity and trust

A public identity is the ordered concatenation of a 32-byte Ed25519 public key and a 2,592-byte ML-DSA-87 public key. A private file contains exactly two PKCS#8 PEM objects in that order; its .pub companion contains exactly two SPKI PEM objects in that order. Only whitespace may surround or separate objects. Missing, reordered, duplicate, unsupported, third, or partially encoded objects are rejected. Private objects must both be plaintext or both encrypted; encrypted objects use one local passphrase but independent PBES2 salts and IVs. Existing 64 KiB file, owner, permission, no-symlink, exclusive-publication and cleansing rules apply.

The complete-pair identity digest H is:

SHA512("psh-identity-ed25519-mldsa87-v1\0" || ed_public[32] || ml_public[2592])

Domain strings include exactly one terminating NUL. The displayed fingerprint is SHA512: followed by unpadded Base64 of H (93 visible characters). Old SHA256: known-host records remain readable only for changed-key diagnosis and explicit removal; they cannot authenticate a psh/3 pair. Both components must match one provisioned identity, never components collected from separate authorized files. First-use confirmation requires independent verification of this complete-pair fingerprint to protect against an initial MITM.

Connection flow and encodings

ALPN is exclusively psh/3; outer PSH1 envelopes and reliable transport version 1 remain unchanged. DTLS requires 1.3, X25519MLKEM768, ChaCha20-Poly1305/SHA-256, and Ed25519 server raw-public-key authentication. The client has no TLS client certificate. Tickets, resumption, early data and compression remain disabled.

The 32-byte exporter uses label EXPORTER-psh-auth-v2 without NUL, and the 16-byte CID as context. Exactly one mutual authentication instance is permitted on the connection. The flow is:

DTLS -> HOST_PROOF -> host-pair verification/trust -> OPEN
     -> CHALLENGE -> dual client PROOF -> READY -> session

No OPEN, user identity, command, or session input is sent before checking the host proof and accepting trust in the complete pair. Actual TLS peer Ed25519 public bytes, local CID, and local exporter must be used for host verification.

FrameBody
HOST_PROOF=15ML public[2592], host nonce[32], ML signature[4627]: exactly 7251 bytes
OPEN=1Existing 8-byte terminal/string-length prefix, Ed public[32], ML public[2592], username, command
CHALLENGE=2Fresh broker client nonce[32]
PROOF=3Ed signature[64], ML signature[4627]: exactly 4691 bytes

Username and command limits remain 63 and 8192 bytes. OPEN is at most 10887 bytes; FRAME_MAX is 11015. Authentication transcript buffers hold 256 bytes. Signatures use normal Pure ML-DSA-87 with empty context and default message encoding; no external-mu or test-entropy modes are enabled. Both client signatures cover the same bytes and must independently verify.

Host transcript (161 bytes):
"psh-host-auth-v1\0" || H[64] || CID[16] || exporter[32] || host_nonce[32]

Client transcript (227 bytes):
"psh-client-auth-v2\0" || H[64] || CID[16] || exporter[32] || client_nonce[32]
|| SHA512(exact_OPEN_bytes)[64]

Host TLS authentication proves Ed25519 possession; the separate ML signature binds the complete host pair to that connection. Client proofs bind the complete host identity and the exact username, command, terminal policy, and client pair.

Broker and state boundaries

The network worker receives the existing Ed25519 TLS signing handle and public material only. The privileged broker owns both host private keys. Its existing TLS signing capability remains restricted to at most two correctly framed server CertificateVerify messages with SHA-256 transcript digests.

After DTLS, HOST_AUTH_BEGIN local IPC type 18 supplies exactly one exporter[32]. The broker requires at least one earlier TLS signing operation, stores the exporter immutably, generates host_nonce itself, constructs the host transcript using its loaded identity and supervisor-bound CID, and returns HOST_PROOF IPC type 19. At most one ML host proof is signed. All subsequent TLS signing requests and repeated host-auth requests fail. AUTH_BEGIN then contains only exact OPEN bytes, never replacement exporter material. The broker independently authorizes the complete client pair, generates a separate fresh client nonce, and verifies both signatures before spawning a process. Each challenge permits one attempt. Malformed, unexpected, repeated and out-of-phase operations terminate the exchange.

The exporter remains a worker assertion. A TLS signing count does not prove TLS completion or validate that assertion. This protocol preserves independent broker user authorization; it does not make a compromised TLS endpoint trustworthy. Both private keys are removed from workers and session relays through the existing privilege-drop/exec boundaries. Complete pairs govern audit, reload, revocation, and host rotation. Changing either host component retires existing sessions.

Resource and migration policy

The UDP ceiling remains 1200 bytes including the 22-byte envelope. Larger application frames use bounded reliable-stream segmentation. Existing 64 KiB BIO rings, reassembly limits, 16 pending authentications and 64 connections remain. DTLS and client host-proof reception each have a 15-second deadline. The broker’s initial 60-second budget includes host proof and waiting for OPEN; client proof has a 15-second single-attempt challenge budget. The supervisor’s existing 65-second admission-to-authenticated deadline is a tighter outer bound: a late OPEN may receive less than 15 seconds, and traffic never extends that outer bound.

Migration requires fresh paired identities, verified complete-pair pins, and coordinated client/daemon upgrades. Old keys are not converted or overwritten. Client default identity is id_psh_hybrid.pem; server examples use host_psh_hybrid.pem. YAML field names/version stay unchanged. Offline pin removal and provisioning remain explicit operations. There is no legacy login path. This profile does not assert overall security equivalence to OpenSSH or whole-protocol NIST category 5/CNSA compliance.

END OF DOCUMENT / psh/3All documentation