Documentation
¶
Overview ¶
Package puttykey parses a PuTTY private key file (the ".ppk" / "PuTTY-User-Key-File-N" format) for triage. A saved PuTTY / WinSCP / FileZilla key is the Windows counterpart to a stolen id_ed25519 — top pentest loot — and the first questions are the same as for an OpenSSH key (see internal/sshkey): is it **encrypted** (so the passphrase must be cracked — putty2john + John the Ripper — before use)? what **key type**? what **SHA256 fingerprint** (to correlate the key with an authorized_keys entry / a known target identity)? and what **comment**? Unlike OpenSSH, a PPK's comment is a cleartext header so it is readable even for an encrypted key. Pure offline transform; no network or device.
Wrap-vs-native judgement ¶
Native. The .ppk format is a simple line-based text container (RFC-822-style "Key: value" headers wrapping two base64 blocks), documented in the PuTTY manual appendix. The public block is base64 of the **same SSH-wire public blob** that goes in an authorized_keys line, so the type + fingerprint are a base64-decode + a length-prefixed read + a SHA-256 — there is nothing to wrap. Pulling in a third-party PPK library (none is in go.mod) would add a runtime dep for what is a few dozen lines of text parsing. Consistent with the other in-tree key/loot parsers.
Verifiable / no confidently-wrong output ¶
The key-type + fingerprint are cross-validated against `ssh-keygen`: a PPK's Public-Lines base64 is byte-for-byte the same SSH-wire blob as the matching OpenSSH .pub, so SHA256(blob) reproduces `ssh-keygen -l`'s exact SHA256 fingerprint (confirmed for a generated ed25519 and rsa key). The header fields (version, Encryption, Comment, Key-Derivation, Argon2-*, Private-MAC) are plain-text reads against the documented PuTTY AppendixC format — no transform that could be confidently wrong. The Private-Lines section (PuTTY's own private-key layout) and the Private-MAC are surfaced/raw, not decoded or verified: MAC verification needs the passphrase-derived key, and a wrong "valid/invalid" verdict would be worse than none. A blob that does not begin with the PuTTY-User-Key-File- magic, or whose public block is missing / undecodable, is rejected.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Result ¶
type Result struct {
Format string `json:"format"` // always "ppk"
Version int `json:"version"` // 1, 2, or 3
Algorithm string `json:"algorithm"` // from the header line, e.g. ssh-ed25519
Encryption string `json:"encryption"` // none / aes256-cbc
Encrypted bool `json:"encrypted"`
KeyType string `json:"key_type"` // from the public blob (should match algorithm)
Fingerprint string `json:"fingerprint"` // SHA256:... (as `ssh-keygen -l` prints)
Comment string `json:"comment,omitempty"`
KeyDerivation string `json:"key_derivation,omitempty"` // Argon2id / Argon2i / Argon2d (v3 encrypted)
Argon2Memory int `json:"argon2_memory_kb,omitempty"`
Argon2Passes int `json:"argon2_passes,omitempty"`
Argon2Parallelism int `json:"argon2_parallelism,omitempty"`
Argon2SaltLen int `json:"argon2_salt_len,omitempty"`
PrivateMAC string `json:"private_mac,omitempty"`
Note string `json:"note,omitempty"`
}
Result is the triage view of a PuTTY .ppk private key file.