From 531f63e5a9e82d86a6ee1f5d44bebee0bc51d828 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Sun, 31 Jul 2016 20:41:52 +0100 Subject: Use a KRL for revoked certs --- .travis.yml | 2 +- cmd/cashierd/main.go | 12 +- server/signer/signer.go | 18 ++ server/signer/signer_test.go | 31 +++ templates/certs.go | 2 +- vendor/github.com/stripe/krl/LICENSE | 20 ++ vendor/github.com/stripe/krl/README.md | 7 + vendor/github.com/stripe/krl/format.go | 163 +++++++++++++++ vendor/github.com/stripe/krl/krl.go | 372 +++++++++++++++++++++++++++++++++ vendor/github.com/stripe/krl/parse.go | 237 +++++++++++++++++++++ vendor/vendor.json | 6 + 11 files changed, 861 insertions(+), 9 deletions(-) create mode 100644 vendor/github.com/stripe/krl/LICENSE create mode 100644 vendor/github.com/stripe/krl/README.md create mode 100644 vendor/github.com/stripe/krl/format.go create mode 100644 vendor/github.com/stripe/krl/krl.go create mode 100644 vendor/github.com/stripe/krl/parse.go 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 = ` Certs 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, §ion); 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 @@ -376,6 +376,12 @@ "revision": "d77da356e56a7428ad25149ca77381849a6a5232", "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", -- cgit v1.2.3