Documentation
¶
Overview ¶
Package krl reads, writes and checks OpenSSH key revocation lists (KRLs), the binary format of OpenSSH's PROTOCOL.krl that sshd loads through RevokedKeys and that ssh-keygen -k writes and ssh-keygen -Q queries. golang.org/x/crypto/ssh has no support for them.
A KRL revokes certificates by serial number (as lists, ranges or bitmaps) or by key ID, each under one CA key or under any CA, and revokes plain keys by their blob or by the SHA-1 or SHA-256 hash of their blob.
k, err := krl.Parse(data)
if err != nil { ... }
if k.IsRevoked(cert) { refuse the login }
b := krl.NewBuilder(version, "revoked by the bridge")
b.RevokeSerial(caKey, 42)
data, err := b.Marshal(time.Now())
Judged by ssh-keygen ¶
Parse, IsRevoked and Builder follow krl.c, OpenSSH's own implementation, and the tests hold them to it: KRLs written by ssh-keygen -k are parsed here and every certificate and key gets the same answer from IsRevoked as from ssh-keygen -Q; KRLs written by Builder are accepted by ssh-keygen -Q, which gives the same answers again and dumps the same revocations as for the KRL it wrote itself.
Builder chooses between list, range and bitmap encodings with krl.c's cost model, so its output is usually byte for byte what ssh-keygen writes for the same revocations. It departs from it on purpose in one place: ssh-keygen lets a bitmap grow past the largest integer its own reader accepts (2048 bytes), and then cannot read the KRL it wrote ("bignum is too large"), which is what happens to many sparse serials close together. Builder starts a new section instead.
Integrity: no signatures ¶
The format once had a signature section. OpenSSH never exposed a way to write it, has stopped verifying it, and recommends a detached SSHSIG signature (ssh-keygen -Y sign) instead. Parse still reads a KRL that has one, for old files, skips it as krl.c does and sets KRL.Signed; the signature is NOT verified, so Signed says nothing about authenticity. Builder never writes one. A KRL's integrity must come from how it is transported, such as HTTPS from a server the reader authenticates, or from a detached SSHSIG signature checked by the caller; this package does neither.
Differences from OpenSSH ¶
Keys are parsed with golang.org/x/crypto/ssh, not OpenSSH's sshkey, so a CA key in a certificate section is refused or accepted by that parser. The two agree on every key type OpenSSH supports, with small exceptions: x/crypto still parses ssh-dss keys, which OpenSSH 10 no longer does, and does not refuse RSA keys shorter than 1024 bits.
Like OpenSSH, Parse allocates in proportion to what the KRL revokes, so a KRL should come from a source trusted not to send an enormous one.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder accumulates revocations and encodes them as a KRL that ssh-keygen and sshd accept. It never writes a signature section. The Revoke methods record the first invalid request and Marshal returns it. A Builder is not safe for concurrent use.
func NewBuilder ¶
NewBuilder returns a Builder for a KRL with this krl_version and comment.
func (*Builder) Marshal ¶
Marshal encodes the KRL with now as its generation date. The output depends only on the revocations and on the order in which CAs were first named. Serials are encoded as lists, ranges and bitmaps chosen by the cost model of krl.c, except that a bitmap is never let grow past what OpenSSH can read back (ssh-keygen itself does not stop there).
func (*Builder) RevokeKey ¶
RevokeKey revokes a key the way ssh-keygen -k does for a public key line of its specification (ssh_krl_revoke_key): a plain key is listed as an explicit key, which also revokes every certificate made for it and, when it is a CA key, every certificate it signed; a certificate is revoked by its serial under the CA that signed it, or by its key ID when its serial is 0.
func (*Builder) RevokeKeyID ¶
RevokeKeyID revokes the certificates with this key ID signed by ca, or by any CA when ca is nil. An ID cannot contain a NUL.
func (*Builder) RevokeSerial ¶
RevokeSerial revokes the certificate with this serial signed by ca, or by any CA when ca is nil. Serial 0 cannot be revoked: it is what a CA writes when it names no serial, and OpenSSH refuses it.
type KRL ¶
type KRL struct {
// Version is krl_version, a number that grows each time the list
// changes.
Version uint64
// GeneratedDate is when the list was generated, in UTC.
GeneratedDate time.Time
// Flags is the header's flags word. PROTOCOL.krl defines none.
Flags uint64
// Comment is the free-form comment of the header.
Comment string
// Signed reports that the list carried a signature section. The
// signature is skipped, never verified: see the package documentation.
Signed bool
// contains filtered or unexported fields
}
KRL is a parsed key revocation list. The zero value revokes nothing. A KRL is not modified by IsRevoked and may be shared between goroutines.
func Parse ¶
Parse decodes a KRL in the format of OpenSSH's PROTOCOL.krl, refusing what ssh-keygen and sshd refuse: a bad magic or format version, truncated or trailing data, an unknown section or certificate subsection, an unknown extension marked critical, a CA key OpenSSH cannot parse, serial 0, an inverted serial range, a bitmap larger than OpenSSH reads or one that wraps past 2^64-1. Unknown extensions not marked critical are ignored. A signature section is skipped and recorded in Signed.
func (*KRL) IsRevoked ¶
IsRevoked reports whether key is revoked by k, with the semantics of ssh_krl_check_key in OpenSSH's krl.c. A plain key is revoked when its SHA-1 or SHA-256 fingerprint or its blob is listed. A certificate (*ssh.Certificate) is revoked when its public key is revoked that way, when its key ID or its serial is revoked under the CA that signed it or under any CA, or when that CA key is itself revoked. Serial 0, what a CA writes when it names none, is never revoked by serial.
Key IDs are compared as C strings, as OpenSSH does: an ID stops at its first NUL. OpenSSH refuses a certificate whose ID has an embedded NUL; golang.org/x/crypto/ssh does not, so such a certificate is judged by the part before the NUL.