dcql

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jul 4, 2026 License: MIT Imports: 8 Imported by: 0

README

go-dcql

DCQL (Digital Credentials Query Language) for OpenID4VP 1.0 §6/§7 in Go: model, strict parsing, validation, a pure matcher for verified credentials, and registered-scope checking against ETSI TS5 intended-use registrations.

  • Stdlib only; framework-free; no crypto — matching operates on already-verified credentials.
  • Formats: mso_mdoc and dc+sd-jwt (HAIP 1.0 profile).
  • Unmet/scope explanations carry claim paths, never claim values.

Implemented specs: OpenID4VP 1.0 (final) §6, §7, Annex B.2/B.3 meta parameters; ETSI TS5 v1.3 §2.4 credential/claim registration mirror.

Status: pre-v1. API frozen no earlier than OIDF conformance pass.

Documentation

Overview

Package dcql implements the OpenID4VP 1.0 Digital Credentials Query Language (§6) and claims path pointers (§7): model, strict parsing, validation, matching of already-verified credentials, and TS5 registered-scope checks. No crypto lives here.

Index

Constants

View Source
const (
	FormatMdoc  = "mso_mdoc"  // OID4VP Annex B.2
	FormatSDJWT = "dc+sd-jwt" // OID4VP Annex B.3
)

Credential format identifiers profiled by HAIP 1.0.

View Source
const (
	AuthorityTypeAKI              = "aki"
	AuthorityTypeETSITL           = "etsi_tl"
	AuthorityTypeOpenIDFederation = "openid_federation"
)

Trusted-authority query types (OID4VP §6.1.1).

Variables

View Source
var (
	// ErrParse wraps syntactic failures of Parse (malformed JSON, unknown
	// members, bad path elements). Services map it to err:credential:parse
	// or a 400 at the management boundary.
	ErrParse = errors.New("dcql: parse")
	// ErrInvalid wraps semantic failures of Validate; every joined entry is
	// a *ValidationError with a position.
	// Resolve also wraps structural misuse (e.g. malformed mdoc paths,
	// unsupported formats) in ErrInvalid, without positions.
	ErrInvalid = errors.New("dcql: invalid query")
)

Functions

func Resolve

func Resolve(format string, claims map[string]any, path ClaimPath) ([]any, error)

Resolve applies a claims path pointer to the claims of one credential. SD-JWT VC: full JSON traversal per OID4VP §7.1/§7.3 — a component that selects nothing resolves the whole path to no claims (empty result, nil error). mdoc: exactly [namespace, element] per §7.2 — any other shape is ErrInvalid (structural misuse, distinct from "not disclosed").

Types

type AuthorityRef

type AuthorityRef struct {
	AKIs            []string // base64url KeyIdentifier values of chain certs (aki)
	TrustedListURIs []string // ETSI TS 119 612 trusted list(s) of the anchor (etsi_tl)
	FederationIDs   []string // OpenID Federation Entity Identifiers (openid_federation)
}

AuthorityRef carries trust references of the candidate's verified issuer chain, supplied by the pipeline; compared against trusted_authorities queries (OID4VP §6.1.1, matching logic in T-05.5).

type Candidate

type Candidate struct {
	QueryCredID     string         // vp_token key: which credential query it answers (OID4VP §8.1)
	Format          string         // mso_mdoc | dc+sd-jwt
	DoctypeOrVCT    string         // mdoc doctype or SD-JWT VC vct
	Claims          map[string]any // mdoc: namespace → element → value; sd-jwt: claims object
	HolderBound     bool           // cryptographic holder binding verified (pipeline step 6)
	IssuerAuthority AuthorityRef
}

Candidate is one already-verified credential presented for a credential query (pipeline steps 3–6 done). WP-05 decision: the matcher operates post-verification only — no crypto here; the pipeline enforces that no unverified candidate enters.

type ClaimPath

type ClaimPath []PathElement

ClaimPath is an OID4VP §7 claims path pointer: a non-empty array whose elements select object keys (string), array indices (non-negative integer), or all elements of an array (null).

func NewPath

func NewPath(elems ...PathElement) ClaimPath

NewPath constructs a ClaimPath from path elements.

func (ClaimPath) Equal

func (p ClaimPath) Equal(o ClaimPath) bool

Equal reports whether two paths are equal.

func (ClaimPath) MarshalJSON

func (p ClaimPath) MarshalJSON() ([]byte, error)

MarshalJSON encodes the path as a JSON array.

func (ClaimPath) String

func (p ClaimPath) String() string

String renders the path in its JSON form — the canonical representation used in MatchResult.ByCredential, Unmet.Paths, and scope offenses.

func (*ClaimPath) UnmarshalJSON

func (p *ClaimPath) UnmarshalJSON(b []byte) error

UnmarshalJSON enforces §7 syntax: string | non-negative integer | null.

type ClaimsQuery

type ClaimsQuery struct {
	ID     string    `json:"id,omitempty"` // required when claim_sets present (§6.3)
	Path   ClaimPath `json:"path"`
	Values []any     `json:"values,omitempty"` // strings, integers, booleans (§6.3)
}

ClaimsQuery per OID4VP §6.3.

type CredentialQuery

type CredentialQuery struct {
	ID                                string             `json:"id"`
	Format                            string             `json:"format"`
	Multiple                          bool               `json:"multiple,omitempty"` // §6.1: default false
	Meta                              *Meta              `json:"meta,omitempty"`     // §6.1: REQUIRED — enforced by Validate
	TrustedAuthorities                []TrustedAuthority `json:"trusted_authorities,omitempty"`
	RequireCryptographicHolderBinding *bool              `json:"require_cryptographic_holder_binding,omitempty"` // §6.1: default true
	Claims                            []ClaimsQuery      `json:"claims,omitempty"`
	ClaimSets                         [][]string         `json:"claim_sets,omitempty"`
}

CredentialQuery per OID4VP §6.1.

func (*CredentialQuery) HolderBindingRequired

func (c *CredentialQuery) HolderBindingRequired() bool

HolderBindingRequired resolves the §6.1 default (true).

type CredentialSetQuery

type CredentialSetQuery struct {
	Options  [][]string `json:"options"`
	Required *bool      `json:"required,omitempty"` // §6.2: default true
}

CredentialSetQuery per OID4VP §6.2.

func (*CredentialSetQuery) IsRequired

func (s *CredentialSetQuery) IsRequired() bool

IsRequired resolves the §6.2 default (true).

type MatchResult

type MatchResult struct {
	Satisfied    bool
	ByCredential map[string][]string // credential query id → claim paths actually used
	Unmet        []Unmet
}

MatchResult reports the outcome. Unmet may be non-empty even when Satisfied is true: it also lists optional queries/sets that did not match.

type Meta

type Meta struct {
	VCTValues    []string `json:"vct_values,omitempty"`    // dc+sd-jwt
	DoctypeValue string   `json:"doctype_value,omitempty"` // mso_mdoc
}

Meta carries the format-specific parameters (OID4VP Annex B.2.3 / B.3.5). Exactly one side is populated, matching Format — enforced by Validate.

type PathElement

type PathElement struct {
	Key   string // set when Kind == KindKey
	Index int    // set when Kind == KindIndex
	Kind  PathKind
}

PathElement is one component of a ClaimPath.

func Index

func Index(i int) PathElement

Index constructs an index path element.

func Key

func Key(s string) PathElement

Key constructs a key path element.

func Wildcard

func Wildcard() PathElement

Wildcard constructs a wildcard path element.

type PathKind

type PathKind uint8

PathKind denotes the type of a path element.

const (
	// KindKey denotes an object key path element.
	KindKey PathKind = iota
	// KindIndex denotes an array index path element.
	KindIndex
	// KindWildcard denotes a wildcard (array all) path element.
	KindWildcard
)

type Query

type Query struct {
	Credentials    []CredentialQuery    `json:"credentials"`
	CredentialSets []CredentialSetQuery `json:"credential_sets,omitempty"`
}

Query is a DCQL query — faithful OID4VP §6 model, json tags exact.

func Parse

func Parse(raw []byte) (*Query, error)

Parse parses raw into a Query. Strict at every level: unknown members are rejected (WP-05 decision — this is the query-AUTHORING boundary; a member we don't understand could silently widen disclosure. OID4VP §6's "ignore unknown properties" targets consumers of foreign queries, i.e. wallets.) Numbers are kept as json.Number so claim values survive untouched.

func PresetMDLDrivingPrivileges

func PresetMDLDrivingPrivileges() *Query

PresetMDLDrivingPrivileges requests the driving_privileges element of an ISO/IEC 18013-5 mobile driving licence.

func PresetPIDAgeOver18

func PresetPIDAgeOver18() *Query

PresetPIDAgeOver18 requests proof of being over 18 from a PID, accepting the mdoc or the SD-JWT VC encoding via one required credential_set with two options (OID4VP §6.2).

Withdrawal caveat: the upstream PID Rulebook (eudi-doc-attestation-rulebooks- catalog, rulebooks/pid/pid-rulebook.md, current as of 2026-07-03) removed age_over_18 (and all age-verification attributes) from the PID attribute catalog in v1.1 (4 Sep 2025) "following CIR 2024/2977"; the current version (v1.6, 1 Jul 2026) and the CIR (EU) 2024/2977 Annex itself list no age attribute at all. PresetPIDAgeOver18 is kept using age_over_18 per this task's brief — no replacement identifier exists in the current PID schema to substitute — but callers should treat it as targeting a deprecated/withdrawn attribute pending a dedicated Age Verification attestation rulebook upstream. See SOURCE.md for exact citations.

func PresetPIDFull

func PresetPIDFull() *Query

PresetPIDFull requests a PID with its mandatory-to-present claims: the claims member is deliberately absent (OID4VP §6.4.1 — the wallet returns the claims mandatory to present), so no claim names are hardcoded here.

func (*Query) Match

func (q *Query) Match(cands []Candidate) MatchResult

Match decides whether the verified candidates satisfy the query (OID4VP §6.4). Precondition: q passed Validate(). Fail closed: a candidate answering an unknown credential query id (over-disclosure, pipeline step 8) makes the whole result unsatisfied.

func (*Query) Validate

func (q *Query) Validate() error

Validate checks the semantic rules of OID4VP §6/§7 and returns all violations joined; each entry is a *ValidationError with a position.

func (*Query) WithinScope

func (q *Query) WithinScope(registered []RegisteredCredential) (bool, []string)

WithinScope checks that every claim the query COULD request (union over claims, regardless of claim_sets choice) lies within the registered intended use. Drives err:client:scope-exceeded at the management boundary. Offense strings carry positions and paths — never claim values. The query must have passed Validate(); on unvalidated queries the check fails closed per-credential but individual guarantees are weaker. A claim is in scope when ANY covering registration lists it (union across registrations).

type RegisteredCredential

type RegisteredCredential struct {
	Format         string
	DoctypesOrVCTs []string    // registered doctype(s) (mdoc) or vct value(s) (sd-jwt)
	AllClaims      bool        // registration covers every claim of the credential
	Claims         []ClaimPath // registered claim paths when AllClaims is false
}

RegisteredCredential mirrors one Credential entry of the client's TS5 registered intended use (ETSI TS5 v1.3 §2.4.1/2.4.4 — WP-05 spec refs).

type TrustedAuthority

type TrustedAuthority struct {
	Type   string   `json:"type"`
	Values []string `json:"values"`
}

TrustedAuthority per OID4VP §6.1.1.

type Unmet

type Unmet struct {
	CredentialID string // credential query id; "" for set-level entries
	SetIndex     int    // index into credential_sets; -1 otherwise
	Reason       UnmetReason
	Paths        []string
}

Unmet explains one failed query or set. Paths only — never claim values (hard rule 3; safe for the verification report).

type UnmetReason

type UnmetReason string

UnmetReason categorizes why a credential query or credential set could not be satisfied.

const (
	UnmetNoCandidate   UnmetReason = "no-candidate"
	UnmetFormat        UnmetReason = "format-mismatch"
	UnmetMeta          UnmetReason = "meta-mismatch"
	UnmetClaims        UnmetReason = "claims-unsatisfied"
	UnmetHolderBinding UnmetReason = "holder-binding-missing"
	UnmetAuthority     UnmetReason = "authority-mismatch"
	UnmetMultiple      UnmetReason = "multiple-not-allowed"
	UnmetSet           UnmetReason = "credential-set-unsatisfied"
	UnmetUnknownQuery  UnmetReason = "unknown-credential-query"
)

Reasons a credential query or credential set can go unmet.

type ValidationError

type ValidationError struct {
	Pos string
	Msg string
}

ValidationError pinpoints one violation, e.g. Pos "credentials[0].claims[1].path". Never contains claim values (hard rule 3).

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) Is

func (e *ValidationError) Is(target error) bool

Is reports whether this error wraps ErrInvalid.

Jump to

Keyboard shortcuts

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