Documentation
¶
Overview ¶
Package trust is the verifier-side client for the central Trust Service (the trust-anchor service) — the ONLY path to trust anchors in the EUDI verifier: typed API client with ETag polling, a fail-closed in-memory anchor cache, and chain-resolution glue over go-eudi-crypto.
The library is framework-free: it takes an injected HTTP Doer (services wire DPoP or internal-network auth per TRUST_AUTH_MODE), an injected clock, and returns typed errors. It never logs, never touches the system certificate pool, and never parses ETSI TS 119 612 trusted-list XML — trusted-list knowledge stays server-side.
Index ¶
- Variables
- func ValidAnchorType(t AnchorType) bool
- type Anchor
- type AnchorSet
- type AnchorSource
- type AnchorType
- type CacheConfig
- type CachingSource
- type Client
- type ClientOption
- type Doer
- type HTTPClient
- type MatchVerdict
- type Matcher
- type ResolvedIssuer
- type SnapshotMeta
- type SourceState
- type SourceStatus
- type StateCallback
- type StateChange
- type TerritoryStatus
- type VerdictCache
Constants ¶
This section is empty.
Variables ¶
var ( // ErrSchema: a trust-service response violates the contract in // docs/trust-service-api.md (missing valid_until, fingerprint mismatch, // ETag/body divergence, undecodable body). Fail closed. ErrSchema = errors.New("trust: trust-service response violates contract schema") // ErrStatus: unexpected HTTP status from the trust service. ErrStatus = errors.New("trust: unexpected trust-service HTTP status") ErrUnavailable = errors.New("trust: trust service unreachable") // ErrCacheExpired: the anchor cache for the requested type is beyond // MaxAge+grace, or was never filled (CLAUDE.md rule 7: fail closed). ErrCacheExpired = errors.New("trust: anchor cache expired") // ErrUnknownAnchorType: not one of the valid AnchorType taxonomy values // (see ValidAnchorType), or not configured on this source. Unknown = // reject, never fall through. ErrUnknownAnchorType = errors.New("trust: unknown anchor type") // ErrChainParse: the presented x5chain/x5c could not be parsed. ErrChainParse = errors.New("trust: issuer chain malformed") // ErrChainUntrusted: the chain does not terminate at any anchor of the // requested type in the resolution territories. ErrChainUntrusted = errors.New("trust: issuer chain does not reach a trust anchor") )
Sentinel errors. Services map these to err:domain:reason problem codes (docs/conventions.md): ErrCacheExpired/ErrUnavailable → err:trust:anchor-unavailable, ErrChainUntrusted → err:credential:issuer-untrusted. This library never carries HTTP semantics or framework dependencies (ADR-0004). Error text never contains certificate bytes or attribute values (CLAUDE.md rule 3).
var AnchorTypes = []AnchorType{ PIDProvider, QEAAProvider, PubEAAProvider, EAAProvider, WalletProvider, AccessCA, WRPRCIssuer, PIDProviderStatus, QEAAProviderStatus, PubEAAProviderStatus, EAAProviderStatus, }
AnchorTypes lists every accepted taxonomy value.
Functions ¶
func ValidAnchorType ¶
func ValidAnchorType(t AnchorType) bool
ValidAnchorType reports whether t is an accepted taxonomy value.
Types ¶
type Anchor ¶
type Anchor struct {
Cert *x509.Certificate
Type AnchorType
Country string // TL territory code; "" or "EU" = EU-level list
Status string // TS 119 612 service-status URI, verbatim
ValidUntil time.Time // anchor cert notAfter — REQUIRED upstream (T-06.2)
TLSequence int64 // sequence of the TL the anchor came from
UseCases []string // GAP-04: EAA use cases this anchor is accredited for (from the trusted list); empty = not use-case-scoped
}
Anchor is one trust anchor as consumed by the verification pipeline (WP-06 README target interface — binding).
type AnchorSet ¶
type AnchorSet struct {
Anchors []Anchor
Snapshot string // snapshot id == strong ETag; report provenance
Stale bool // X-Trust-Stale / body stale: upstream degraded
FetchedAt time.Time // injected clock at fetch; drives cache aging
}
AnchorSet is one 200 result of /v1/anchors.json: the COMPLETE anchor set of one type. A new snapshot (changed ETag) fully replaces the previous set — anchor withdrawal arrives as snapshot replacement; there is no changes cursor (WP-06 Decisions; trust-anchor D9).
type AnchorSource ¶
type AnchorSource interface {
// AnchorsFor returns the usable anchors of one type for one country
// ("" = EU-level lists only). ErrCacheExpired when the cache is stale
// beyond grace or never filled (fail closed, CLAUDE.md rule 7).
AnchorsFor(t AnchorType, country string) ([]Anchor, error)
}
AnchorSource is what the verification pipeline consumes (WP-06 README target interface — binding): anchors served from memory, fed by trust-cache-worker via Refresh. Implemented by CachingSource.
type AnchorType ¶
type AnchorType string
AnchorType is the verifier anchor-type taxonomy — the values of the additive type= query parameter on /v1/anchors.json (trust-service extension E3, docs/trust-service-api.md §3). ARF §6.6.3.6 scopes which anchor type may authenticate which artefact; unknown type = reject.
const ( PIDProvider AnchorType = "pid_provider" // ARF §6.6.3.2: PID provider anchors QEAAProvider AnchorType = "qeaa_provider" // qualified EAA providers (qtsp-tl) PubEAAProvider AnchorType = "pub_eaa_provider" // public-body EAA providers EAAProvider AnchorType = "eaa_provider" // non-qualified EAA providers WalletProvider AnchorType = "wallet_provider" // wallet-provider trusted list AccessCA AnchorType = "access_ca" // wallet Access-CA LoTE WRPRCIssuer AnchorType = "wrprc_issuer" // registrar / WRPRC issuer keys // PIDProviderStatus and its *_status siblings are status-list / // Identifiers-list signer anchors (ADR-0010, GAP-01): the status service // may be distinct from the issuer. Resolved status-first with issuer // fallback by verifier-core statusAnchorTypesFor (WP-09). PIDProviderStatus AnchorType = "pid_provider_status" QEAAProviderStatus AnchorType = "qeaa_provider_status" PubEAAProviderStatus AnchorType = "pub_eaa_provider_status" EAAProviderStatus AnchorType = "eaa_provider_status" )
Anchor-type taxonomy values (docs/trust-service-api.md §3): the additive type= query parameter values accepted by /v1/anchors.json.
func StatusType ¶
func StatusType(t AnchorType) (AnchorType, bool)
StatusType maps an issuer provider anchor type to its status-signer counterpart (ADR-0010): PIDProvider→PIDProviderStatus, etc. ok is false for types with no status pairing (WalletProvider, AccessCA, WRPRCIssuer) and for the *_status types themselves. Keeps the provider↔status pairing authoritative in one place; consumers (verifier-core credtrust, GAP-03) derive status anchor types from issuer types instead of hardcoding the string mapping.
type CacheConfig ¶
type CacheConfig struct {
// Types to maintain. Required, non-empty, all valid taxonomy values.
Types []AnchorType
// Territory restricts upstream fetches ("" = all territories).
Territory string
// MaxAge is the freshness window measured from FetchedAt. Required > 0.
MaxAge time.Duration
// Grace extends serving per type beyond MaxAge (served stale + flagged).
// Types without an entry get zero grace: stale = expired immediately.
Grace map[AnchorType]time.Duration
// Clock is the injected time source. Defaults to time.Now().UTC.
Clock func() time.Time
// OnStateChange is invoked synchronously on every per-type state
// transition (edge-triggered; T-06.6). Optional. See StateCallback.
OnStateChange StateCallback
}
CacheConfig configures a CachingSource. The refresh LOOP lives in the consuming service (trust-cache-worker) — this library runs no goroutines or timers (ADR-0004); it only exposes the Refresh hook.
type CachingSource ¶
type CachingSource struct {
// contains filtered or unexported fields
}
CachingSource is the in-memory AnchorSource fed by Refresh. Safe for concurrent use. The ONLY path from the trust service to the pipeline (CLAUDE.md rule 6).
func NewCachingSource ¶
func NewCachingSource(client Client, cfg CacheConfig) (*CachingSource, error)
NewCachingSource validates the config and returns an empty (fail-closed) source: every type is StateUnknown until its first successful Refresh.
func (*CachingSource) AnchorsFor ¶
func (s *CachingSource) AnchorsFor(t AnchorType, country string) ([]Anchor, error)
AnchorsFor implements AnchorSource. Fail closed: StateUnknown/StateExpired ⇒ ErrCacheExpired (services map to err:trust:anchor-unavailable); StateStale serves with the flag observable via Status/StateCallback. Per-anchor validity: anchors past ValidUntil are never served (ARF §6.6.3.2: an expired trust anchor must not validate anything).
func (*CachingSource) Now ¶
func (s *CachingSource) Now() time.Time
Now exposes the injected clock. ResolveIssuerKey (T-06.4) uses it for RFC 5280 §6.1 time checks so the whole trust path shares one time source.
func (*CachingSource) Refresh ¶
func (s *CachingSource) Refresh(ctx context.Context) error
Refresh fetches every configured type once with If-None-Match revalidation — the hook the trust-cache-worker loop calls.
docs/trust-service-api.md §4 / trust-anchor D9 (ETag polling, no changes cursor): a 304 confirms the cached snapshot is still current (FetchedAt renewed); a 200 with a new ETag carries the COMPLETE new set which fully REPLACES the cached one — anchor withdrawal arrives exactly this way and the withdrawn anchor is unusable on the next AnchorsFor (WP-06 Decisions). Fetch errors keep the previous set (carry-over) and are returned joined; the entry keeps aging toward fail-closed expiry (CLAUDE.md rule 7). State transitions observed here fire the OnStateChange callback (T-06.6).
func (*CachingSource) States ¶
func (s *CachingSource) States() []SourceStatus
States reports every configured type in config order — one call for a consumer /health endpoint (WP-06 T-06.6: staleness state for /health of consumers). Observed transitions fire the callback.
func (*CachingSource) Status ¶
func (s *CachingSource) Status(t AnchorType) SourceStatus
Status reports the serve-time status of one type (health + provenance). Observing a transition here fires the callback (T-06.6).
type Client ¶
type Client interface {
// Anchors fetches GET /v1/anchors.json?type=&territory= with
// If-None-Match revalidation. ok=false on 304 (cache still fresh).
Anchors(ctx context.Context, t AnchorType, territory string, etag string) (AnchorSet, bool, error)
// Snapshot fetches GET /v1/snapshot.
Snapshot(ctx context.Context) (SnapshotMeta, error)
}
Client is the typed trust-service API client (WP-06 README target interface — binding). *HTTPClient implements it; CachingSource consumes it.
type ClientOption ¶
type ClientOption func(*HTTPClient)
ClientOption configures HTTPClient.
func WithClock ¶
func WithClock(clock func() time.Time) ClientOption
WithClock injects the time source stamped into AnchorSet.FetchedAt (docs/conventions.md: inject clocks). Defaults to time.Now().UTC.
type Doer ¶
Doer is the injected HTTP transport (ADR-0004). Services wire a DPoP-signing client, an mTLS *http.Client, or a plain internal-network client per TRUST_AUTH_MODE — this library never constructs transports or credentials (docs/trust-service-api.md §1).
type HTTPClient ¶
type HTTPClient struct {
// contains filtered or unexported fields
}
HTTPClient implements Client over an injected Doer.
func NewClient ¶
func NewClient(baseURL string, doer Doer, opts ...ClientOption) (*HTTPClient, error)
NewClient builds a trust-service client for a base URL such as "https://trust-anchor.internal".
func (*HTTPClient) Anchors ¶
func (c *HTTPClient) Anchors(ctx context.Context, t AnchorType, territory, etag string) (AnchorSet, bool, error)
Anchors implements Client. docs/trust-service-api.md §4: GET /v1/anchors.json with type= (+ optional territory=), If-None-Match revalidation (304 = freshness confirmation), X-Trust-Stale mapped onto AnchorSet.Stale. Contract strictness: schema violations (including missing valid_until and ETag/body snapshot divergence) fail closed with ErrSchema.
func (*HTTPClient) MatchCertificate ¶
func (c *HTTPClient) MatchCertificate(ctx context.Context, certDER []byte) (MatchVerdict, error)
MatchCertificate calls POST /v1/match (trust-anchor extension E4 — the recorded fixtures under testdata/trust/ are the contract until the endpoint lands upstream; WP-06 Decisions). v1 verification does NOT depend on it: chains are validated locally by ResolveIssuerKey; this passthrough exists for report enrichment where a server-side TS 119 615 §4.3/§4.4 verdict is wanted.
func (*HTTPClient) Snapshot ¶
func (c *HTTPClient) Snapshot(ctx context.Context) (SnapshotMeta, error)
Snapshot implements Client (docs/trust-service-api.md §4: /v1/snapshot feeds trust-cache-worker telemetry).
type MatchVerdict ¶
type MatchVerdict struct {
Verdict string // TS 119 615 §4.4: PASSED | FAILED | WARNING
Snapshot string
CheckedAt time.Time
Raw json.RawMessage
}
MatchVerdict is an ETSI TS 119 615 certificate-vs-trusted-list verdict from the trust service. Raw carries the full response body UNMODIFIED — it is surfaced verbatim into the verification report (T-06.5 acceptance: verdict fields surface unmodified).
type Matcher ¶
type Matcher interface {
MatchCertificate(ctx context.Context, certDER []byte) (MatchVerdict, error)
}
Matcher is the MatchCertificate capability. *HTTPClient implements it; *VerdictCache decorates any Matcher.
type ResolvedIssuer ¶
type ResolvedIssuer struct {
Subject string // leaf certificate subject (RFC 2253 string)
AnchorSubject string // matched anchor certificate subject
AnchorFingerprint string // SHA-256 hex of the matched anchor cert
AnchorType AnchorType // the type the caller demanded
AnchorCountry string // anchor's territory verbatim ("EU"/"" = EU-level)
TerritoriesTried []string // resolution order, verbatim (e.g. ["LV", ""])
UseCases []string // GAP-04: matched anchor's accredited EAA use cases (Anchor.UseCases passthrough)
}
ResolvedIssuer identifies the trust anchor an issuer chain resolved to. It is recorded verbatim in the verification-report provenance (WP-06 Decisions: territory resolution order recorded verbatim). It carries subjects, fingerprints and territory codes only — never key material or attribute values (CLAUDE.md rule 3).
func ResolveIssuerKey ¶
func ResolveIssuerKey(src AnchorSource, chain [][]byte, t AnchorType) (stdcrypto.PublicKey, ResolvedIssuer, error)
ResolveIssuerKey resolves an mdoc x5chain / SD-JWT x5c (raw DER certificates, leaf first) to a verified issuer public key against anchors of the required type (WP-06 README target interface — binding).
ARF §6.6.3.2: issuer chains terminate at the provider trust anchors from the trusted-list infrastructure. ARF §6.6.3.6: the anchor TYPE scopes what it may authenticate — the caller names the type and a chain to any other type fails (ErrChainUntrusted). Path validation is RFC 5280 §6.1 via go-eudi-crypto.VerifyChain: explicit anchors only, required At time, never the system pool (CLAUDE.md rules 4/6/7). EKU enforcement is the format profiles' concern (WP-02/WP-03), so no EKUs are passed here.
Territory resolution order (WP-06 Decisions): the issuing territory from the certificate country attribute (leaf subject C, else leaf issuer C) first, then EU-level ("") — recorded verbatim in TerritoriesTried. A source error (e.g. ErrCacheExpired) aborts immediately: a degraded cache must not silently fall through to the next territory.
type SnapshotMeta ¶
type SnapshotMeta struct {
ID string
GeneratedAt time.Time
LOTLSequence uint64
LOTLIssueTime time.Time
LOTLNextUpdate *time.Time
Territories []TerritoryStatus
PendingAnchors int
PendingBootstrap bool // staged OJ bootstrap awaiting approval → ops alert
}
SnapshotMeta is the /v1/snapshot summary: LOTL sequence, per-territory staleness, pending bootstrap presence — trust-cache-worker telemetry (docs/trust-service-api.md §4).
type SourceState ¶
type SourceState int
SourceState is the degraded-mode state of one anchor type in a CachingSource. The ladder implements the fail-closed staleness policy of docs/trust-service-api.md §4: X-Trust-Stale or cache older than MaxAge ⇒ degraded; beyond the per-type grace ⇒ fail closed.
const ( // StateUnknown means no successful fetch has happened yet — serving // fails closed. StateUnknown SourceState = iota // StateFresh means the cache is within MaxAge and not flagged stale // upstream. StateFresh // StateStale means the cache is past MaxAge but within grace, or // upstream flagged X-Trust-Stale — served WITH the flag // (Status/StateCallback). StateStale // StateExpired means the cache is past the deadline — AnchorsFor // returns ErrCacheExpired. StateExpired )
func (SourceState) String ¶
func (s SourceState) String() string
String implements fmt.Stringer (health endpoints, tests).
type SourceStatus ¶
type SourceStatus struct {
Type AnchorType
State SourceState
Snapshot string // snapshot id of the cached set ("" while Unknown)
FetchedAt time.Time // zero while Unknown
UpstreamStale bool // X-Trust-Stale / body stale on the last 200
}
SourceStatus is a serve-time status snapshot for one anchor type — consumers surface it in /health and in verification-report provenance.
type StateCallback ¶
type StateCallback func(StateChange)
StateCallback receives state transitions synchronously, in the goroutine of the Refresh/AnchorsFor/Status/States call that observed the change, WITHOUT the cache lock held (re-entrant calls into the source are safe). The library never logs (ADR-0004) — consumers turn transitions into /health state and metrics (WP-09 verifier-core, WP-12 trust-cache-worker). Callbacks must be fast and non-blocking.
type StateChange ¶
type StateChange struct {
Type AnchorType
From, To SourceState
Snapshot string // snapshot id of the cached set ("" while Unknown)
At time.Time // injected-clock time of the observation
}
StateChange is one observed per-type state transition of a CachingSource.
type TerritoryStatus ¶
type TerritoryStatus struct {
Code string
TLSequence int64
NextUpdate *time.Time
Stale bool
CarriedOver bool
AnchorCount int
}
TerritoryStatus summarizes one territory of the snapshot.
type VerdictCache ¶
type VerdictCache struct {
// contains filtered or unexported fields
}
VerdictCache caches verdicts by certificate SHA-256 for a short TTL (T-06.5). Verdicts are snapshot-scoped and cheap to refetch — consuming services should keep the TTL low (order of minutes). Errors are never cached. Safe for concurrent use.
func NewVerdictCache ¶
func NewVerdictCache(next Matcher, ttl time.Duration, clock func() time.Time) (*VerdictCache, error)
NewVerdictCache decorates next with a TTL cache. clock nil ⇒ time.Now().UTC.
func (*VerdictCache) MatchCertificate ¶
func (v *VerdictCache) MatchCertificate(ctx context.Context, certDER []byte) (MatchVerdict, error)
MatchCertificate implements Matcher with cache-aside semantics.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
wire
Package wire holds the JSON DTOs of the trust-anchor service API, mirroring docs/trust-service-api.md and the service's routes/response/response.go + trust/anchor.go.
|
Package wire holds the JSON DTOs of the trust-anchor service API, mirroring docs/trust-service-api.md and the service's routes/response/response.go + trust/anchor.go. |