aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/stripe/krl/krl.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/stripe/krl/krl.go')
-rw-r--r--vendor/github.com/stripe/krl/krl.go372
1 files changed, 372 insertions, 0 deletions
diff --git a/vendor/github.com/stripe/krl/krl.go b/vendor/github.com/stripe/krl/krl.go
new file mode 100644
index 0000000..e3457f8
--- /dev/null
+++ b/vendor/github.com/stripe/krl/krl.go
@@ -0,0 +1,372 @@
+// Package krl provides functionality for reading and writing SSH Key Revocation
+// Lists (KRLs).
+//
+// References:
+// https://raw.githubusercontent.com/openssh/openssh-portable/master/PROTOCOL.krl
+package krl
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "io"
+ "math/big"
+ "sort"
+ "time"
+
+ "golang.org/x/crypto/ssh"
+)
+
+// KRL, or Key Revocation List, is a list of revoked keys, certificates, and
+// identities, possibly signed by some authority. The zero value of KRL is
+// appropriate for use, and represents an empty list.
+type KRL struct {
+ // Version is a number that increases every time the KRL is modified.
+ // When marshaling a KRL, if Version is zero GeneratedDate will be used
+ // instead.
+ Version uint64
+ // GeneratedDate is the Unix timestamp the KRL was generated at. When
+ // marshaling a KRL, if GeneratedDate is zero the current Unix timestamp
+ // will be used instead.
+ GeneratedDate uint64
+ // Comment is an optional comment for the KRL.
+ Comment string
+ // Sections is a list of public key and certificate selectors that this
+ // KRL applies to.
+ Sections []KRLSection
+ // SigningKeys is set by ParseKRL and Marshal to the list of Signers
+ // that signed (or which claimed to sign) the KRL in the order they
+ // appeared (i.e., innermost-first).
+ SigningKeys []ssh.PublicKey
+}
+
+/*
+KRLSection describes a section of a KRL, which selects certain certificates and
+keys for revocation. The concrete types KRLCertificateSection,
+KRLExplicitKeySection, and KRLFingerprintSection satisfy this interface, and
+correspond to the three types of KRL sections currently defined.
+*/
+type KRLSection interface {
+ isRevoked(key ssh.PublicKey) bool
+ marshal() []byte
+}
+
+/*
+KRLCertificateSection revokes SSH certificates by certificate authority and
+either serial numbers or key ids.
+*/
+type KRLCertificateSection struct {
+ // CA is the certificate authority whose keys are being revoked by this
+ // section. If CA is nil, this section applies to keys signed by any
+ // certificate authority.
+ CA ssh.PublicKey
+ // Sections is a list of certificate selectors.
+ Sections []KRLCertificateSubsection
+}
+
+func (k *KRLCertificateSection) isRevoked(key ssh.PublicKey) bool {
+ cert, ok := key.(*ssh.Certificate)
+ if !ok {
+ return false
+ }
+
+ if k.CA != nil {
+ sk := cert.SignatureKey.Marshal()
+ ca := k.CA.Marshal()
+ if !bytes.Equal(sk, ca) {
+ return false
+ }
+ }
+
+ for _, section := range k.Sections {
+ if section.isRevoked(cert) {
+ return true
+ }
+ }
+ return false
+}
+
+func (k *KRLCertificateSection) marshal() []byte {
+ var buf bytes.Buffer
+ var ca []byte
+ if k.CA != nil {
+ ca = k.CA.Marshal()
+ }
+ buf.Write(ssh.Marshal(krlCertificateSectionHeader{CAKey: ca}))
+ headerLen := buf.Len()
+ for _, section := range k.Sections {
+ buf.Write(section.marshal())
+ }
+ // All subsections were empty; we should be empty too.
+ if buf.Len() == headerLen {
+ return nil
+ }
+ return ssh.Marshal(krlSection{
+ SectionType: 1,
+ SectionData: buf.Bytes(),
+ })
+}
+
+/*
+KRLCertificateSubsection describes a subsection of a KRL certificate selection,
+and selects certain certificates for revocation. The concrete types
+KRLCertificateSerialList, KRLCertificateSerialRange, KRLCertificateSerialBitmap,
+and KRLCertificateSerialBitmap satisfy this interface, and correspond to the
+four subsections currently defined.
+*/
+type KRLCertificateSubsection interface {
+ isRevoked(cert *ssh.Certificate) bool
+ marshal() []byte
+}
+
+// KRLCertificateSerialList revokes certificates by listing their serial
+// numbers.
+type KRLCertificateSerialList []uint64
+
+func (k *KRLCertificateSerialList) isRevoked(cert *ssh.Certificate) bool {
+ for _, serial := range *k {
+ if serial == cert.Serial {
+ return true
+ }
+ }
+ return false
+}
+
+func (k *KRLCertificateSerialList) marshal() []byte {
+ if len(*k) == 0 {
+ return nil
+ }
+
+ var buf bytes.Buffer
+ for _, serial := range *k {
+ buf.Write(ssh.Marshal(krlSerialList{
+ RevokedCertSerial: serial,
+ }))
+ }
+ return ssh.Marshal(krlCertificateSection{
+ CertSectionType: krlSectionCertSerialList,
+ CertSectionData: buf.Bytes(),
+ })
+}
+
+// KRLCertificateSerialRange revokes all certificates with serial numbers in the
+// range between Min and Max, inclusive.
+type KRLCertificateSerialRange struct {
+ Min, Max uint64
+}
+
+func (k *KRLCertificateSerialRange) isRevoked(cert *ssh.Certificate) bool {
+ return k.Min <= cert.Serial && cert.Serial <= k.Max
+}
+
+func (k *KRLCertificateSerialRange) marshal() []byte {
+ return ssh.Marshal(krlCertificateSection{
+ CertSectionType: krlSectionCertSerialRange,
+ CertSectionData: ssh.Marshal(krlSerialRange{
+ SerialMin: k.Min,
+ SerialMax: k.Max,
+ }),
+ })
+}
+
+// KRLCertificateSerialBitmap revokes certificates densely using a bitmap. If
+// bit N of the bitmap is set, the certificate with serial Offset + N is
+// revoked.
+type KRLCertificateSerialBitmap struct {
+ Offset uint64
+ Bitmap *big.Int
+}
+
+func (k *KRLCertificateSerialBitmap) isRevoked(cert *ssh.Certificate) bool {
+ if cert.Serial < k.Offset {
+ return false
+ }
+ if cert.Serial-k.Offset > krlMaxBitmapSize {
+ return false
+ }
+ return k.Bitmap.Bit(int(cert.Serial-k.Offset)) == 1
+}
+
+func (k *KRLCertificateSerialBitmap) marshal() []byte {
+ return ssh.Marshal(krlCertificateSection{
+ CertSectionType: krlSectionCertSerialBitmap,
+ CertSectionData: ssh.Marshal(krlSerialBitmap{
+ SerialOffset: k.Offset,
+ RevokedKeysBitmap: k.Bitmap,
+ }),
+ })
+}
+
+// KRLCertificateKeyID revokes certificates by listing key ids. This may be
+// useful in revoking all certificates associated with a particular identity,
+// for instance hosts or users.
+type KRLCertificateKeyID []string
+
+func (k *KRLCertificateKeyID) isRevoked(cert *ssh.Certificate) bool {
+ for _, id := range *k {
+ if id == cert.KeyId {
+ return true
+ }
+ }
+ return false
+}
+
+func (k *KRLCertificateKeyID) marshal() []byte {
+ if len(*k) == 0 {
+ return nil
+ }
+
+ var buf bytes.Buffer
+ for _, id := range *k {
+ buf.Write(ssh.Marshal(krlKeyID{
+ KeyID: id,
+ }))
+ }
+ return ssh.Marshal(krlCertificateSection{
+ CertSectionType: krlSectionCertKeyId,
+ CertSectionData: buf.Bytes(),
+ })
+}
+
+// ssh.PublicKey objects might be certificates, which have a different wire
+// format.
+func marshalPubkey(key ssh.PublicKey) []byte {
+ switch v := key.(type) {
+ case *ssh.Certificate:
+ return marshalPubkey(v.Key)
+ default:
+ return key.Marshal()
+ }
+}
+
+// KRLExplicitKeySection revokes keys by explicitly listing them.
+type KRLExplicitKeySection []ssh.PublicKey
+
+func (k *KRLExplicitKeySection) isRevoked(key ssh.PublicKey) bool {
+ kbuf := marshalPubkey(key)
+ for _, key := range *k {
+ if bytes.Equal(kbuf, marshalPubkey(key)) {
+ return true
+ }
+ }
+ return false
+}
+
+func (k *KRLExplicitKeySection) marshal() []byte {
+ if len(*k) == 0 {
+ return nil
+ }
+
+ var buf bytes.Buffer
+ for _, key := range *k {
+ buf.Write(ssh.Marshal(krlExplicitKey{
+ PublicKeyBlob: marshalPubkey(key),
+ }))
+ }
+ return ssh.Marshal(krlSection{
+ SectionType: 2,
+ SectionData: buf.Bytes(),
+ })
+}
+
+// KRLFingerprintSection revokes keys by their SHA1 fingerprints. It is
+// semantically equivalent to--but is more space efficient than--
+// KRLExplicitKeySection.
+type KRLFingerprintSection [][sha1.Size]byte
+
+func (k *KRLFingerprintSection) isRevoked(key ssh.PublicKey) bool {
+ sha := sha1.Sum(marshalPubkey(key))
+ for _, hash := range *k {
+ if hash == sha {
+ return true
+ }
+ }
+ return false
+}
+
+type bigEndian [][sha1.Size]byte
+
+func (b bigEndian) Len() int {
+ return len(b)
+}
+func (b bigEndian) Less(i, j int) bool {
+ return bytes.Compare(b[i][:], b[j][:]) == -1
+}
+func (b bigEndian) Swap(i, j int) {
+ b[i], b[j] = b[j], b[i]
+}
+
+func (k *KRLFingerprintSection) marshal() []byte {
+ if len(*k) == 0 {
+ return nil
+ }
+
+ // For some reason SSH insists that keys revoked by fingerprint must be
+ // sorted as if they were big-endian integers (i.e., lexicographically).
+ be := make(bigEndian, len(*k))
+ for i, hash := range *k {
+ be[i] = hash
+ }
+ sort.Sort(be)
+
+ var buf bytes.Buffer
+ for _, hash := range be {
+ buf.Write(ssh.Marshal(krlFingerprintSHA1{
+ PublicKeyHash: hash[:],
+ }))
+ }
+ return ssh.Marshal(krlSection{
+ SectionType: 3,
+ SectionData: buf.Bytes(),
+ })
+}
+
+// Marshal serializes the KRL and optionally signs it with one or more authority
+// keys.
+func (k *KRL) Marshal(rand io.Reader, keys ...ssh.Signer) ([]byte, error) {
+ if k.GeneratedDate == 0 {
+ k.GeneratedDate = uint64(time.Now().Unix())
+ }
+ if k.Version == 0 {
+ k.Version = k.GeneratedDate
+ }
+ k.SigningKeys = nil
+
+ var buf bytes.Buffer
+ buf.Write(ssh.Marshal(krlHeader{
+ KRLMagic: krlMagic,
+ KRLFormatVersion: 1,
+ KRLVersion: k.Version,
+ GeneratedDate: k.GeneratedDate,
+ Comment: k.Comment,
+ }))
+
+ for _, section := range k.Sections {
+ buf.Write(section.marshal())
+ }
+
+ for _, key := range keys {
+ buf.Write(ssh.Marshal(krlSignatureHeader{
+ SignatureKey: key.PublicKey().Marshal(),
+ }))
+ sig, err := key.Sign(rand, buf.Bytes())
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(ssh.Marshal(krlSignature{
+ Signature: ssh.Marshal(sig),
+ }))
+ k.SigningKeys = append(k.SigningKeys, key.PublicKey())
+ }
+
+ return buf.Bytes(), nil
+}
+
+// IsRevoked returns true if the given key has been revoked by this KRL.
+func (k *KRL) IsRevoked(key ssh.PublicKey) bool {
+ for _, section := range k.Sections {
+ if section.isRevoked(key) {
+ return true
+ }
+ }
+ return false
+}