Documentation
¶
Overview ¶
Package sshpub triages OpenSSH *public* keys — the authorized_keys / known_hosts / bare public-key lines an operator finds on a host during IR or an audit. It is the public-key counterpart to the private-key decoders (ssh_privkey_decode, pem_privkey_decode, putty_privkey_decode): given a line (or a whole file of lines), it reports per key the type, key size, and the SHA256 + MD5 fingerprints exactly as `ssh-keygen -l` prints them, plus the comment, any authorized_keys options, and any known_hosts marker / host field.
Two forensic levers beyond plain parsing:
- For an ssh-rsa key it surfaces the modulus (hex) so the key chains straight into roca_detect — fingerprint the key, then screen the RSA ones for the Infineon ROCA weakness.
- For a hashed known_hosts entry (|1|salt|hash) it can test a candidate hostname against the HMAC-SHA1, deanonymising which host the entry refers to — the same check `ssh-keygen -F` performs, the standard technique for mapping lateral movement off a captured known_hosts file.
Wrap-vs-native: native — the public-key wire blob is parsed directly off the RFC 4253 length-prefixed format (a sequence of uint32-prefixed strings) and the fingerprints are stdlib crypto/sha256 + crypto/md5 + crypto/hmac over that blob. No x/crypto/ssh dependency (and so none of its govulncheck surface) and no new go.mod entry. The fingerprint, key-size, and hashed-host computations are pinned against `ssh-keygen -l` / `-H` / `-F`.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Key ¶
type Key struct {
// Type is the declared/embedded key algorithm, e.g. "ssh-ed25519",
// "ssh-rsa", "ecdsa-sha2-nistp256", "ssh-dss",
// "sk-ssh-ed25519@openssh.com".
Type string `json:"type"`
// Label is the friendly type ssh-keygen prints in parentheses
// (ED25519 / RSA / ECDSA / DSA / ED25519-SK / ECDSA-SK).
Label string `json:"label"`
// Bits is the key size as ssh-keygen reports it.
Bits int `json:"bits"`
// FingerprintSHA256 is "SHA256:" + base64(SHA-256(blob)) without padding.
FingerprintSHA256 string `json:"fingerprint_sha256"`
// FingerprintMD5 is "MD5:" + colon-separated lowercase hex of MD5(blob).
FingerprintMD5 string `json:"fingerprint_md5"`
// Comment is the trailing comment field, if any.
Comment string `json:"comment,omitempty"`
// Options is the authorized_keys options field, when the prefix parsed as
// options (contained "=" or a known option keyword).
Options string `json:"options,omitempty"`
// Marker is a known_hosts marker: "@cert-authority" or "@revoked".
Marker string `json:"marker,omitempty"`
// Hosts is the known_hosts host field (raw — may be a comma list, a
// [host]:port form, or a |1|salt|hash hashed entry).
Hosts string `json:"hosts,omitempty"`
// HashedHost is true when Hosts is a |1|salt|hash hashed known_hosts entry.
HashedHost bool `json:"hashed_host,omitempty"`
// HostMatch is set to the candidate hostname when it was supplied and
// matched this entry's host field (hashed via HMAC-SHA1, or plaintext).
HostMatch string `json:"host_match,omitempty"`
// RSAModulusHex is the ssh-rsa modulus in hex, for chaining into
// roca_detect. Empty for non-RSA keys.
RSAModulusHex string `json:"rsa_modulus_hex,omitempty"`
// Note carries interpretation guidance (weak key size, ROCA chaining, …).
Note string `json:"note,omitempty"`
}
Key is one parsed public-key entry.
type Result ¶
type Result struct {
// Keys is one entry per parsed line.
Keys []Key `json:"keys"`
// Count is len(Keys).
Count int `json:"count"`
}
Result is the outcome of a Decode call.
func Decode ¶
Decode parses one or many OpenSSH public-key lines (newline-separated). candidateHost, when non-empty, is tested against each entry's host field — matching hashed (|1|salt|hash) entries via HMAC-SHA1 and plaintext host lists directly. Blank lines and #-comments are skipped. A line that does not parse is reported as a Key with a Note rather than aborting the whole batch.