trust

package
v0.5.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2026 License: BSD-2-Clause Imports: 21 Imported by: 0

Documentation

Overview

Package trust — JWKSKeyResolver implements SD-JWT VC spec §5.3 key resolution with a multi-endpoint discovery chain for maximum interoperability.

Package trust provides a unified trust evaluation interface for all credential formats.

This package bridges the gap between different credential formats' trust models:

  • W3C VC 2.0 Data Integrity: uses DIDs for key identification
  • SD-JWT VC: uses x5c certificate chains with issuer URLs
  • ISO mDOC: uses IACA certificate chains with document signer certificates

The key distinction is between:

  • Name-to-key RESOLUTION: Given a name (DID), fetch the associated public key
  • Name-to-key VALIDATION: Given a name and key, verify the binding is trusted

When DIDs are used, go-trust can perform resolution. When certificates are used (SD-JWT x5c, mDOC MSO), the key is already present, so only validation is needed.

This package uses types from github.com/sirosfoundation/go-trust/pkg/trustapi and adds vc-specific logic like GetEffectiveAction() for policy routing.

Index

Examples

Constants

View Source
const (
	// DefaultTrustCacheTTL is the default TTL for trust decisions.
	// Trust decisions are relatively stable, so a longer TTL is appropriate.
	DefaultTrustCacheTTL = 5 * time.Minute

	// MaxTrustCacheTTL is the maximum TTL for trust decisions.
	MaxTrustCacheTTL = 1 * time.Hour
)
View Source
const (
	// DefaultJWKSCacheTTL is the default TTL for cached JWKS entries.
	DefaultJWKSCacheTTL = 5 * time.Minute

	// DefaultJWKSMaxCapacity is the default max capacity for the JWKS cache.
	DefaultJWKSMaxCapacity = 100
)
View Source
const (
	KeyTypeJWK = trustapi.KeyTypeJWK
	KeyTypeX5C = trustapi.KeyTypeX5C

	RoleIssuer             = trustapi.RoleIssuer
	RoleVerifier           = trustapi.RoleVerifier
	RoleWalletProvider     = trustapi.RoleWalletProvider
	RolePIDProvider        = trustapi.RolePIDProvider
	RoleCredentialIssuer   = trustapi.RoleCredentialIssuer
	RoleCredentialVerifier = trustapi.RoleCredentialVerifier
	RoleAny                = trustapi.RoleAny
)

Constants re-exported from trustapi for convenience.

View Source
const (
	// ErrMsgNilRequest is returned when an evaluation request is nil.
	ErrMsgNilRequest = "evaluation request is nil"
)

Error messages for trust evaluation. These are package-level constants to ensure consistency across evaluator implementations.

View Source
const (
	// TrustFrameworkNone indicates no trust framework validation occurred.
	// Used by AllowAllEvaluator when operating without a PDP.
	TrustFrameworkNone = "none"
)

TrustFramework constants for common trust framework identifiers.

Variables

This section is empty.

Functions

This section is empty.

Types

type AllowAllEvaluator

type AllowAllEvaluator struct{}

AllowAllEvaluator is a TrustEvaluator that always returns trusted=true. This is used when no PDP URL is configured ("allow all" mode).

In "allow all" mode:

  • All trust evaluation requests return Trusted=true
  • The TrustFramework is set to "none" to indicate no trust validation occurred
  • Key resolution is not supported (returns an error)

This provides a consistent behavior across applications when trust evaluation is not required or during development/testing scenarios.

func NewAllowAllEvaluator

func NewAllowAllEvaluator() *AllowAllEvaluator

NewAllowAllEvaluator creates a trust evaluator that accepts all requests. Use this when operating in "allow all" mode without a PDP.

Example
package main

import (
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	evaluator := trust.NewAllowAllEvaluator()
	fmt.Printf("%T\n", evaluator)
}
Output:
*trust.AllowAllEvaluator

func (*AllowAllEvaluator) Evaluate

Evaluate implements TrustEvaluator. Always returns trusted=true for supported key types.

Example
package main

import (
	"context"
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	ctx := context.Background()
	evaluator := trust.NewAllowAllEvaluator()

	req := trust.NewEvaluationRequest("https://issuer.example.com", trust.KeyTypeJWK, nil)
	decision, err := evaluator.Evaluate(ctx, req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println("trusted:", decision.Trusted)
	fmt.Println("reason:", decision.Reason)
	fmt.Println("framework:", decision.TrustFramework)
}
Output:
trusted: true
reason: allow all mode: no PDP configured
framework: none

func (*AllowAllEvaluator) ResolveKey

func (e *AllowAllEvaluator) ResolveKey(ctx context.Context, verificationMethod string) (crypto.PublicKey, error)

ResolveKey attempts to resolve a key but is not supported in allow-all mode. Key resolution requires an actual PDP service to resolve DIDs and verify trust.

func (*AllowAllEvaluator) SupportsKeyType

func (e *AllowAllEvaluator) SupportsKeyType(kt KeyType) bool

SupportsKeyType implements TrustEvaluator. Supports JWK and X5C key types.

Example
package main

import (
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	evaluator := trust.NewAllowAllEvaluator()

	fmt.Println("supports JWK:", evaluator.SupportsKeyType(trust.KeyTypeJWK))
	fmt.Println("supports X5C:", evaluator.SupportsKeyType(trust.KeyTypeX5C))
}
Output:
supports JWK: true
supports X5C: true

type CachedDecision

type CachedDecision struct {
	Decision  *TrustDecision
	CachedAt  time.Time
	ExpiresAt time.Time
}

CachedDecision wraps a TrustDecision with cache metadata.

type CachingTrustEvaluator

type CachingTrustEvaluator struct {
	// contains filtered or unexported fields
}

CachingTrustEvaluator wraps a TrustEvaluator with caching.

func NewCachingTrustEvaluator

func NewCachingTrustEvaluator(evaluator TrustEvaluator, config TrustCacheConfig) *CachingTrustEvaluator

NewCachingTrustEvaluator creates a caching wrapper around a TrustEvaluator.

func (*CachingTrustEvaluator) Cache

func (c *CachingTrustEvaluator) Cache() *TrustCache

Cache returns the underlying cache for advanced operations.

func (*CachingTrustEvaluator) Clear

func (c *CachingTrustEvaluator) Clear()

Clear removes all entries from the cache.

func (*CachingTrustEvaluator) Evaluate

Evaluate checks the cache first, then delegates to the wrapped evaluator.

func (*CachingTrustEvaluator) Invalidate

func (c *CachingTrustEvaluator) Invalidate(req *EvaluationRequest)

Invalidate removes an entry from the cache.

func (*CachingTrustEvaluator) Stop

func (c *CachingTrustEvaluator) Stop()

Stop stops the cache's automatic expiration.

func (*CachingTrustEvaluator) SupportsKeyType

func (c *CachingTrustEvaluator) SupportsKeyType(kt KeyType) bool

SupportsKeyType delegates to the wrapped evaluator.

type CombinedTrustService

type CombinedTrustService interface {
	TrustEvaluator
	KeyResolver
}

CombinedTrustService combines evaluation and resolution capabilities. This is the full interface for trust management across all credential formats.

type CompositeEvaluator

type CompositeEvaluator struct {
	// contains filtered or unexported fields
}

CompositeEvaluator combines multiple TrustEvaluators with configurable strategy.

func NewCompositeEvaluator

func NewCompositeEvaluator(strategy CompositeStrategy, evaluators ...TrustEvaluator) *CompositeEvaluator

NewCompositeEvaluator creates a composite evaluator with the given strategy.

Example
package main

import (
	"context"
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	ctx := context.Background()

	// Create a composite evaluator with two AllowAll evaluators using FirstSuccess strategy
	eval1 := trust.NewAllowAllEvaluator()
	eval2 := trust.NewAllowAllEvaluator()
	composite := trust.NewCompositeEvaluator(trust.StrategyFirstSuccess, eval1, eval2)

	req := trust.NewEvaluationRequest("https://issuer.example.com", trust.KeyTypeJWK, nil)
	decision, err := composite.Evaluate(ctx, req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println("trusted:", decision.Trusted)
	fmt.Println("supports JWK:", composite.SupportsKeyType(trust.KeyTypeJWK))
}
Output:
trusted: true
supports JWK: true

func (*CompositeEvaluator) AddEvaluator

func (c *CompositeEvaluator) AddEvaluator(eval TrustEvaluator)

AddEvaluator adds an evaluator to the composite.

func (*CompositeEvaluator) Evaluate

Evaluate implements TrustEvaluator.

func (*CompositeEvaluator) SupportsKeyType

func (c *CompositeEvaluator) SupportsKeyType(kt KeyType) bool

SupportsKeyType returns true if any evaluator supports the given key type.

type CompositeStrategy

type CompositeStrategy int

CompositeStrategy determines how multiple evaluators are combined.

const (
	// StrategyFirstSuccess returns success on the first positive decision.
	// This is useful for "any trust source" scenarios.
	StrategyFirstSuccess CompositeStrategy = iota

	// StrategyAllMustSucceed requires all evaluators to return a positive decision.
	// This is useful for "all trust sources must agree" scenarios.
	StrategyAllMustSucceed

	// StrategyFallback tries evaluators in order, returning the first non-error result.
	// This is useful for "try local first, then remote" scenarios.
	StrategyFallback
)

type EvaluationRequest

type EvaluationRequest struct {
	trustapi.EvaluationRequest
}

EvaluationRequest contains the parameters for a trust evaluation. This embeds trustapi.EvaluationRequest and adds vc-specific methods.

func NewEvaluationRequest

func NewEvaluationRequest(subjectID string, keyType KeyType, key any) *EvaluationRequest

NewEvaluationRequest creates a new EvaluationRequest with the given parameters. This is a convenience constructor.

Example
package main

import (
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	req := trust.NewEvaluationRequest(
		"https://issuer.example.com",
		trust.KeyTypeJWK,
		nil,
	)

	fmt.Println("subject:", req.SubjectID)
	fmt.Println("key type:", req.KeyType)
}
Output:
subject: https://issuer.example.com
key type: jwk

func (*EvaluationRequest) GetEffectiveAction

func (r *EvaluationRequest) GetEffectiveAction() string

GetEffectiveAction returns the action name to use for policy routing. Priority: 1. Explicit Action field, 2. Composed from Role + CredentialType/DocType, 3. Role alone

This is vc-specific logic that maps credential types to go-trust policy names.

Example
package main

import (
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	// With explicit role set
	req := trust.NewEvaluationRequest("https://issuer.example.com", trust.KeyTypeJWK, nil)
	req.Role = trust.RoleIssuer
	req.CredentialType = "PID"

	fmt.Println("PID issuer action:", req.GetEffectiveAction())

	// With a verifier role
	req2 := trust.NewEvaluationRequest("https://verifier.example.com", trust.KeyTypeJWK, nil)
	req2.Role = trust.RoleVerifier

	fmt.Println("verifier action:", req2.GetEffectiveAction())
}
Output:
PID issuer action: pid-provider
verifier action: credential-verifier

type GoTrustEvaluator

type GoTrustEvaluator struct {
	// contains filtered or unexported fields
}

GoTrustEvaluator implements TrustEvaluator using go-trust AuthZEN client. It can validate both JWK and x5c key types against a Policy Decision Point.

func NewGoTrustEvaluator

func NewGoTrustEvaluator(pdpURL string) *GoTrustEvaluator

NewGoTrustEvaluator creates a trust evaluator using go-trust with a known PDP URL.

func NewGoTrustEvaluatorWithClient

func NewGoTrustEvaluatorWithClient(client *authzenclient.Client) *GoTrustEvaluator

NewGoTrustEvaluatorWithClient creates a trust evaluator with a pre-configured client.

func NewGoTrustEvaluatorWithDiscovery

func NewGoTrustEvaluatorWithDiscovery(ctx context.Context, baseURL string) (*GoTrustEvaluator, error)

NewGoTrustEvaluatorWithDiscovery creates a trust evaluator using AuthZEN discovery.

func (*GoTrustEvaluator) Evaluate

Evaluate implements TrustEvaluator.

func (*GoTrustEvaluator) GetClient

func (e *GoTrustEvaluator) GetClient() *authzenclient.Client

GetClient returns the underlying AuthZEN client for advanced usage.

func (*GoTrustEvaluator) ResolveKey

func (e *GoTrustEvaluator) ResolveKey(ctx context.Context, verificationMethod string) (crypto.PublicKey, error)

ResolveKey implements KeyResolver for DID-based resolution.

func (*GoTrustEvaluator) SupportsKeyType

func (e *GoTrustEvaluator) SupportsKeyType(kt KeyType) bool

SupportsKeyType implements TrustEvaluator.

type JWKSKeyResolver

type JWKSKeyResolver struct {
	// contains filtered or unexported fields
}

JWKSKeyResolver resolves issuer public keys using a multi-endpoint discovery chain per SD-JWT VC §5.3 with fallbacks for maximum interoperability:

  1. .well-known/jwt-vc-issuer → inline jwks or jwks_uri (SD-JWT VC §5.3 primary)
  2. .well-known/openid-credential-issuer → authorization_servers → AS metadata → jwks_uri
  3. .well-known/openid-configuration → jwks_uri (OIDC Discovery)
  4. .well-known/oauth-authorization-server → jwks_uri (RFC 8414)

Resolved JWKS are cached per issuer URL. Keys are matched by kid.

func NewJWKSKeyResolver

func NewJWKSKeyResolver(config JWKSResolverConfig) *JWKSKeyResolver

NewJWKSKeyResolver creates a new resolver for SD-JWT VC issuer key resolution. The parseJWK function must be provided to convert JWK maps to crypto.PublicKey (this avoids coupling pkg/trust to pkg/jose).

func (*JWKSKeyResolver) InvalidateIssuer

func (r *JWKSKeyResolver) InvalidateIssuer(issuerURL string)

InvalidateIssuer removes a cached JWKS for a specific issuer. Useful when key rotation is detected (e.g., kid not found in cached JWKS).

func (*JWKSKeyResolver) Len

func (r *JWKSKeyResolver) Len() int

Len returns the number of issuers currently cached.

func (*JWKSKeyResolver) ResolveKeyByKID

func (r *JWKSKeyResolver) ResolveKeyByKID(ctx context.Context, issuerURL, kid string) (crypto.PublicKey, map[string]any, error)

ResolveKeyByKID resolves the public key for the given issuer and kid. Returns the public key and the JWK map (for trust evaluation).

Per SD-JWT VC §5.3, the metadata is fetched from {issuer}/.well-known/jwt-vc-issuer. Resolved JWKS are cached per issuer URL.

func (*JWKSKeyResolver) Stop

func (r *JWKSKeyResolver) Stop()

Stop stops the cache's automatic expiration goroutine.

type JWKSResolverConfig

type JWKSResolverConfig struct {
	// HTTPClient is the HTTP client used for fetching metadata and JWKS.
	// If nil, a default client with 30s timeout is used.
	HTTPClient *http.Client

	// CacheTTL is the time-to-live for cached JWKS entries per issuer.
	// Default: 5 minutes.
	CacheTTL time.Duration

	// MaxCapacity is the maximum number of issuers to cache.
	// Default: 100.
	MaxCapacity uint64

	// ParseJWKToPublicKey converts a JWK map to a crypto.PublicKey.
	// If nil, a default implementation using lestrrat-go/jwx is expected
	// to be injected by the caller (avoids coupling pkg/trust to pkg/jose).
	ParseJWKToPublicKey func(jwkData any) (crypto.PublicKey, error)
}

JWKSResolverConfig contains configuration for the JWKSKeyResolver.

type KeyResolver

type KeyResolver interface {
	// ResolveKey retrieves the public key for the given verification method.
	ResolveKey(ctx context.Context, verificationMethod string) (crypto.PublicKey, error)
}

KeyResolver resolves public keys from identifiers. This is used when the key needs to be fetched (DID-based credentials).

type KeyType

type KeyType = trustapi.KeyType

KeyType indicates the format of the public key being validated.

type LocalTrustConfig deprecated

type LocalTrustConfig struct {
	// TrustedRoots are the trusted root certificates.
	TrustedRoots []*x509.Certificate

	// AllowedRoles limits which roles are accepted. Nil means all roles.
	AllowedRoles []string

	// Clock is used for time-based validation. If nil, time.Now() is used.
	Clock func() time.Time
}

LocalTrustConfig configures a LocalTrustEvaluator.

Deprecated: See LocalTrustEvaluator deprecation notice.

type LocalTrustEvaluator deprecated

type LocalTrustEvaluator struct {
	// contains filtered or unexported fields
}

LocalTrustEvaluator implements TrustEvaluator using local trust anchors.

Deprecated: LocalTrustEvaluator performs trust evaluation locally without consulting an AuthZEN PDP. For harmonized trust management across applications, use NewTrustEvaluatorFromConfig instead, which returns AllowAllEvaluator when no PDP URL is configured (returning trusted=true for all requests).

LocalTrustEvaluator is retained for backward compatibility and testing purposes.

func NewLocalTrustEvaluator

func NewLocalTrustEvaluator(config LocalTrustConfig) *LocalTrustEvaluator

NewLocalTrustEvaluator creates a new local trust evaluator.

func (*LocalTrustEvaluator) AddTrustedRoot

func (e *LocalTrustEvaluator) AddTrustedRoot(cert *x509.Certificate)

AddTrustedRoot adds a trusted root certificate.

func (*LocalTrustEvaluator) Evaluate

Evaluate implements TrustEvaluator.

func (*LocalTrustEvaluator) GetTrustedRoots

func (e *LocalTrustEvaluator) GetTrustedRoots() []*x509.Certificate

GetTrustedRoots returns all trusted root certificates.

func (*LocalTrustEvaluator) SupportsKeyType

func (e *LocalTrustEvaluator) SupportsKeyType(kt KeyType) bool

SupportsKeyType implements TrustEvaluator.

type Role

type Role = trustapi.Role

Role represents the expected role of the key holder.

type TrustCache

type TrustCache struct {
	// contains filtered or unexported fields
}

TrustCache caches trust evaluation results to avoid repeated remote calls. It uses a composite key based on SubjectID, KeyType, Role, and key fingerprint.

func NewTrustCache

func NewTrustCache(config TrustCacheConfig) *TrustCache

NewTrustCache creates and starts a new trust decision cache.

func (*TrustCache) Clear

func (c *TrustCache) Clear()

Clear removes all entries from the cache.

func (*TrustCache) Get

Get retrieves a cached trust decision for the given request. Returns nil if not found or expired.

func (*TrustCache) Invalidate

func (c *TrustCache) Invalidate(req *EvaluationRequest)

Invalidate removes a specific entry from the cache.

func (*TrustCache) InvalidateSubject

func (c *TrustCache) InvalidateSubject(subjectID string)

InvalidateSubject removes all entries for a subject from the cache. This is useful when a subject's trust status is known to have changed.

func (*TrustCache) Len

func (c *TrustCache) Len() int

Len returns the number of items currently in the cache.

func (*TrustCache) Set

func (c *TrustCache) Set(req *EvaluationRequest, decision *TrustDecision)

Set stores a trust decision in the cache.

func (*TrustCache) SetWithTTL

func (c *TrustCache) SetWithTTL(req *EvaluationRequest, decision *TrustDecision, ttl time.Duration)

SetWithTTL stores a trust decision with a custom TTL.

func (*TrustCache) Stop

func (c *TrustCache) Stop()

Stop stops the cache's automatic expiration goroutine.

type TrustCacheConfig

type TrustCacheConfig struct {
	// TTL is the time-to-live for cached decisions. Default: 5 minutes.
	TTL time.Duration

	// MaxCapacity is the maximum number of items in the cache.
	// If 0, no capacity limit is applied (items only expire by TTL).
	MaxCapacity uint64
}

TrustCacheConfig contains configuration for the trust cache.

type TrustDecision

type TrustDecision = trustapi.TrustDecision

TrustDecision represents the result of a trust evaluation.

type TrustEvaluator

type TrustEvaluator interface {
	// Evaluate checks if the given key is trusted for the specified subject and role.
	// This is used when the key is already known (SD-JWT x5c, mDOC certificates).
	Evaluate(ctx context.Context, req *EvaluationRequest) (*TrustDecision, error)

	// SupportsKeyType returns true if this evaluator can handle the given key type.
	SupportsKeyType(kt KeyType) bool
}

TrustEvaluator evaluates whether a name-to-key binding is trusted. This extends trustapi.TrustEvaluator to work with our EvaluationRequest.

func NewTrustEvaluatorFromConfig

func NewTrustEvaluatorFromConfig(pdpURL string) TrustEvaluator

NewTrustEvaluatorFromConfig creates the appropriate TrustEvaluator based on configuration. If pdpURL is empty, returns an AllowAllEvaluator (trusted=true for all requests). If pdpURL is set, returns a GoTrustEvaluator that queries the PDP for trust decisions.

This provides harmonized trust management across applications:

  • Empty PDP URL: "allow all" mode - resolved keys are always considered trusted
  • Non-empty PDP URL: "default deny" mode - trust decisions require PDP approval
Example
package main

import (
	"fmt"

	"github.com/SUNET/vc/pkg/trust"
)

func main() {
	// Empty PDP URL returns an AllowAll evaluator
	evaluator := trust.NewTrustEvaluatorFromConfig("")
	fmt.Printf("%T\n", evaluator)
}
Output:
*trust.AllowAllEvaluator

type TrustOptions

type TrustOptions = trustapi.TrustOptions

TrustOptions contains additional options for trust evaluation.

type X5CCertChain

type X5CCertChain = trustapi.X5CCertChain

X5CCertChain is a helper type for x5c certificate chains.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL