aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Sheridan <nsheridan@gmail.com>2016-07-31 20:41:52 +0100
committerNiall Sheridan <nsheridan@gmail.com>2016-07-31 21:18:55 +0100
commit531f63e5a9e82d86a6ee1f5d44bebee0bc51d828 (patch)
tree882b6dfb10c4db96b9e983fd6112a29d227a416a
parent44fef1c2a163bdfd781ef08a06e3cf5cf9b7d5da (diff)
Use a KRL for revoked certs
-rw-r--r--.travis.yml2
-rw-r--r--cmd/cashierd/main.go12
-rw-r--r--server/signer/signer.go18
-rw-r--r--server/signer/signer_test.go31
-rw-r--r--templates/certs.go2
-rw-r--r--vendor/github.com/stripe/krl/LICENSE20
-rw-r--r--vendor/github.com/stripe/krl/README.md7
-rw-r--r--vendor/github.com/stripe/krl/format.go163
-rw-r--r--vendor/github.com/stripe/krl/krl.go372
-rw-r--r--vendor/github.com/stripe/krl/parse.go237
-rw-r--r--vendor/vendor.json6
11 files changed, 861 insertions, 9 deletions
diff --git a/.travis.yml b/.travis.yml
index df435f0..38fa579 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,4 +26,4 @@ script:
- go list ./... |grep -v vendor/ |xargs go test
- gofmt -d $(find -type f -name '*.go' -not -path './vendor/*')
- go list ./... |grep -v vendor/ |xargs go vet
- - go list ./... |grep -v vendor/ |xargs -L1 golint
+ - go list ./... |grep -v vendor/ |xargs -L1 golint -set_exit_status
diff --git a/cmd/cashierd/main.go b/cmd/cashierd/main.go
index 2a649c2..8295c74 100644
--- a/cmd/cashierd/main.go
+++ b/cmd/cashierd/main.go
@@ -1,7 +1,6 @@
package main
import (
- "bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
@@ -211,17 +210,16 @@ func rootHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er
}
func listRevokedCertsHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, error) {
- var out bytes.Buffer
revoked, err := a.certstore.GetRevoked()
if err != nil {
return http.StatusInternalServerError, err
}
- for _, c := range revoked {
- out.WriteString(c.Raw)
- out.WriteString("\n")
+ rl, err := a.sshKeySigner.GenerateRevocationList(revoked)
+ if err != nil {
+ return http.StatusInternalServerError, err
}
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Write(out.Bytes())
+ w.Header().Set("Content-Type", "application/octet-stream")
+ w.Write(rl)
return http.StatusOK, nil
}
diff --git a/server/signer/signer.go b/server/signer/signer.go
index 8169c11..0bff1c3 100644
--- a/server/signer/signer.go
+++ b/server/signer/signer.go
@@ -13,6 +13,8 @@ import (
"github.com/nsheridan/cashier/lib"
"github.com/nsheridan/cashier/server/config"
+ "github.com/nsheridan/cashier/server/store"
+ "github.com/stripe/krl"
"golang.org/x/crypto/ssh"
)
@@ -51,6 +53,22 @@ func (s *KeySigner) SignUserKey(req *lib.SignRequest) (*ssh.Certificate, error)
return cert, nil
}
+// GenerateRevocationList returns an SSH key revocation list (KRL).
+func (s *KeySigner) GenerateRevocationList(certs []*store.CertRecord) ([]byte, error) {
+ revoked := &krl.KRLCertificateSection{
+ CA: s.ca.PublicKey(),
+ }
+ ids := krl.KRLCertificateKeyID{}
+ for _, c := range certs {
+ ids = append(ids, c.KeyID)
+ }
+ revoked.Sections = append(revoked.Sections, &ids)
+ k := &krl.KRL{
+ Sections: []krl.KRLSection{revoked},
+ }
+ return k.Marshal(rand.Reader)
+}
+
func makeperms(perms []string) map[string]string {
if len(perms) > 0 {
m := make(map[string]string)
diff --git a/server/signer/signer_test.go b/server/signer/signer_test.go
index a80e64a..9c76f4b 100644
--- a/server/signer/signer_test.go
+++ b/server/signer/signer_test.go
@@ -7,7 +7,9 @@ import (
"time"
"github.com/nsheridan/cashier/lib"
+ "github.com/nsheridan/cashier/server/store"
"github.com/nsheridan/cashier/testdata"
+ "github.com/stripe/krl"
"golang.org/x/crypto/ssh"
)
@@ -49,3 +51,32 @@ func TestCert(t *testing.T) {
t.Fatalf("Invalid validity, expected %d, got %d", r.ValidUntil, cert.ValidBefore)
}
}
+
+func TestRevocationList(t *testing.T) {
+ r := &lib.SignRequest{
+ Key: string(testdata.Pub),
+ Principal: "revoked",
+ ValidUntil: time.Now().Add(1 * time.Hour),
+ }
+ cert1, _ := signer.SignUserKey(r)
+ r.Principal = "ok"
+ cert2, _ := signer.SignUserKey(r)
+ var rec []*store.CertRecord
+ rec = append(rec, &store.CertRecord{
+ KeyID: cert1.KeyId,
+ })
+ rl, err := signer.GenerateRevocationList(rec)
+ if err != nil {
+ t.Error(err)
+ }
+ k, err := krl.ParseKRL(rl)
+ if err != nil {
+ t.Error(err)
+ }
+ if !k.IsRevoked(cert1) {
+ t.Errorf("expected cert %s to be revoked", cert1.KeyId)
+ }
+ if k.IsRevoked(cert2) {
+ t.Errorf("cert %s should not be revoked", cert2.KeyId)
+ }
+}
diff --git a/templates/certs.go b/templates/certs.go
index 2fde4fd..877a0b3 100644
--- a/templates/certs.go
+++ b/templates/certs.go
@@ -1,6 +1,6 @@
package templates
-// Token is the page users see when authenticated.
+// Certs lists all unexpired issued certificates.
const Certs = `<html>
<head>
<title>Certs</title>
diff --git a/vendor/github.com/stripe/krl/LICENSE b/vendor/github.com/stripe/krl/LICENSE
new file mode 100644
index 0000000..f3241a2
--- /dev/null
+++ b/vendor/github.com/stripe/krl/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015, 2016 Stripe, Inc.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/stripe/krl/README.md b/vendor/github.com/stripe/krl/README.md
new file mode 100644
index 0000000..6402e63
--- /dev/null
+++ b/vendor/github.com/stripe/krl/README.md
@@ -0,0 +1,7 @@
+krl
+===
+
+[![GoDoc](https://godoc.org/github.com/stripe/krl?status.svg)](https://godoc.org/github.com/stripe/krl)
+
+Package krl provides functionality for reading and writing SSH Key Revocation
+Lists (KRLs).
diff --git a/vendor/github.com/stripe/krl/format.go b/vendor/github.com/stripe/krl/format.go
new file mode 100644
index 0000000..4a4b822
--- /dev/null
+++ b/vendor/github.com/stripe/krl/format.go
@@ -0,0 +1,163 @@
+package krl
+
+import "math/big"
+
+// We (unfortunately) make extensive use of x/crypto/ssh.Unmarshal's "rest"
+// parameter here. The KRL specification makes extensive use of sections placed
+// back-to-back, and there's no other way to get x/crypto/ssh.Unmarshal to emit
+// the portion of the input that has not yet been parsed.
+
+const krlMagic = 0x5353484b524c0a00
+
+/*
+#define KRL_MAGIC 0x5353484b524c0a00ULL /* "SSHKRL\n\0" * /
+#define KRL_FORMAT_VERSION 1
+
+ uint64 KRL_MAGIC
+ uint32 KRL_FORMAT_VERSION
+ uint64 krl_version
+ uint64 generated_date
+ uint64 flags
+ string reserved
+ string comment
+*/
+type krlHeader struct {
+ KRLMagic uint64
+ KRLFormatVersion uint32
+ KRLVersion uint64
+ GeneratedDate uint64
+ Flags uint64
+ Reserved []byte
+ Comment string
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ byte section_type
+ string section_data
+
+#define KRL_SECTION_CERTIFICATES 1
+#define KRL_SECTION_EXPLICIT_KEY 2
+#define KRL_SECTION_FINGERPRINT_SHA1 3
+#define KRL_SECTION_SIGNATURE 4
+*/
+type krlSection struct {
+ SectionType byte
+ SectionData []byte
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ string ca_key
+ string reserved
+*/
+type krlCertificateSectionHeader struct {
+ CAKey []byte
+ Reserved []byte
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ byte cert_section_type
+ string cert_section_data
+
+#define KRL_SECTION_CERT_SERIAL_LIST 0x20
+#define KRL_SECTION_CERT_SERIAL_RANGE 0x21
+#define KRL_SECTION_CERT_SERIAL_BITMAP 0x22
+#define KRL_SECTION_CERT_KEY_ID 0x23
+*/
+type krlCertificateSection struct {
+ CertSectionType byte
+ CertSectionData []byte
+
+ Rest []byte `ssh:"rest"`
+}
+
+const (
+ krlSectionCertSerialList = 0x20
+ krlSectionCertSerialRange = 0x21
+ krlSectionCertSerialBitmap = 0x22
+ krlSectionCertKeyId = 0x23
+)
+
+/*
+ uint64 revoked_cert_serial
+ uint64 ...
+*/
+type krlSerialList struct {
+ RevokedCertSerial uint64
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ uint64 serial_min
+ uint64 serial_max
+*/
+type krlSerialRange struct {
+ SerialMin uint64
+ SerialMax uint64
+}
+
+/*
+ uint64 serial_offset
+ mpint revoked_keys_bitmap
+*/
+type krlSerialBitmap struct {
+ SerialOffset uint64
+ RevokedKeysBitmap *big.Int
+}
+
+/*
+ string key_id[0]
+ ...
+*/
+type krlKeyID struct {
+ KeyID string
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ string public_key_blob[0]
+ ....
+*/
+type krlExplicitKey struct {
+ PublicKeyBlob []byte
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ string public_key_hash[0]
+ ....
+*/
+type krlFingerprintSHA1 struct {
+ PublicKeyHash []byte
+
+ Rest []byte `ssh:"rest"`
+}
+
+/*
+ byte KRL_SECTION_SIGNATURE
+ string signature_key
+ string signature
+
+We split this struct into two parts: krlSignatureHeader is included in the
+signature, and so the inverse of its "Rest" key is the data coverd by the
+signature.
+*/
+type krlSignatureHeader struct {
+ SignatureKey []byte `sshtype:"4"`
+
+ Rest []byte `ssh:"rest"`
+}
+
+type krlSignature struct {
+ Signature []byte
+
+ Rest []byte `ssh:"rest"`
+}
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
+}
diff --git a/vendor/github.com/stripe/krl/parse.go b/vendor/github.com/stripe/krl/parse.go
new file mode 100644
index 0000000..75f2551
--- /dev/null
+++ b/vendor/github.com/stripe/krl/parse.go
@@ -0,0 +1,237 @@
+package krl
+
+import (
+ "crypto/sha1"
+ "fmt"
+
+ "golang.org/x/crypto/ssh"
+)
+
+// Hundreds of millions, or 32MB of bitmap
+const krlMaxBitmapSize = 0x10000000
+
+// KRLSigningErrors is a slice of error messages which correspond one-to-one
+// with KRL.SigningKeys.
+type KRLSigningErrors []error
+
+func (k KRLSigningErrors) Error() string {
+ return fmt.Sprintf("krl: bad signatures: %v", []error(k))
+}
+
+func (k KRLSigningErrors) err() error {
+ for _, err := range k {
+ if err != nil {
+ return k
+ }
+ }
+ return nil
+}
+
+// ParseKRL parses a KRL. If the KRL was signed by one or more authorities,
+// those signatures will be checked, and any verification errors will be
+// returned.
+func ParseKRL(in []byte) (*KRL, error) {
+ orig := in
+
+ var header krlHeader
+ if err := ssh.Unmarshal(in, &header); err != nil {
+ return nil, fmt.Errorf("krl: while parsing header: %v", err)
+ }
+ if header.KRLMagic != krlMagic {
+ return nil, fmt.Errorf("krl: bad magic value %x", header.KRLMagic)
+ }
+ if header.KRLFormatVersion != 1 {
+ return nil, fmt.Errorf("krl: bad format version %v", header.KRLFormatVersion)
+ }
+ krl := &KRL{
+ Version: header.KRLVersion,
+ GeneratedDate: header.GeneratedDate,
+ Comment: header.Comment,
+ }
+ in = header.Rest
+
+ for len(in) > 0 && in[0] != 4 { // // KRL_SECTION_SIGNATURE
+ var sdata krlSection
+ if err := ssh.Unmarshal(in, &sdata); err != nil {
+ return nil, fmt.Errorf("krl: malformed section: %v", err)
+ }
+ in = sdata.Rest
+
+ var err error
+ var section KRLSection
+ switch sdata.SectionType {
+ case 1: // KRL_SECTION_CERTIFICATES
+ section, err = parseCertificateSection(sdata.SectionData)
+ case 2: // KRL_SECTION_EXPLICIT_KEY
+ section, err = parseExplicitKeySection(sdata.SectionData)
+ case 3: // KRL_SECTION_FINGERPRINT_SHA1
+ section, err = parseFingerprintSection(sdata.SectionData)
+ default:
+ return nil, fmt.Errorf("krl: unexpected section type %d", sdata.SectionType)
+ }
+ if err != nil {
+ return nil, err
+ }
+ krl.Sections = append(krl.Sections, section)
+ }
+
+ var signingErrors KRLSigningErrors
+ for len(in) > 0 {
+ var sigHeader krlSignatureHeader
+ if err := ssh.Unmarshal(in, &sigHeader); err != nil {
+ return nil, fmt.Errorf("krl: malfored signature header: %v", err)
+ }
+ in = sigHeader.Rest
+
+ key, err := ssh.ParsePublicKey(sigHeader.SignatureKey)
+ if err != nil {
+ return nil, fmt.Errorf("krl: malformed signing key: %v", err)
+ }
+
+ var sig krlSignature
+ if err := ssh.Unmarshal(in, &sig); err != nil {
+ return nil, fmt.Errorf("krl: malfored signature wrapper: %v", err)
+ }
+ in = sig.Rest
+
+ sshsig := new(ssh.Signature)
+ if err := ssh.Unmarshal(sig.Signature, sshsig); err != nil {
+ return nil, fmt.Errorf("krl: malformed signature: %v", err)
+ }
+
+ // The entire KRL up until immediately after the signature
+ // header is signed.
+ data := orig[:len(orig)-len(sigHeader.Rest)]
+
+ krl.SigningKeys = append(krl.SigningKeys, key)
+ signingErrors = append(signingErrors, key.Verify(data, sshsig))
+ }
+
+ return krl, signingErrors.err()
+}
+
+func parseCertificateSection(in []byte) (*KRLCertificateSection, error) {
+ var header krlCertificateSectionHeader
+ if err := ssh.Unmarshal(in, &header); err != nil {
+ return nil, fmt.Errorf("krl: while parsing certificate section header: %v", err)
+ }
+ ca, err := ssh.ParsePublicKey(header.CAKey)
+ if err != nil {
+ return nil, fmt.Errorf("krl: while parsing CA key: %v", err)
+ }
+ k := &KRLCertificateSection{CA: ca}
+ in = header.Rest
+ for len(in) > 0 {
+ var section krlCertificateSection
+ if err := ssh.Unmarshal(in, &section); err != nil {
+ return nil, fmt.Errorf("krl: malformed certificate section: %v", err)
+ }
+ in = section.Rest
+ var err error
+ var subsection KRLCertificateSubsection
+ switch section.CertSectionType {
+ case krlSectionCertSerialList:
+ subsection, err = parseCertSerialList(section.CertSectionData)
+ case krlSectionCertSerialRange:
+ subsection, err = parseCertSerialRange(section.CertSectionData)
+ case krlSectionCertSerialBitmap:
+ subsection, err = parseCertSerialBitmap(section.CertSectionData)
+ case krlSectionCertKeyId:
+ subsection, err = parseCertKeyID(section.CertSectionData)
+ default:
+ return nil, fmt.Errorf("krl: unexpected cert section type %x", in[0])
+ }
+ if err != nil {
+ return nil, err
+ }
+ k.Sections = append(k.Sections, subsection)
+ }
+
+ return k, nil
+}
+
+func parseCertSerialList(in []byte) (*KRLCertificateSerialList, error) {
+ s := &KRLCertificateSerialList{}
+ for len(in) > 0 {
+ var list krlSerialList
+ if err := ssh.Unmarshal(in, &list); err != nil {
+ return nil, fmt.Errorf("krl: while parsing serial in list: %v", err)
+ }
+ in = list.Rest
+ *s = append(*s, list.RevokedCertSerial)
+ }
+ return s, nil
+}
+
+func parseCertSerialRange(in []byte) (*KRLCertificateSerialRange, error) {
+ var s krlSerialRange
+ if err := ssh.Unmarshal(in, &s); err != nil {
+ return nil, fmt.Errorf("krl: while parsing serial range: %v", err)
+ }
+ return &KRLCertificateSerialRange{
+ Min: s.SerialMin,
+ Max: s.SerialMax,
+ }, nil
+}
+
+func parseCertSerialBitmap(in []byte) (*KRLCertificateSerialBitmap, error) {
+ var s krlSerialBitmap
+ if err := ssh.Unmarshal(in, &s); err != nil {
+ return nil, fmt.Errorf("krl: while parsing serial bitmap: %v", err)
+ }
+ if bl := s.RevokedKeysBitmap.BitLen(); bl > krlMaxBitmapSize {
+ return nil, fmt.Errorf("krl: serial bitmap too wide: %v", bl)
+ }
+ return &KRLCertificateSerialBitmap{
+ Offset: s.SerialOffset,
+ Bitmap: s.RevokedKeysBitmap,
+ }, nil
+}
+
+func parseCertKeyID(in []byte) (*KRLCertificateKeyID, error) {
+ s := &KRLCertificateKeyID{}
+ for len(in) > 0 {
+ var list krlKeyID
+ if err := ssh.Unmarshal(in, &list); err != nil {
+ return nil, fmt.Errorf("krl: while parsing key id in list: %v", err)
+ }
+ in = list.Rest
+ *s = append(*s, list.KeyID)
+ }
+ return s, nil
+}
+
+func parseExplicitKeySection(in []byte) (*KRLExplicitKeySection, error) {
+ s := &KRLExplicitKeySection{}
+ for len(in) > 0 {
+ var list krlExplicitKey
+ if err := ssh.Unmarshal(in, &list); err != nil {
+ return nil, fmt.Errorf("krl: while parsing explicit key in list: %v", err)
+ }
+ in = list.Rest
+ key, err := ssh.ParsePublicKey(list.PublicKeyBlob)
+ if err != nil {
+ return nil, fmt.Errorf("krl: while parsing explicit key: %v", err)
+ }
+ *s = append(*s, key)
+ }
+ return s, nil
+}
+
+func parseFingerprintSection(in []byte) (*KRLFingerprintSection, error) {
+ s := &KRLFingerprintSection{}
+ for len(in) > 0 {
+ var list krlFingerprintSHA1
+ if err := ssh.Unmarshal(in, &list); err != nil {
+ return nil, fmt.Errorf("krl: while parsing fingerprint in list: %v", err)
+ }
+ in = list.Rest
+ if len(list.PublicKeyHash) != sha1.Size {
+ return nil, fmt.Errorf("krl: key fingerprint wrong length for SHA1: %x", list.PublicKeyHash)
+ }
+ var sha [sha1.Size]byte
+ copy(sha[:], list.PublicKeyHash)
+ *s = append(*s, sha)
+ }
+ return s, nil
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 4e31773..a687a53 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -377,6 +377,12 @@
"revisionTime": "2016-06-15T09:26:46Z"
},
{
+ "checksumSHA1": "Mqb+Cn1S2ZN/nOfn8FDaAiXTdhU=",
+ "path": "github.com/stripe/krl",
+ "revision": "fd565ec6f8e236d6b63b953f3aecb2f2cca80605",
+ "revisionTime": "2016-07-21T22:16:07Z"
+ },
+ {
"checksumSHA1": "BS9oue0y6JjMzz3spKlMTVmxZxo=",
"path": "go4.org/wkfs",
"revision": "85455cb60c902182109ca27131042a41bc4cb85d",