Documentation
¶
Overview ¶
Package sshdecode parses SSH wire-protocol frames per RFC 4253 (SSH Transport Layer Protocol) and RFC 4250-4256. Specifically it covers the cleartext portions every SSH session emits before encryption is negotiated: the "SSH-2.0-..." version-exchange banner and the SSH_MSG_KEXINIT message that lists the algorithms each peer supports.
Wrap-vs-native judgement ¶
Native. RFC 4253 §4.2 defines the version banner as plain ASCII; §6 defines the binary packet envelope; §7.1 defines KEXINIT. SSH name-lists are comma-separated UTF-8 strings. Pasting either form into this decoder is enough — no SSH client/server, no key material, no live network attach.
This is the SSH counterpart to tls_handshake_decode: the HASSH / HASSHServer fingerprint (Salesforce, ben-aaron-bowers, 2018) is the SSH analogue of JA3 and identifies SSH client/ server stacks across thousands of distinct signatures.
What this package covers ¶
- **Version exchange line** (RFC 4253 §4.2): `SSH-protoversion-softwareversion [SP comments]` — broken out into protocol version (1.x / 1.99 / 2.0), software version (OpenSSH_8.9p1 / dropbear_2022.83 / libssh2_1.10.0 / etc.), and optional comment field.
- **Binary packet envelope** (RFC 4253 §6): `[packet_length:4][padding_length:1][payload][padding] [MAC]`. The packet length excludes the length field itself but includes padding_length, payload, and padding. MAC length is session-dependent so we surface it as raw trailing bytes (if any are present beyond the declared packet length).
- **Message type dispatch** (27-entry table from RFC 4250 §4.1.2): SSH_MSG_DISCONNECT (1) / SSH_MSG_IGNORE (2) / SSH_MSG_UNIMPLEMENTED (3) / SSH_MSG_DEBUG (4) / SSH_MSG_SERVICE_REQUEST (5) / SSH_MSG_SERVICE_ACCEPT (6) / SSH_MSG_EXT_INFO (7) / SSH_MSG_NEWCOMPRESS (8) / SSH_MSG_KEXINIT (20) / SSH_MSG_NEWKEYS (21) / SSH_MSG_KEXDH_INIT (30) / SSH_MSG_KEXDH_REPLY (31) / SSH_MSG_USERAUTH_REQUEST (50) / SSH_MSG_USERAUTH_FAILURE (51) / SSH_MSG_USERAUTH_SUCCESS (52) / SSH_MSG_USERAUTH_BANNER (53) / SSH_MSG_USERAUTH_INFO_REQUEST (60) / SSH_MSG_USERAUTH_INFO_RESPONSE (61) / SSH_MSG_GLOBAL_REQUEST (80) / SSH_MSG_REQUEST_SUCCESS (81) / SSH_MSG_REQUEST_FAILURE (82) / SSH_MSG_CHANNEL_OPEN (90) / SSH_MSG_CHANNEL_OPEN_CONFIRMATION (91) / SSH_MSG_CHANNEL_OPEN_FAILURE (92) / SSH_MSG_CHANNEL_WINDOW_ADJUST (93) / SSH_MSG_CHANNEL_DATA (94) / SSH_MSG_CHANNEL_EXTENDED_DATA (95) / SSH_MSG_CHANNEL_EOF (96) / SSH_MSG_CHANNEL_CLOSE (97) / SSH_MSG_CHANNEL_REQUEST (98) / SSH_MSG_CHANNEL_SUCCESS (99) / SSH_MSG_CHANNEL_FAILURE (100).
- **SSH_MSG_KEXINIT decode** (RFC 4253 §7.1): 16-byte cookie + 10 name-lists (kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, encryption_algorithms_s2c, mac_algorithms_c2s, mac_algorithms_s2c, compression_algorithms_c2s, compression_algorithms_s2c, languages_c2s, languages_s2c) + first_kex_packet_follows + 4-byte reserved.
- **HASSH / HASSHServer fingerprints** (Salesforce spec): the colon-separated string `kex_algos;encryption_algos;mac_algos;compression_algos` using c2s lists for HASSH and s2c lists for HASSHServer, plus the MD5 hash of each. The HASSH client fingerprint identifies the SSH client stack (OpenSSH version, PuTTY, libssh, JSch, ParamPro, etc.) across thousands of distinct signatures.
What this package does NOT cover (deliberately out of scope) ¶
- Encrypted body decode for post-KEXINIT packets: SSH_MSG_USERAUTH_*, SSH_MSG_CHANNEL_*, and friends are sent over the encrypted session. The envelope is decoded but the body is surfaced as raw hex.
- SSH-1 protocol (deprecated since ~2006) — banner parsing still works (the protocol-version field is surfaced), but the binary-packet path assumes SSH-2.
- HASSH version of JA4 family (JA4SSH from FoxIO, 2023) — different algorithm; deferred until real-world demand surfaces.
- Host-key extraction from SSH_MSG_KEXDH_REPLY — the RSA/ECDSA/Ed25519 public key blob is surfaced as hex but not parsed into structured form (that's a separate ASN.1 walker effort).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BinaryPacket ¶
type BinaryPacket struct {
PacketLength int `json:"packet_length"`
PaddingLength int `json:"padding_length"`
PayloadLength int `json:"payload_length"`
MessageType int `json:"message_type"`
MessageName string `json:"message_name"`
PaddingHex string `json:"padding_hex,omitempty"`
TrailingMACHex string `json:"trailing_mac_hex,omitempty"`
PayloadBodyHex string `json:"payload_body_hex,omitempty"`
KEXInit *KEXInit `json:"kex_init,omitempty"`
}
BinaryPacket is the decoded SSH binary-packet envelope plus the dispatched body.
type Frame ¶
type Frame struct {
HexInput string `json:"hex_input,omitempty"`
VersionBanner *VersionBanner `json:"version_banner,omitempty"`
BinaryPacket *BinaryPacket `json:"binary_packet,omitempty"`
}
Frame is the decoded view of an SSH frame. Exactly one of VersionBanner or BinaryPacket is non-nil per call.
func Decode ¶
Decode parses an SSH frame. Input starting with `SSH-` is treated as the version banner; anything else is parsed as a hex-encoded binary packet.
func DecodeBytes ¶
DecodeBytes parses a raw SSH binary-packet byte slice.
type KEXInit ¶
type KEXInit struct {
CookieHex string `json:"cookie_hex"`
KexAlgorithms []string `json:"kex_algorithms"`
ServerHostKeyAlgorithms []string `json:"server_host_key_algorithms"`
EncryptionAlgorithmsClientToServer []string `json:"encryption_algorithms_client_to_server"`
EncryptionAlgorithmsServerToClient []string `json:"encryption_algorithms_server_to_client"`
MACAlgorithmsClientToServer []string `json:"mac_algorithms_client_to_server"`
MACAlgorithmsServerToClient []string `json:"mac_algorithms_server_to_client"`
CompressionAlgorithmsClientToServer []string `json:"compression_algorithms_client_to_server"`
CompressionAlgorithmsServerToClient []string `json:"compression_algorithms_server_to_client"`
LanguagesClientToServer []string `json:"languages_client_to_server"`
LanguagesServerToClient []string `json:"languages_server_to_client"`
FirstKexPacketFollows bool `json:"first_kex_packet_follows"`
Reserved uint32 `json:"reserved"`
HASSH string `json:"hassh"`
HASSHHash string `json:"hassh_hash"`
HASSHServer string `json:"hassh_server"`
HASSHServerHash string `json:"hassh_server_hash"`
}
KEXInit is the SSH_MSG_KEXINIT body.