diff options
Diffstat (limited to 'vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle')
4 files changed, 360 insertions, 0 deletions
diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go new file mode 100644 index 0000000..ebd3cac --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go @@ -0,0 +1,200 @@ +package jwtbundle + +import ( + "crypto" + "encoding/json" + "errors" + "io" + "os" + "sync" + + "github.com/go-jose/go-jose/v4" + "github.com/spiffe/go-spiffe/v2/internal/jwtutil" + "github.com/spiffe/go-spiffe/v2/spiffeid" + "github.com/zeebo/errs" +) + +var jwtbundleErr = errs.Class("jwtbundle") + +// Bundle is a collection of trusted JWT authorities for a trust domain. +type Bundle struct { + trustDomain spiffeid.TrustDomain + + mtx sync.RWMutex + jwtAuthorities map[string]crypto.PublicKey +} + +// New creates a new bundle. +func New(trustDomain spiffeid.TrustDomain) *Bundle { + return &Bundle{ + trustDomain: trustDomain, + jwtAuthorities: make(map[string]crypto.PublicKey), + } +} + +// FromJWTAuthorities creates a new bundle from JWT authorities +func FromJWTAuthorities(trustDomain spiffeid.TrustDomain, jwtAuthorities map[string]crypto.PublicKey) *Bundle { + return &Bundle{ + trustDomain: trustDomain, + jwtAuthorities: jwtutil.CopyJWTAuthorities(jwtAuthorities), + } +} + +// Load loads a bundle from a file on disk. The file must contain a standard RFC 7517 JWKS document. +func Load(trustDomain spiffeid.TrustDomain, path string) (*Bundle, error) { + bundleBytes, err := os.ReadFile(path) + if err != nil { + return nil, jwtbundleErr.New("unable to read JWT bundle: %w", err) + } + + return Parse(trustDomain, bundleBytes) +} + +// Read decodes a bundle from a reader. The contents must contain a standard RFC 7517 JWKS document. +func Read(trustDomain spiffeid.TrustDomain, r io.Reader) (*Bundle, error) { + b, err := io.ReadAll(r) + if err != nil { + return nil, jwtbundleErr.New("unable to read: %v", err) + } + + return Parse(trustDomain, b) +} + +// Parse parses a bundle from bytes. The data must be a standard RFC 7517 JWKS document. +func Parse(trustDomain spiffeid.TrustDomain, bundleBytes []byte) (*Bundle, error) { + jwks := new(jose.JSONWebKeySet) + if err := json.Unmarshal(bundleBytes, jwks); err != nil { + return nil, jwtbundleErr.New("unable to parse JWKS: %v", err) + } + + bundle := New(trustDomain) + for i, key := range jwks.Keys { + if err := bundle.AddJWTAuthority(key.KeyID, key.Key); err != nil { + return nil, jwtbundleErr.New("error adding authority %d of JWKS: %v", i, errors.Unwrap(err)) + } + } + + return bundle, nil +} + +// TrustDomain returns the trust domain that the bundle belongs to. +func (b *Bundle) TrustDomain() spiffeid.TrustDomain { + return b.trustDomain +} + +// JWTAuthorities returns the JWT authorities in the bundle, keyed by key ID. +func (b *Bundle) JWTAuthorities() map[string]crypto.PublicKey { + b.mtx.RLock() + defer b.mtx.RUnlock() + + return jwtutil.CopyJWTAuthorities(b.jwtAuthorities) +} + +// FindJWTAuthority finds the JWT authority with the given key ID from the bundle. If the authority +// is found, it is returned and the boolean is true. Otherwise, the returned +// value is nil and the boolean is false. +func (b *Bundle) FindJWTAuthority(keyID string) (crypto.PublicKey, bool) { + b.mtx.RLock() + defer b.mtx.RUnlock() + + if jwtAuthority, ok := b.jwtAuthorities[keyID]; ok { + return jwtAuthority, true + } + return nil, false +} + +// HasJWTAuthority returns true if the bundle has a JWT authority with the given key ID. +func (b *Bundle) HasJWTAuthority(keyID string) bool { + b.mtx.RLock() + defer b.mtx.RUnlock() + + _, ok := b.jwtAuthorities[keyID] + return ok +} + +// AddJWTAuthority adds a JWT authority to the bundle. If a JWT authority already exists +// under the given key ID, it is replaced. A key ID must be specified. +func (b *Bundle) AddJWTAuthority(keyID string, jwtAuthority crypto.PublicKey) error { + if keyID == "" { + return jwtbundleErr.New("keyID cannot be empty") + } + + b.mtx.Lock() + defer b.mtx.Unlock() + + b.jwtAuthorities[keyID] = jwtAuthority + return nil +} + +// RemoveJWTAuthority removes the JWT authority identified by the key ID from the bundle. +func (b *Bundle) RemoveJWTAuthority(keyID string) { + b.mtx.Lock() + defer b.mtx.Unlock() + + delete(b.jwtAuthorities, keyID) +} + +// SetJWTAuthorities sets the JWT authorities in the bundle. +func (b *Bundle) SetJWTAuthorities(jwtAuthorities map[string]crypto.PublicKey) { + b.mtx.Lock() + defer b.mtx.Unlock() + + b.jwtAuthorities = jwtutil.CopyJWTAuthorities(jwtAuthorities) +} + +// Empty returns true if the bundle has no JWT authorities. +func (b *Bundle) Empty() bool { + b.mtx.RLock() + defer b.mtx.RUnlock() + + return len(b.jwtAuthorities) == 0 +} + +// Marshal marshals the JWT bundle into a standard RFC 7517 JWKS document. The +// JWKS does not contain any SPIFFE-specific parameters. +func (b *Bundle) Marshal() ([]byte, error) { + b.mtx.RLock() + defer b.mtx.RUnlock() + + jwks := jose.JSONWebKeySet{} + for keyID, jwtAuthority := range b.jwtAuthorities { + jwks.Keys = append(jwks.Keys, jose.JSONWebKey{ + Key: jwtAuthority, + KeyID: keyID, + }) + } + + return json.Marshal(jwks) +} + +// Clone clones the bundle. +func (b *Bundle) Clone() *Bundle { + b.mtx.RLock() + defer b.mtx.RUnlock() + + return FromJWTAuthorities(b.trustDomain, b.jwtAuthorities) +} + +// Equal compares the bundle for equality against the given bundle. +func (b *Bundle) Equal(other *Bundle) bool { + if b == nil || other == nil { + return b == other + } + + return b.trustDomain == other.trustDomain && + jwtutil.JWTAuthoritiesEqual(b.jwtAuthorities, other.jwtAuthorities) +} + +// GetJWTBundleForTrustDomain returns the JWT bundle for the given trust +// domain. It implements the Source interface. An error will be returned if +// the trust domain does not match that of the bundle. +func (b *Bundle) GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error) { + b.mtx.RLock() + defer b.mtx.RUnlock() + + if b.trustDomain != trustDomain { + return nil, jwtbundleErr.New("no JWT bundle for trust domain %q", trustDomain) + } + + return b, nil +} diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go new file mode 100644 index 0000000..394878e --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go @@ -0,0 +1,43 @@ +// Package jwtbundle provides JWT bundle related functionality. +// +// A bundle represents a collection of JWT authorities, i.e., those that +// are used to authenticate SPIFFE JWT-SVIDs. +// +// You can create a new bundle for a specific trust domain: +// +// td := spiffeid.RequireTrustDomainFromString("example.org") +// bundle := jwtbundle.New(td) +// +// Or you can load it from disk: +// +// td := spiffeid.RequireTrustDomainFromString("example.org") +// bundle := jwtbundle.Load(td, "bundle.jwks") +// +// The bundle can be initialized with JWT authorities: +// +// td := spiffeid.RequireTrustDomainFromString("example.org") +// var jwtAuthorities map[string]crypto.PublicKey = ... +// bundle := jwtbundle.FromJWTAuthorities(td, jwtAuthorities) +// +// In addition, you can add JWT authorities to the bundle: +// +// var keyID string = ... +// var publicKey crypto.PublicKey = ... +// bundle.AddJWTAuthority(keyID, publicKey) +// +// Bundles can be organized into a set, keyed by trust domain: +// +// set := jwtbundle.NewSet() +// set.Add(bundle) +// +// A Source is source of JWT bundles for a trust domain. Both the Bundle +// and Set types implement Source: +// +// // Initialize the source from a bundle or set +// var source jwtbundle.Source = bundle +// // ... or ... +// var source jwtbundle.Source = set +// +// // Use the source to query for bundles by trust domain +// bundle, err := source.GetJWTBundleForTrustDomain(td) +package jwtbundle diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go new file mode 100644 index 0000000..048dd0d --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go @@ -0,0 +1,105 @@ +package jwtbundle + +import ( + "sort" + "sync" + + "github.com/spiffe/go-spiffe/v2/spiffeid" +) + +// Set is a set of bundles, keyed by trust domain. +type Set struct { + mtx sync.RWMutex + bundles map[spiffeid.TrustDomain]*Bundle +} + +// NewSet creates a new set initialized with the given bundles. +func NewSet(bundles ...*Bundle) *Set { + bundlesMap := make(map[spiffeid.TrustDomain]*Bundle) + + for _, b := range bundles { + if b != nil { + bundlesMap[b.trustDomain] = b + } + } + + return &Set{ + bundles: bundlesMap, + } +} + +// Add adds a new bundle into the set. If a bundle already exists for the +// trust domain, the existing bundle is replaced. +func (s *Set) Add(bundle *Bundle) { + s.mtx.Lock() + defer s.mtx.Unlock() + + if bundle != nil { + s.bundles[bundle.trustDomain] = bundle + } +} + +// Remove removes the bundle for the given trust domain. +func (s *Set) Remove(trustDomain spiffeid.TrustDomain) { + s.mtx.Lock() + defer s.mtx.Unlock() + + delete(s.bundles, trustDomain) +} + +// Has returns true if there is a bundle for the given trust domain. +func (s *Set) Has(trustDomain spiffeid.TrustDomain) bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + + _, ok := s.bundles[trustDomain] + return ok +} + +// Get returns a bundle for the given trust domain. If the bundle is in the set +// it is returned and the boolean is true. Otherwise, the returned value is +// nil and the boolean is false. +func (s *Set) Get(trustDomain spiffeid.TrustDomain) (*Bundle, bool) { + s.mtx.RLock() + defer s.mtx.RUnlock() + + bundle, ok := s.bundles[trustDomain] + return bundle, ok +} + +// Bundles returns the bundles in the set sorted by trust domain. +func (s *Set) Bundles() []*Bundle { + s.mtx.RLock() + defer s.mtx.RUnlock() + + out := make([]*Bundle, 0, len(s.bundles)) + for _, bundle := range s.bundles { + out = append(out, bundle) + } + sort.Slice(out, func(a, b int) bool { + return out[a].TrustDomain().Compare(out[b].TrustDomain()) < 0 + }) + return out +} + +// Len returns the number of bundles in the set. +func (s *Set) Len() int { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return len(s.bundles) +} + +// GetJWTBundleForTrustDomain returns the JWT bundle for the given trust +// domain. It implements the Source interface. +func (s *Set) GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error) { + s.mtx.RLock() + defer s.mtx.RUnlock() + + bundle, ok := s.bundles[trustDomain] + if !ok { + return nil, jwtbundleErr.New("no JWT bundle for trust domain %q", trustDomain) + } + + return bundle, nil +} diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go new file mode 100644 index 0000000..224cd9f --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go @@ -0,0 +1,12 @@ +package jwtbundle + +import ( + "github.com/spiffe/go-spiffe/v2/spiffeid" +) + +// Source represents a source of JWT bundles keyed by trust domain. +type Source interface { + // GetJWTBundleForTrustDomain returns the JWT bundle for the given trust + // domain. + GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error) +} |
