Documentation
¶
Overview ¶
Example (CombinedPresentation_differentHolderKeys) ¶
Example_combinedPresentation_differentHolderKeys shows how enforcement catches a presentation where credentials are bound to different keys, indicating they may belong to different users.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
verifier := &openid4vp.CombinedBindingVerifier{
Config: openid4vp.CombinedPresentationConfig{
Enabled: true,
Enforcement: openid4vp.BindingEnforcementEnforce,
KeyBindingEnabled: true,
},
}
// Two different holder keys → different WSCDs → suspicious
key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
thumb1, _ := openid4vp.ECPublicKeyToJWKThumbprint(&key1.PublicKey)
thumb2, _ := openid4vp.ECPublicKeyToJWKThumbprint(&key2.PublicKey)
credentials := []openid4vp.VerifiedCredentialBinding{
{Scope: "pid", HolderKeyThumbprint: thumb1, Claims: map[string]any{"sub": "user-A"}},
{Scope: "ehic", HolderKeyThumbprint: thumb2, Claims: map[string]any{"sub": "user-B"}},
}
result, _ := verifier.Verify(credentials)
fmt.Printf("Bound: %v\n", result.Bound)
fmt.Printf("Confidence: %s\n", result.HighestConfidence)
for _, r := range result.Results {
if r.Error != "" {
fmt.Printf(" [%s] ERROR: %s\n", r.Method, r.Error)
}
}
}
Output: Bound: false Confidence: low [key_based] ERROR: holder key mismatch: credentials are bound to different keys
Example (CombinedPresentation_universityAdmission) ¶
Example_combinedPresentation_universityAdmission demonstrates a university admissions use case where the Verifier requests a PID (identity) and a diploma attestation, then verifies they belong to the same applicant.
ARF 3.0 Discussion Topic K §3.1 lists this as a core combined presentation scenario: a university needs citizenship (PID) plus academic credentials (diploma) and must confirm both originate from the same person.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"log"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
// ── Verifier setup ──────────────────────────────────────────────────
// The verifier is configured to:
// 1. Compare holder keys (high confidence — same WSCD)
// 2. Compare family_name + birth_date across credentials (medium confidence)
// 3. Enforce binding — reject if not established
verifier := &openid4vp.CombinedBindingVerifier{
Config: openid4vp.CombinedPresentationConfig{
Enabled: true,
Enforcement: openid4vp.BindingEnforcementEnforce,
KeyBindingEnabled: true,
BindingAttributes: []openid4vp.BindingAttributeConfig{
{Paths: []string{"family_name", "birth_date"}}, // compound: both must match
},
},
}
// ── Simulate wallet response ────────────────────────────────────────
// In production, these are extracted during VerificationDirectPost
// after SD-JWT/mDoc verification. Here we simulate two credentials
// that share the same holder key (same wallet/WSCD).
holderKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
}
thumbprint, err := openid4vp.ECPublicKeyToJWKThumbprint(&holderKey.PublicKey)
if err != nil {
log.Fatal(err)
}
credentials := []openid4vp.VerifiedCredentialBinding{
{
Scope: "pid",
HolderKeyThumbprint: thumbprint,
Claims: map[string]any{
"given_name": "Anna",
"family_name": "Lindqvist",
"birth_date": "1998-03-15",
"nationality": "SE",
"sub": "urn:eudi:pid:se:199803150123",
},
},
{
Scope: "diploma",
HolderKeyThumbprint: thumbprint, // same key → same WSCD → high confidence
Claims: map[string]any{
"given_name": "Anna",
"family_name": "Lindqvist",
"birth_date": "1998-03-15",
"degree": "MSc Computer Science",
"issuing_authority": "Uppsala University",
},
},
}
// ── Verify binding ──────────────────────────────────────────────────
result, err := verifier.Verify(credentials)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Bound: %v\n", result.Bound)
fmt.Printf("Confidence: %s\n", result.HighestConfidence)
for _, r := range result.Results {
fmt.Printf(" [%s] confidence=%s\n", r.Method, r.Confidence)
}
}
Output: Bound: true Confidence: high [session_based] confidence=low [key_based] confidence=high [attribute_based] confidence=medium
Example (CombinedPresentation_warnMode) ¶
Example_combinedPresentation_warnMode shows how "warn" enforcement allows the presentation through even when key binding fails, relying on session-based trust. This follows ARF 3.0 ACP_08: "SHOULD NOT refuse solely because proof is absent."
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
verifier := &openid4vp.CombinedBindingVerifier{
Config: openid4vp.CombinedPresentationConfig{
Enabled: true,
Enforcement: openid4vp.BindingEnforcementWarn,
KeyBindingEnabled: true,
BindingAttributes: []openid4vp.BindingAttributeConfig{
{Paths: []string{"sub"}},
},
},
}
// Credentials lack key material but share "sub" identifier
credentials := []openid4vp.VerifiedCredentialBinding{
{Scope: "pid", Claims: map[string]any{"sub": "urn:eudi:pid:se:199803150123"}},
{Scope: "ehic", Claims: map[string]any{"sub": "urn:eudi:pid:se:199803150123"}},
}
result, _ := verifier.Verify(credentials)
fmt.Printf("Bound: %v\n", result.Bound)
fmt.Printf("Confidence: %s\n", result.HighestConfidence)
}
Output: Bound: true Confidence: medium
Index ¶
- Constants
- Variables
- func ArrayElementPath(parts ...string) []*string
- func BuildDirectPostURL(baseURL string, response *ResponseParameters) (string, error)
- func ComputeJWKThumbprint(jwkMap map[string]any) (string, error)
- func ECPublicKeyFromJWKMap(jwkMap map[string]any) (*ecdsa.PublicKey, error)
- func ECPublicKeyToJWKThumbprint(key *ecdsa.PublicKey) (string, error)
- func ExtractHolderKeyThumbprint(claims map[string]any) (string, error)
- func FilterStandardScopes(scopes []string) []string
- func GenerateQRV2(ctx context.Context, data string) (string, error)
- func GetClaimMappings(template PresentationRequestTemplate) map[string]string
- func HashTransactionData(txData []string, hashAlg string) ([]string, error)
- func IsMdocFormat(format string) bool
- func IsMdocZkFormat(format string) bool
- func IsSDJWTFormatIdentifier(format string) bool
- func IsW3CVCFormatIdentifier(format string) bool
- func MatchCryptosuite(cryptosuite string, cryptosuiteValues []string) bool
- func MatchProofType(proofType string, proofTypeValues []string) bool
- func MatchTrustedAuthorities(trustedAuthorities []TrustedAuthority, credentialCertChain [][]byte, ...) bool
- func MatchTypeValues(credentialTypes []string, typeValues [][]string) bool
- func PublicKeyToJWKThumbprint(key crypto.PublicKey) (string, error)
- func ResolveDCQLPath(data any, path []*string) any
- func StringPath(parts ...string) []*string
- func ValidateClaims(claims map[string]any, validations []ClaimValidation) error
- func ValidateCredentialQuery(query CredentialQuery) error
- func ValidateVPToken(vpToken, nonce, clientID string) error
- type BindingAttributeConfig
- type BindingConfidence
- type BindingEnforcement
- type BindingMethod
- type BindingResult
- type ClaimQuery
- type ClaimTransformDef
- type ClaimValidation
- type ClaimsExtractor
- func (ce *ClaimsExtractor) ApplyClaimTransforms(claims map[string]any, transformDefs map[string]ClaimTransformDef) (map[string]any, error)
- func (ce *ClaimsExtractor) ExtractAndMapClaims(ctx context.Context, vpToken string, claimMappings map[string]string, ...) (map[string]any, error)
- func (ce *ClaimsExtractor) ExtractClaimsFromVPToken(ctx context.Context, vpToken string) (map[string]any, error)
- func (ce *ClaimsExtractor) MapClaimsToOIDC(vpClaims map[string]any, claimMappings map[string]string) (map[string]any, error)
- type Client
- type ClientMetadata
- type CombinedBindingResult
- type CombinedBindingVerifier
- type CombinedPresentationConfig
- type Config
- type Constraints
- type CredentialQuery
- type CredentialSetQuery
- type DCQL
- type DCQLValidationError
- type Descriptor
- type DirectPostJWTResponse
- type DirectPostResponse
- type EphemeralEncryptionKeyCache
- func (e *EphemeralEncryptionKeyCache) Delete(kid string)
- func (e *EphemeralEncryptionKeyCache) GenerateAndStore(kid string) (privateKey jwk.Key, publicKey jwk.Key, err error)
- func (e *EphemeralEncryptionKeyCache) Get(kid string) (jwk.Key, bool)
- func (e *EphemeralEncryptionKeyCache) Len() int
- func (e *EphemeralEncryptionKeyCache) Set(kid string, key jwk.Key)
- func (e *EphemeralEncryptionKeyCache) SetWithTTL(kid string, key jwk.Key, ttl time.Duration)
- func (e *EphemeralEncryptionKeyCache) Stop()
- type ErrorResponse
- type Field
- type Filter
- type Format
- type InputDescriptor
- type JWK
- type JWTVCFormat
- type Keys
- type LDPVCFormat
- type MetaQuery
- type MsoMdocFormat
- type PresentationBuilder
- func (pb *PresentationBuilder) BuildDCQLQuery(ctx context.Context, scopes []string) (*DCQL, error)
- func (pb *PresentationBuilder) BuildFromScopes(ctx context.Context, scopes []string) (*DCQL, PresentationRequestTemplate, error)
- func (pb *PresentationBuilder) BuildFromTemplate(ctx context.Context, templateID string) (*DCQL, PresentationRequestTemplate, error)
- func (pb *PresentationBuilder) FindTemplateByScopes(scopes []string) PresentationRequestTemplate
- func (pb *PresentationBuilder) GetTemplate(templateID string) (PresentationRequestTemplate, error)
- func (pb *PresentationBuilder) ListTemplates() []PresentationRequestTemplate
- type PresentationDefinitionParameter
- type PresentationRequestTemplate
- type PresentationSubmission
- type QRReply
- type RequestObject
- type RequestObjectCache
- func (r *RequestObjectCache) Delete(requestURI string)
- func (r *RequestObjectCache) Get(requestURI string) (*RequestObject, bool)
- func (r *RequestObjectCache) Len() int
- func (r *RequestObjectCache) Set(requestURI string, requestObject *RequestObject)
- func (r *RequestObjectCache) SetWithTTL(requestURI string, requestObject *RequestObject, ttl time.Duration)
- func (r *RequestObjectCache) Stop()
- type ResponseParameters
- type SDJWTVCFormat
- type StaticVC20KeyResolver
- type SubmissionRequirement
- type TransactionData
- type TrustService
- type TrustedAuthority
- type TrustedAuthorityMatcher
- type VC20CreateRequest
- type VC20CreateResult
- type VC20Handler
- type VC20HandlerOption
- func WithVC20AllowedSkew(skew time.Duration) VC20HandlerOption
- func WithVC20Clock(clock func() time.Time) VC20HandlerOption
- func WithVC20KeyResolver(resolver VC20KeyResolver) VC20HandlerOption
- func WithVC20RevocationCheck(check bool) VC20HandlerOption
- func WithVC20SignerConfig(config *VC20SignerConfig) VC20HandlerOption
- func WithVC20StaticKey(key crypto.PublicKey) VC20HandlerOption
- func WithVC20TrustedIssuers(issuers []string) VC20HandlerOption
- type VC20KeyResolver
- type VC20SignerConfig
- type VC20VerificationResult
- type VPBuildOptions
- type VPBuilder
- type VPBuilderOption
- type VPFormatsSupported
- type VPResponse
- type VPTokenValidator
- type VerificationFailedError
- type VerificationRejectedError
- type VerifiedCredentialBinding
- type VerifierInfo
- type ZKSystemTypeSpec
Examples ¶
- Package (CombinedPresentation_differentHolderKeys)
- Package (CombinedPresentation_universityAdmission)
- Package (CombinedPresentation_warnMode)
- EphemeralEncryptionKeyCache.GenerateAndStore
- ErrorResponse.Error
- ErrorResponse.IsAuthorizationError
- InputDescriptor
- New
- New (Usage)
- New (WithCustomConfig)
- NewEphemeralEncryptionKeyCache
- NewErrorResponse
- NewRequestObjectCache
- PresentationSubmission
- RequestObjectCache.SetWithTTL
- VerificationFailedError
- VerificationRejectedError
Constants ¶
const ( // FormatJwtVCJson is the format identifier for JWT-based W3C VC without JSON-LD. FormatJwtVCJson = "jwt_vc_json" // FormatSDJWTVC is the format identifier for IETF SD-JWT VC. FormatSDJWTVC = "dc+sd-jwt" // FormatMsoMdoc is the format identifier for ISO mdoc. FormatMsoMdoc = "mso_mdoc" // FormatLdpVCDCQL is the format identifier for W3C VC Data Integrity (used in DCQL). // Note: This duplicates FormatLdpVC from vc20_handler.go but is needed for non-vc20 builds. FormatLdpVCDCQL = "ldp_vc" )
Credential format identifiers as defined in OpenID4VP Appendix B. Note: FormatLdpVC is defined in vc20_handler.go (vc20 build tag)
const ( // TrustedAuthorityTypeAKI is for Authority Key Identifier matching. // Value is base64url-encoded KeyIdentifier from AuthorityKeyIdentifier. TrustedAuthorityTypeAKI = "aki" // TrustedAuthorityTypeETSI is for ETSI Trusted List matching. // Value is the URL of the Trusted List (e.g., https://lotl.example.com). TrustedAuthorityTypeETSI = "etsi_tl" // TrustedAuthorityTypeOpenIDFederation is for OpenID Federation matching. // Value is the Entity Identifier of the Trust Anchor. TrustedAuthorityTypeOpenIDFederation = "openid_federation" )
TrustedAuthorityType constants per OpenID4VP spec Section 6.1.1
const ( // OAuth 2.0 standard errors ErrorInvalidScope = "invalid_scope" // Requested scope value is invalid, unknown, or malformed ErrorInvalidRequest = "invalid_request" // Request contains invalid parameters or violates requirements ErrorInvalidClient = "invalid_client" // Client authentication failed ErrorAccessDenied = "access_denied" // User denied consent or wallet lacks credentials // OpenID4VP specific errors ErrorVPFormatsNotSupported = "vp_formats_not_supported" // Wallet doesn't support requested VP formats ErrorInvalidRequestURIMethod = "invalid_request_uri_method" // Invalid request_uri_method value ErrorInvalidTransactionData = "invalid_transaction_data" // Transaction data is invalid or malformed )
Error codes defined in OpenID4VP spec Section 8.5
const ( FormatLdpVC = "ldp_vc" // VC Data Model 1.1 with Data Integrity FormatVC20JSON = "vc+ld+json" // VC Data Model 2.0 with Data Integrity )
VC20Format identifiers per OpenID4VC spec Appendix A
const ( CryptosuiteECDSA2019 = "ecdsa-rdfc-2019" CryptosuiteECDSASd = "ecdsa-sd-2023" CryptosuiteEdDSA2022 = "eddsa-rdfc-2022" )
Supported cryptosuites
const ( // DefaultEphemeralKeyTTL is the default TTL for ephemeral encryption keys DefaultEphemeralKeyTTL = 10 * time.Minute )
const ( // DefaultRequestObjectTTL is the default TTL for request objects DefaultRequestObjectTTL = 10 * time.Minute )
const FormatMsoMdocZk = "mso_mdoc_zk"
FormatMsoMdocZk is the DCQL/OpenID4VP format identifier for a zero-knowledge-proof presentation of an ISO mdoc credential (multipaz's "mso_mdoc_zk", as opposed to the plain "mso_mdoc" format). A wallet presenting this format returns a DeviceResponse-shaped `vp_token` whose top-level CBOR map has a `zkDocuments` array instead of (or alongside) the standard `documents` array — see pkg/mdoc.ZkDocumentMdoc.
Variables ¶
var ErrInvalidVerifierHost = errors.New("verifier host is invalid or empty")
var StandardOIDCScopes = map[string]bool{ "openid": true, "profile": true, "email": true, "address": true, "phone": true, "offline_access": true, }
StandardOIDCScopes contains scopes defined by OpenID Connect Core. These are protocol-level scopes that are optional for credential matching. The "openid" scope is REQUIRED by the OIDC specification and will always be present, but it does not need to be mapped to a credential.
Functions ¶
func ArrayElementPath ¶ added in v0.6.2
ArrayElementPath creates a []*string path ending with nil (null) for array element access. Example: ArrayElementPath("nationalities") → ["nationalities", null]
func BuildDirectPostURL ¶
func BuildDirectPostURL(baseURL string, response *ResponseParameters) (string, error)
BuildDirectPostURL creates a URL-encoded form data string for direct_post
func ComputeJWKThumbprint ¶ added in v0.7.0
ComputeJWKThumbprint computes an RFC 7638 thumbprint of a JWK map. Only includes the required members for the key type (kty, crv, x, y for EC; kty, e, n for RSA) in lexicographic order.
func ECPublicKeyFromJWKMap ¶ added in v0.7.0
ECPublicKeyFromJWKMap extracts an *ecdsa.PublicKey from a JWK map.
func ECPublicKeyToJWKThumbprint ¶ added in v0.7.0
ECPublicKeyToJWKThumbprint converts an *ecdsa.PublicKey to its RFC 7638 thumbprint. Used for mDoc device keys.
func ExtractHolderKeyThumbprint ¶ added in v0.7.0
ExtractHolderKeyThumbprint extracts the RFC 7638 SHA-256 thumbprint from a cnf.jwk claim map (as found in SD-JWT credentials).
func FilterStandardScopes ¶
FilterStandardScopes removes standard OIDC scopes from the list. This is a utility function that can be used when you specifically need to identify scopes that are not standard OIDC scopes. Note that credential matching logic does NOT use this - all scopes (including standard ones) are considered for matching to allow optional credential mappings.
func GetClaimMappings ¶
func GetClaimMappings(template PresentationRequestTemplate) map[string]string
GetClaimMappings is a helper to extract claim mappings from a template Returns nil if the template doesn't implement this method
func HashTransactionData ¶ added in v0.7.0
HashTransactionData computes base64url-encoded hashes of raw transaction data strings per OpenID4VP §8.4.
func IsMdocFormat ¶
IsMdocFormat returns true if the format is ISO mdoc format.
func IsMdocZkFormat ¶ added in v0.7.1
IsMdocZkFormat returns true if format is the ZK-mdoc DCQL format identifier ("mso_mdoc_zk").
func IsSDJWTFormatIdentifier ¶
IsSDJWTFormatIdentifier returns true if the format identifier is SD-JWT VC format. Note: This is different from sdjwtvc.IsSDJWTFormat which checks the actual token format.
func IsW3CVCFormatIdentifier ¶
IsW3CVCFormatIdentifier returns true if the format identifier is a W3C Verifiable Credential format (ldp_vc or jwt_vc_json).
func MatchCryptosuite ¶
MatchCryptosuite checks if a cryptosuite is supported by the format configuration. Returns true if cryptosuiteValues is empty (no constraint) or contains the cryptosuite.
func MatchProofType ¶
MatchProofType checks if a proof type is supported by the format configuration. Returns true if proofTypeValues is empty (no constraint) or contains the proofType.
func MatchTrustedAuthorities ¶
func MatchTrustedAuthorities( trustedAuthorities []TrustedAuthority, credentialCertChain [][]byte, issuer string, matcher TrustedAuthorityMatcher, ) bool
MatchTrustedAuthorities checks if a credential matches any of the trusted authorities constraints. Returns true if trustedAuthorities is empty (no constraint) or the credential matches at least one. The matcher parameter provides the actual trust verification implementation. If matcher is nil, returns true (no validation performed).
func MatchTypeValues ¶
MatchTypeValues checks if a credential's types match any of the type_values alternatives. credentialTypes should be the fully expanded type IRIs from the credential. typeValues is the query's type_values constraint (array of string arrays). Returns true if the credential matches at least one of the alternatives.
func PublicKeyToJWKThumbprint ¶ added in v0.7.0
PublicKeyToJWKThumbprint converts a crypto.PublicKey to its RFC 7638 thumbprint. Supports *ecdsa.PublicKey. Returns empty string for unsupported key types.
func ResolveDCQLPath ¶ added in v0.6.2
ResolveDCQLPath resolves a DCQL claim path against a credential value. Per DCQL Section 7.1:
- string segments navigate object keys
- nil means "all array elements" (returns []any of resolved values)
- numeric strings are array indices
func StringPath ¶ added in v0.6.2
StringPath creates a []*string path from string arguments. This is a convenience for constructing ClaimQuery paths.
func ValidateClaims ¶ added in v0.6.0
func ValidateClaims(claims map[string]any, validations []ClaimValidation) error
ValidateClaims applies validation rules against a set of extracted claims. claims is a map of claim names to their values (from selective disclosures or JWT payload).
func ValidateCredentialQuery ¶
func ValidateCredentialQuery(query CredentialQuery) error
ValidateCredentialQuery validates that a CredentialQuery has the required fields for the specified format.
func ValidateVPToken ¶
ValidateVPToken is a convenience function for basic VP Token validation
Types ¶
type BindingAttributeConfig ¶ added in v0.7.0
type BindingAttributeConfig struct {
// Paths lists claim paths that must ALL match across credentials (AND semantics).
Paths []string `` /* 142-byte string literal not displayed */
}
BindingAttributeConfig defines a set of attribute paths to compare across credentials.
type BindingConfidence ¶ added in v0.7.0
type BindingConfidence string
BindingConfidence represents the level of assurance from a binding check.
const ( BindingConfidenceHigh BindingConfidence = "high" BindingConfidenceMedium BindingConfidence = "medium" BindingConfidenceLow BindingConfidence = "low" BindingConfidenceNone BindingConfidence = "none" )
type BindingEnforcement ¶ added in v0.7.0
type BindingEnforcement string
BindingEnforcement configures how binding verification results are handled.
const ( // BindingEnforcementEnforce rejects the presentation if binding cannot be established. BindingEnforcementEnforce BindingEnforcement = "enforce" // BindingEnforcementWarn logs a warning but allows the presentation through. BindingEnforcementWarn BindingEnforcement = "warn" // BindingEnforcementDisabled skips binding verification entirely. BindingEnforcementDisabled BindingEnforcement = "disabled" )
type BindingMethod ¶ added in v0.7.0
type BindingMethod string
BindingMethod represents a method used to verify combined presentation binding.
const ( // BindingMethodSession indicates binding via session context (all credentials // arrived in the same VP response). This is the baseline assumption per ARF 3.0 §3.2. BindingMethodSession BindingMethod = "session_based" // BindingMethodKey indicates binding via holder key comparison (same cnf.jwk // or device key across multiple credentials = same WSCD). BindingMethodKey BindingMethod = "key_based" // BindingMethodAttribute indicates binding via shared identifier attributes // (e.g., sub, pid_number, or compound name+DOB matching). BindingMethodAttribute BindingMethod = "attribute_based" )
type BindingResult ¶ added in v0.7.0
type BindingResult struct {
Method BindingMethod `json:"method"`
Confidence BindingConfidence `json:"confidence"`
Details string `json:"details,omitempty"`
Error string `json:"error,omitempty"`
}
BindingResult holds the outcome of a single binding method check.
type ClaimQuery ¶
type ClaimQuery struct {
// ID REQUIRED if claim_sets is present in the Credential Query; OPTIONAL otherwise.
// A string identifying the particular claim. The value MUST be a non-empty string
// consisting of alphanumeric, underscore (_), or hyphen (-) characters.
// Within the particular claims array, the same id MUST NOT be present more than once.
ID string `json:"-" yaml:"-"`
// Path REQUIRED The value MUST be a non-empty array representing a claims path pointer
// that specifies the path to a claim within the Credential, as defined in Section 7.
// Elements are strings (object keys) or nil (representing null for array element access).
Path []*string `json:"-" yaml:"-" validate:"required,min=1"`
// Values OPTIONAL A non-empty array of strings, integers or boolean values that
// specifies the expected values of the claim. If present, the Wallet SHOULD return
// the claim only if the type and value of the claim both match exactly for at least
// one of the elements in the array. See OpenID4VP Section 6.3 and 6.4.1.
Values []any `json:"-" yaml:"-" validate:"omitnil,min=1"`
}
func (ClaimQuery) MarshalJSON ¶ added in v0.6.2
func (cq ClaimQuery) MarshalJSON() ([]byte, error)
MarshalJSON implements custom JSON marshaling for ClaimQuery. Nil path elements are serialized as JSON null (for DCQL array element access). A non-nil Values slice is validated against OpenID4VP §6.3; use nil to omit.
func (ClaimQuery) MarshalYAML ¶ added in v0.7.1
func (cq ClaimQuery) MarshalYAML() (any, error)
MarshalYAML implements gopkg.in/yaml.v2's Marshaler interface for ClaimQuery. Mirrors MarshalJSON: the ClaimQuery fields carry yaml:"-" tags to route through this method, so without it yaml.Marshal would emit an empty mapping and drop id/path/values.
func (*ClaimQuery) UnmarshalJSON ¶ added in v0.6.2
func (cq *ClaimQuery) UnmarshalJSON(data []byte) error
UnmarshalJSON implements custom JSON unmarshaling for ClaimQuery. JSON null path elements are deserialized as nil pointers; non-string/non-null elements cause an unmarshal error (Go's json package rejects them for *string).
func (*ClaimQuery) UnmarshalYAML ¶ added in v0.6.2
func (cq *ClaimQuery) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML implements custom YAML unmarshaling for ClaimQuery. YAML null path elements are deserialized as nil pointers; non-string/non-null elements cause an unmarshal error.
type ClaimTransformDef ¶
type ClaimTransformDef struct {
Type string // Transform type: date_format, boolean_string, uppercase, lowercase, etc.
Params map[string]string // Transform parameters
}
ClaimTransformDef defines a claim transformation
type ClaimValidation ¶ added in v0.6.0
type ClaimValidation struct {
// Rule is the validation rule to apply, e.g., "age_over".
Rule string `json:"rule" yaml:"rule" validate:"required,oneof=age_over" doc_example:"\"age_over\""`
// Path is the claim path to validate, e.g., ["birthdate"].
Path []string `json:"path" yaml:"path" validate:"required,min=1,dive,required" doc_example:"[\"birthdate\"]"`
// Value is the threshold or expected value for the validation.
Value any `json:"value" yaml:"value" validate:"required" doc_example:"18"`
}
ClaimValidation defines a validation rule to apply against extracted credential claims.
type ClaimsExtractor ¶
type ClaimsExtractor struct {
// contains filtered or unexported fields
}
ClaimsExtractor extracts and maps claims from VP tokens to OIDC claims
func NewClaimsExtractor ¶
func NewClaimsExtractor() *ClaimsExtractor
NewClaimsExtractor creates a new claims extractor
func (*ClaimsExtractor) ApplyClaimTransforms ¶
func (ce *ClaimsExtractor) ApplyClaimTransforms(claims map[string]any, transformDefs map[string]ClaimTransformDef) (map[string]any, error)
ApplyClaimTransforms applies transformations to claim values transformDefs: Map of OIDC claim name to transform definition
func (*ClaimsExtractor) ExtractAndMapClaims ¶
func (ce *ClaimsExtractor) ExtractAndMapClaims( ctx context.Context, vpToken string, claimMappings map[string]string, transformDefs map[string]ClaimTransformDef, ) (map[string]any, error)
ExtractAndMapClaims is a convenience function that combines extraction, mapping, and transformation This is the main entry point for the complete claims processing pipeline
func (*ClaimsExtractor) ExtractClaimsFromVPToken ¶
func (ce *ClaimsExtractor) ExtractClaimsFromVPToken(ctx context.Context, vpToken string) (map[string]any, error)
ExtractClaimsFromVPToken extracts claims from a VP token. Automatically detects the format:
- DCQL response: JSON object mapping credential query IDs to individual tokens
- mdoc: CBOR-based mobile document
- SD-JWT: dot-separated JWT with selective disclosures
Returns a merged map of disclosed claims from all credentials.
func (*ClaimsExtractor) MapClaimsToOIDC ¶
func (ce *ClaimsExtractor) MapClaimsToOIDC(vpClaims map[string]any, claimMappings map[string]string) (map[string]any, error)
MapClaimsToOIDC maps VP claims to OIDC claims using the template's claim mappings claimMappings: Key = VP claim path, Value = OIDC claim name Special mapping "*" : "*" means pass all claims through unchanged
type Client ¶
type Client struct {
EphemeralKeyCache *EphemeralEncryptionKeyCache
RequestObjectCache *RequestObjectCache
}
Client holds the OpenID4VP client with ephemeral key caching and request object caching
func New ¶
New creates a new OpenID4VP client with ephemeral key cache and request object cache
Example ¶
ExampleNew demonstrates creating an OpenID4VP client with default settings
package main
import (
"context"
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
ctx := context.TODO()
// Create a client with default TTL settings (10 minutes for both caches)
client, err := openid4vp.New(ctx, nil)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
defer client.Close()
fmt.Println("Client created with default settings")
fmt.Printf("Ephemeral key cache initialized: %t\n", client.EphemeralKeyCache != nil)
fmt.Printf("Request object cache initialized: %t\n", client.RequestObjectCache != nil)
}
Output: Client created with default settings Ephemeral key cache initialized: true Request object cache initialized: true
Example (Usage) ¶
ExampleNew_usage demonstrates using both caches in the client
package main
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
"github.com/lestrrat-go/jwx/v3/jwk"
)
func main() {
ctx := context.TODO()
// Create client
client, err := openid4vp.New(ctx, nil)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// 1. Store a request object in the request object cache
requestObject := &openid4vp.RequestObject{
ResponseType: "vp_token",
ClientID: "https://verifier.example.com",
Nonce: "n-0S6_WzA2Mj",
State: "af0ifjsldkj",
}
requestURI := "urn:ietf:params:oauth:request_uri:6eSG8FrjKQb1Qiwj"
client.RequestObjectCache.Set(requestURI, requestObject)
// Retrieve the request object
retrieved, found := client.RequestObjectCache.Get(requestURI)
if found {
fmt.Printf("Request object - Client: %s\n", retrieved.ClientID)
fmt.Printf("Request object - State: %s\n", retrieved.State)
}
// 2. Store an ephemeral encryption key in the ephemeral key cache
// Generate a test key
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
key, _ := jwk.Import(privateKey)
_ = key.Set(jwk.KeyIDKey, "ephemeral-key-123")
client.EphemeralKeyCache.Set("ephemeral-key-123", key)
// Retrieve the ephemeral key
retrievedKey, found := client.EphemeralKeyCache.Get("ephemeral-key-123")
if found {
kid, _ := retrievedKey.KeyID()
fmt.Printf("Ephemeral key - KID: %s\n", kid)
}
// Check both cache sizes
fmt.Printf("Request objects in cache: %d\n", client.RequestObjectCache.Len())
fmt.Printf("Ephemeral keys in cache: %d\n", client.EphemeralKeyCache.Len())
}
Output: Request object - Client: https://verifier.example.com Request object - State: af0ifjsldkj Ephemeral key - KID: ephemeral-key-123 Request objects in cache: 1 Ephemeral keys in cache: 1
Example (WithCustomConfig) ¶
ExampleNew_withCustomConfig demonstrates creating a client with custom TTL settings
package main
import (
"context"
"fmt"
"time"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
ctx := context.TODO()
// Create a client with custom TTL values
config := &openid4vp.Config{
EphemeralKeyTTL: 5 * time.Minute, // Ephemeral keys expire after 5 minutes
RequestObjectTTL: 3 * time.Minute, // Request objects expire after 3 minutes
}
client, err := openid4vp.New(ctx, config)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
defer client.Close()
fmt.Println("Client created with custom TTL settings")
fmt.Printf("Ephemeral key cache TTL: 5 minutes\n")
fmt.Printf("Request object cache TTL: 3 minutes\n")
}
Output: Client created with custom TTL settings Ephemeral key cache TTL: 5 minutes Request object cache TTL: 3 minutes
type ClientMetadata ¶
type ClientMetadata struct {
// JWKS OPTIONAL. A JSON Web Key Set, as defined in [RFC7591], that contains one or more public keys, such as those used by the Wallet as an input to a key agreement that may be used for encryption of the Authorization Response (see Section 8.3), or where the Wallet will require the public key of the Verifier to generate a Verifiable Presentation. This allows the Verifier to pass ephemeral keys specific to this Authorization Request. Public keys included in this parameter MUST NOT be used to verify the signature of signed Authorization Requests. Each JWK in the set MUST have a kid (Key ID) parameter that uniquely identifies the key within the context of the request.
JWKS *Keys `json:"jwks,omitempty" bson:"jwks,omitempty" validate:"omitempty"`
// encrypted_response_enc_values_supported: OPTIONAL. Non-empty array of strings, where each string is a JWE [RFC7516] enc algorithm that can be used as the content encryption algorithm for encrypting the Response. When a response_mode requiring encryption of the Response (such as dc_api.jwt or direct_post.jwt) is specified, this MUST be present for anything other than the default single value of A128GCM. Otherwise, this SHOULD be absent.
EncryptedResponseEncValuesSupported []string `` /* 189-byte string literal not displayed */
// vp_formats_supported: REQUIRED when not available to the Wallet via another mechanism. As defined in Section 11.1.
// Per OpenID4VP spec, this is the properly formatted field for client_metadata in authorization requests
VPFormatsSupported *VPFormatsSupported `json:"vp_formats_supported,omitempty" bson:"vp_formats_supported,omitempty"`
// authorization_signed_response_alg: OPTIONAL. As defined in [JARM].
AuthorizationSignedResponseALG string `` /* 152-byte string literal not displayed */
// authorization_encrypted_response_alg: OPTIONAL. As defined in [JARM].
AuthorizationEncryptedResponseALG string `` /* 175-byte string literal not displayed */
// authorization_encrypted_response_enc: OPTIONAL. As defined in [JARM].
AuthorizationEncryptedResponseENC string `` /* 178-byte string literal not displayed */
}
type CombinedBindingResult ¶ added in v0.7.0
type CombinedBindingResult struct {
// Bound indicates whether the verifier considers the credentials bound to the same holder.
Bound bool `json:"bound"`
// HighestConfidence is the best confidence level achieved across all methods.
HighestConfidence BindingConfidence `json:"highest_confidence"`
// Results contains individual results per method.
Results []BindingResult `json:"results"`
}
CombinedBindingResult aggregates results from all binding methods.
func (*CombinedBindingResult) Err ¶ added in v0.7.0
func (r *CombinedBindingResult) Err() error
Err returns a combined error if any binding method produced an error, or nil if all methods succeeded.
func (*CombinedBindingResult) Valid ¶ added in v0.7.0
func (r *CombinedBindingResult) Valid() bool
Valid returns true if binding was established without any method errors.
type CombinedBindingVerifier ¶ added in v0.7.0
type CombinedBindingVerifier struct {
Config CombinedPresentationConfig
}
CombinedBindingVerifier verifies that multiple credentials in a combined presentation belong to the same holder.
func (*CombinedBindingVerifier) Verify ¶ added in v0.7.0
func (v *CombinedBindingVerifier) Verify(credentials []VerifiedCredentialBinding) (*CombinedBindingResult, error)
Verify checks binding across the provided verified credentials. Returns nil result and nil error if fewer than 2 credentials are provided (no binding needed).
type CombinedPresentationConfig ¶ added in v0.7.0
type CombinedPresentationConfig struct {
// Enabled activates combined presentation binding verification.
Enabled bool `yaml:"enabled" json:"enabled"`
// Enforcement determines how binding verification results are handled:
// - "enforce": reject the presentation if binding cannot be established
// - "warn": log a warning but allow the presentation through (per ARF 3.0 ACP_08)
// - "disabled": skip binding verification entirely
Enforcement BindingEnforcement `yaml:"enforcement" json:"enforcement" validate:"omitempty,oneof=enforce warn disabled" default:"warn"`
// BindingAttributes configures attribute-based binding checks.
BindingAttributes []BindingAttributeConfig `yaml:"binding_attributes,omitempty" json:"binding_attributes,omitempty" validate:"omitempty,dive"`
// KeyBindingEnabled activates key-based binding (cnf.jwk / device key comparison).
// Cross-format comparison (SD-JWT cnf.jwk vs mDoc device key) is always
// supported since both are converted to RFC 7638 JWK thumbprints.
KeyBindingEnabled bool `yaml:"key_binding_enabled" json:"key_binding_enabled"`
}
CombinedPresentationConfig configures combined presentation verification.
type Config ¶
type Config struct {
// EphemeralKeyTTL specifies the TTL for ephemeral encryption keys.
// If not set or zero, DefaultEphemeralKeyTTL (10 minutes) is used.
EphemeralKeyTTL time.Duration
// RequestObjectTTL specifies the TTL for request objects.
// If not set or zero, DefaultRequestObjectTTL (10 minutes) is used.
RequestObjectTTL time.Duration
}
Config holds configuration for the OpenID4VP client
type Constraints ¶
type CredentialQuery ¶
type CredentialQuery struct {
// ID REQUIRED. A string identifying the Credential in the response and, if provided, the constraints in credential_sets. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the Authorization Request, the same id MUST NOT be present more than once.
ID string `json:"id" yaml:"id" validate:"required"`
// Format REQUIRED. A string that specifies the format of the requested Credential. Valid Credential Format Identifier values are defined in Appendix B.
Format string `json:"format" yaml:"format" validate:"required"`
// Multiple OPTIONAL. A boolean which indicates whether multiple Credentials can be returned for this Credential Query. If omitted, the default value is false.
Multiple bool `json:"multiple,omitempty" yaml:"multiple,omitempty"`
// Meta REQUIRED. An object defining additional properties requested by the Verifier that apply to the metadata and validity data of the Credential. The properties of this object are defined per Credential Format. Examples of those are in Appendix B.3.5 and Appendix B.2.3. If empty, no specific constraints are placed on the metadata or validity of the requested Credential.
Meta MetaQuery `json:"meta" yaml:"meta" validate:"required"`
// TrustedAuthorities OPTIONAL. A non-empty array of objects as defined in Section 6.1.1 that specifies expected authorities or trust frameworks that certify Issuers, that the Verifier will accept. Every Credential returned by the Wallet SHOULD match at least one of the conditions present in the corresponding trusted_authorities array if present.
TrustedAuthorities []TrustedAuthority `json:"trusted_authorities,omitempty" yaml:"trusted_authorities,omitempty"`
// RequireCryptographicHolderBinding OPTIONAL. Spec default is true when omitted; nil pointer preserves that distinction from an explicit false.
RequireCryptographicHolderBinding *bool `json:"require_cryptographic_holder_binding,omitempty" yaml:"require_cryptographic_holder_binding,omitempty"`
// Claims OPTIONAL. A non-empty array of objects as defined in Section 6.3 that specifies claims in the requested Credential. Verifiers MUST NOT point to the same claim more than once in a single query. Wallets SHOULD ignore such duplicate claim queries.
Claims []ClaimQuery `json:"claims,omitempty" yaml:"claims,omitempty"`
// ClaimSet OPTIONAL. A non-empty array containing arrays of identifiers for elements in claims that specifies which combinations of claims for the Credential are requested. The rules for selecting claims to send are defined in Section 6.4.1.
ClaimSet [][]string `json:"claim_sets,omitempty" yaml:"claim_sets,omitempty" validate:"omitnil,min=1,dive,required,min=1,dive,required"`
}
CredentialQuery is an object representing a request for a presentation of one or more matching Credentials.
func NewVC20CredentialQuery ¶
func NewVC20CredentialQuery(id string, typeValues [][]string, claims []ClaimQuery) CredentialQuery
NewVC20CredentialQuery creates a new CredentialQuery for W3C VC 2.0 Data Integrity format. typeValues should be an array of type alternatives, where each alternative is an array of fully expanded type IRIs that must all be present in the credential.
func (CredentialQuery) RequiresCryptographicHolderBinding ¶ added in v0.7.2
func (q CredentialQuery) RequiresCryptographicHolderBinding() bool
RequiresCryptographicHolderBinding returns the effective value of RequireCryptographicHolderBinding, applying the OpenID4VP 1.0 §6.1 default (true) when the field was omitted.
type CredentialSetQuery ¶
type CredentialSetQuery struct {
// Options REQUIRED A non-empty array, where each value in the array is a list of Credential Query identifiers representing one set of Credentials that satisfies the use case. The value of each element in the options array is a non-empty array of identifiers which reference elements in credentials.
Options [][]string `json:"options" yaml:"options" validate:"required,min=1,dive,required,min=1,dive,required"`
// Required OPTIONAL. Spec default is true when omitted; nil pointer preserves that distinction from an explicit false.
Required *bool `json:"required,omitempty" yaml:"required,omitempty"`
}
func (CredentialSetQuery) IsRequired ¶ added in v0.7.2
func (q CredentialSetQuery) IsRequired() bool
IsRequired returns the effective value of Required, applying the OpenID4VP 1.0 §6.2 default (true) when the field was omitted.
type DCQL ¶
type DCQL struct {
// Credentials REQUIRED. A non-empty array of Credential Queries as defined in Section 6.1 that specify the requested Credentials.
Credentials []CredentialQuery `json:"credentials" yaml:"credentials" validate:"required,min=1,dive,required"`
// CredentialSets OPTIONAL. A non-empty array of Credential Set Queries as defined in Section 6.2 that specifies additional constraints on which of the requested Credentials to return.
CredentialSets []CredentialSetQuery `json:"credential_sets,omitempty" yaml:"credential_sets,omitempty" validate:"omitnil,min=1,dive,required"`
}
type DCQLValidationError ¶
DCQLValidationError represents a validation error in a DCQL query.
func (*DCQLValidationError) Error ¶
func (e *DCQLValidationError) Error() string
type Descriptor ¶
type Descriptor struct {
ID string `json:"id" validate:"required"`
Path string `json:"path" validate:"required"`
PathNested *Descriptor `json:"path_nested,omitempty"`
Format string `json:"format" validate:"required,oneof=jwt jwt_vc jwt_vp ldp ldp_vc ldp_vp mso_mdoc ac_vc ac_vp sd_jwt"`
}
type DirectPostJWTResponse ¶
type DirectPostJWTResponse struct {
Response string `json:"response"` // Encrypted JWT containing the Authorization Response
}
DirectPostJWTResponse represents response_mode=direct_post.jwt (Section 8.3.1)
type DirectPostResponse ¶
type DirectPostResponse struct {
RedirectURI string `json:"redirect_uri,omitempty"` // Optional per spec
}
DirectPostResponse is the response from the direct_post endpoint (Section 8.2)
type EphemeralEncryptionKeyCache ¶
type EphemeralEncryptionKeyCache struct {
// contains filtered or unexported fields
}
EphemeralEncryptionKeyCache manages short-lived encryption keys for response encryption
func NewEphemeralEncryptionKeyCache ¶
func NewEphemeralEncryptionKeyCache(ttl time.Duration) *EphemeralEncryptionKeyCache
NewEphemeralEncryptionKeyCache creates and starts a new ephemeral encryption key cache with the specified TTL. Keys are automatically evicted after the TTL expires.
Example ¶
ExampleNewEphemeralEncryptionKeyCache demonstrates creating an ephemeral encryption key cache
package main
import (
"fmt"
"time"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
// Create a cache with 10-minute TTL
cache := openid4vp.NewEphemeralEncryptionKeyCache(10 * time.Minute)
defer cache.Stop()
fmt.Println("Ephemeral encryption key cache created")
fmt.Printf("Initial cache size: %d\n", cache.Len())
}
Output: Ephemeral encryption key cache created Initial cache size: 0
func (*EphemeralEncryptionKeyCache) Delete ¶
func (e *EphemeralEncryptionKeyCache) Delete(kid string)
Delete removes an ephemeral encryption key from the cache
func (*EphemeralEncryptionKeyCache) GenerateAndStore ¶
func (e *EphemeralEncryptionKeyCache) GenerateAndStore(kid string) (privateKey jwk.Key, publicKey jwk.Key, err error)
GenerateAndStore generates a new ephemeral encryption key pair, stores the private key in the cache, and returns both private and public JWKs. The key uses ECDH P-256.
Example ¶
ExampleEphemeralEncryptionKeyCache_GenerateAndStore demonstrates generating and storing a key pair
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
cache := openid4vp.NewEphemeralEncryptionKeyCache(openid4vp.DefaultEphemeralKeyTTL)
defer cache.Stop()
// Generate ECDH P-256 key pair and store private key in cache
kid := "ephemeral-key-abc123"
privateKey, publicKey, err := cache.GenerateAndStore(kid)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Private key is automatically stored in cache
retrievedPrivate, found := cache.Get(kid)
if found {
fmt.Printf("Private key stored in cache: %t\n", retrievedPrivate != nil)
}
// Get key IDs
privateKid, _ := privateKey.KeyID()
publicKid, _ := publicKey.KeyID()
fmt.Printf("Private key KID: %s\n", privateKid)
fmt.Printf("Public key KID: %s\n", publicKid)
// Public key has "use" set to "enc"
use, _ := publicKey.KeyUsage()
fmt.Printf("Public key usage: %s\n", use)
}
Output: Private key stored in cache: true Private key KID: ephemeral-key-abc123 Public key KID: ephemeral-key-abc123 Public key usage: enc
func (*EphemeralEncryptionKeyCache) Get ¶
func (e *EphemeralEncryptionKeyCache) Get(kid string) (jwk.Key, bool)
Get retrieves an ephemeral encryption key by its key ID (kid)
func (*EphemeralEncryptionKeyCache) Len ¶
func (e *EphemeralEncryptionKeyCache) Len() int
Len returns the number of items currently in the cache
func (*EphemeralEncryptionKeyCache) Set ¶
func (e *EphemeralEncryptionKeyCache) Set(kid string, key jwk.Key)
Set stores an ephemeral encryption key with the specified key ID (kid)
func (*EphemeralEncryptionKeyCache) SetWithTTL ¶
SetWithTTL stores an ephemeral encryption key with a custom TTL
func (*EphemeralEncryptionKeyCache) Stop ¶
func (e *EphemeralEncryptionKeyCache) Stop()
Stop stops the cache's automatic expiration goroutine
type ErrorResponse ¶
type ErrorResponse struct {
ErrorCode string `json:"error"`
ErrorDescription string `json:"error_description,omitempty"`
State string `json:"state,omitempty"`
}
ErrorResponse represents an OAuth 2.0 error response as defined in Section 8.5
func NewErrorResponse ¶
func NewErrorResponse(code, description, state string) *ErrorResponse
NewErrorResponse creates a new ErrorResponse
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
err := openid4vp.NewErrorResponse(
openid4vp.ErrorAccessDenied,
"user denied consent",
"state-abc123",
)
fmt.Println("error:", err.Error())
fmt.Println("code:", err.ErrorCode)
fmt.Println("state:", err.State)
}
Output: error: access_denied: user denied consent code: access_denied state: state-abc123
func (*ErrorResponse) Error ¶
func (e *ErrorResponse) Error() string
Error implements the error interface for ErrorResponse
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
// Error with description
err1 := openid4vp.NewErrorResponse(openid4vp.ErrorInvalidRequest, "missing nonce", "")
fmt.Println(err1.Error())
// Error without description
err2 := openid4vp.NewErrorResponse(openid4vp.ErrorWalletUnavailable, "", "")
fmt.Println(err2.Error())
}
Output: invalid_request: missing nonce wallet_unavailable
func (*ErrorResponse) IsAuthorizationError ¶
func (e *ErrorResponse) IsAuthorizationError() bool
IsAuthorizationError checks if this is an authorization-related error
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
authErr := openid4vp.NewErrorResponse(openid4vp.ErrorAccessDenied, "denied", "")
fmt.Println("access_denied is auth error:", authErr.IsAuthorizationError())
formatErr := openid4vp.NewErrorResponse(openid4vp.ErrorVPFormatsNotSupported, "unsupported", "")
fmt.Println("vp_formats_not_supported is auth error:", formatErr.IsAuthorizationError())
}
Output: access_denied is auth error: true vp_formats_not_supported is auth error: false
type InputDescriptor ¶
type InputDescriptor struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Purpose string `json:"purpose,omitempty"`
Format map[string]map[string][]string `json:"format,omitempty"`
Group []string `json:"group,omitempty"`
Constraints Constraints `json:"constraints"`
}
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
descriptor := openid4vp.InputDescriptor{
ID: "identity_credential",
Name: "Identity Credential",
Purpose: "We need to verify your identity",
Constraints: openid4vp.Constraints{
LimitDisclosure: "required",
Fields: []openid4vp.Field{
{
Path: []string{"$.vc.credentialSubject.given_name"},
},
},
},
}
fmt.Println("id:", descriptor.ID)
fmt.Println("name:", descriptor.Name)
fmt.Println("limit_disclosure:", descriptor.Constraints.LimitDisclosure)
fmt.Println("fields:", len(descriptor.Constraints.Fields))
}
Output: id: identity_credential name: Identity Credential limit_disclosure: required fields: 1
type JWK ¶
type JWK struct {
KTY string `json:"kty,omitempty" bson:"kty,omitempty" validate:"required,oneof=RSA EC OKP"`
X string `json:"x,omitempty" bson:"x,omitempty" validate:"omitempty"`
Y string `json:"y,omitempty" bson:"y,omitempty" validate:"omitempty"`
CRV string `json:"crv,omitempty" bson:"crv,omitempty" validate:"omitempty,oneof=P-256 P-384 P-521 Ed25519 Ed448 X25519 X448"`
N string `json:"n,omitempty" bson:"n,omitempty" validate:"omitempty"`
KID string `json:"kid,omitempty" bson:"kid,omitempty" validate:"omitempty"`
E string `json:"e,omitempty" bson:"e,omitempty" validate:"omitempty"`
Use string `json:"use,omitempty" bson:"use,omitempty" validate:"omitempty,oneof=sig enc"`
Alg string `json:"alg,omitempty" bson:"alg,omitempty"`
}
type JWTVCFormat ¶
type JWTVCFormat struct {
// AlgValues is a non-empty array containing identifiers of cryptographic algorithms supported.
// If present, the alg JOSE header of the presented VC/VP MUST match one of the array values.
AlgValues []string `json:"alg_values,omitempty" yaml:"alg_values,omitempty"`
}
JWTVCFormat defines format-specific parameters for JWT-based W3C VC (jwt_vc_json).
type LDPVCFormat ¶
type LDPVCFormat struct {
// ProofTypeValues is a non-empty array containing identifiers of proof types supported.
// If present, the proof type of the presented VC/VP MUST match one of the array values.
ProofTypeValues []string `` /* 134-byte string literal not displayed */
// CryptosuiteValues is a non-empty array containing identifiers of crypto suites supported.
// Used when one of the algorithms in ProofTypeValues supports multiple crypto suites.
CryptosuiteValues []string `` /* 161-byte string literal not displayed */
}
LDPVCFormat defines format-specific parameters for W3C VC Data Integrity (ldp_vc).
type MetaQuery ¶
type MetaQuery struct {
// VCTValues for SD-JWT VC format (dc+sd-jwt).
// A non-empty array of strings that specifies allowed values for the type of the requested Verifiable Credential.
// All elements in the array MUST be valid type identifiers as defined in [I-D.ietf-oauth-sd-jwt-vc].
// The Wallet MAY return Credentials that inherit from any of the specified types.
VCTValues []string `json:"vct_values,omitempty" yaml:"vct_values,omitempty"`
// TypeValues for W3C VC format (ldp_vc, jwt_vc_json).
// A non-empty array of string arrays specifying the fully expanded types (IRIs) that the Verifier accepts.
// Each top-level array specifies one alternative to match the fully expanded type values of the Verifiable Credential.
// Each inner array specifies a set of fully expanded types that MUST be present in the credential's type property.
TypeValues [][]string `json:"type_values,omitempty" yaml:"type_values,omitempty"`
// DoctypeValue for ISO mdoc format (mso_mdoc) and ZK-mdoc format (mso_mdoc_zk).
// String that specifies an allowed value for the doctype of the requested Verifiable Credential.
DoctypeValue string `json:"doctype_value,omitempty" yaml:"doctype_value,omitempty"`
// ZKSystemType for ZK-mdoc format (mso_mdoc_zk) only.
// A non-empty array of ZK proof system/circuit variants the Verifier is
// willing to accept (e.g. Longfellow circuits for different attribute
// counts). See ZKSystemTypeSpec and dcql_zk.go.
ZKSystemType []ZKSystemTypeSpec `json:"zk_system_type,omitempty" yaml:"zk_system_type,omitempty"`
// PPIDContext for ZK-mdoc format (mso_mdoc_zk) only.
// An optional verifier-supplied string that further scopes a requested
// pairwise pseudonym (PPID) beyond the verifier's own identity - see
// pkg/mdoc.ComputeZkVerifierContext.
PPIDContext string `json:"ppid_context,omitempty" yaml:"ppid_context,omitempty"`
}
MetaQuery represents format-specific metadata constraints for credential queries. For SD-JWT VC format (dc+sd-jwt): use VCTValues For W3C VC format (ldp_vc): use TypeValues
type MsoMdocFormat ¶
type MsoMdocFormat struct {
// IssuerAuthAlgValues is a non-empty array containing cryptographic algorithm identifiers
// supported for IssuerAuth COSE signatures.
IssuerAuthAlgValues []int `json:"issuerauth_alg_values,omitempty" yaml:"issuerauth_alg_values,omitempty"`
// DeviceAuthAlgValues is a non-empty array containing cryptographic algorithm identifiers
// supported for DeviceAuth COSE signatures or MACs.
DeviceAuthAlgValues []int `json:"deviceauth_alg_values,omitempty" yaml:"deviceauth_alg_values,omitempty"`
}
MsoMdocFormat defines format-specific parameters for ISO mdoc (mso_mdoc).
type PresentationBuilder ¶
type PresentationBuilder struct {
// contains filtered or unexported fields
}
PresentationBuilder builds OpenID4VP presentation requests from templates
func NewPresentationBuilder ¶
func NewPresentationBuilder[T PresentationRequestTemplate](templates []T) *PresentationBuilder
NewPresentationBuilder creates a new PresentationBuilder with the given templates The templates parameter accepts any slice of types that implement PresentationRequestTemplate
func (*PresentationBuilder) BuildDCQLQuery ¶
BuildDCQLQuery creates a DCQL query from OIDC scopes. This attempts to find matching templates, and falls back to a generic DCQL query if none are found. All scopes are considered for matching, including standard OIDC scopes like "openid". This allows standard OIDC scopes to optionally map to credentials if configured. Non-standard scopes are prioritized over standard scopes to prevent "openid" from always being selected when it appears first in the request.
func (*PresentationBuilder) BuildFromScopes ¶
func (pb *PresentationBuilder) BuildFromScopes(ctx context.Context, scopes []string) (*DCQL, PresentationRequestTemplate, error)
BuildFromScopes creates a DCQL query from OIDC scopes using configured templates Returns the DCQL query and the template that was used
func (*PresentationBuilder) BuildFromTemplate ¶
func (pb *PresentationBuilder) BuildFromTemplate(ctx context.Context, templateID string) (*DCQL, PresentationRequestTemplate, error)
BuildFromTemplate creates a DCQL query from a specific template ID
func (*PresentationBuilder) FindTemplateByScopes ¶
func (pb *PresentationBuilder) FindTemplateByScopes(scopes []string) PresentationRequestTemplate
FindTemplateByScopes finds a template that matches the given OIDC scopes Returns the first template where all requested scopes are present in the template's scopes
func (*PresentationBuilder) GetTemplate ¶
func (pb *PresentationBuilder) GetTemplate(templateID string) (PresentationRequestTemplate, error)
GetTemplate returns a specific template by ID
func (*PresentationBuilder) ListTemplates ¶
func (pb *PresentationBuilder) ListTemplates() []PresentationRequestTemplate
ListTemplates returns all templates
type PresentationDefinitionParameter ¶
type PresentationDefinitionParameter struct {
// ID The Presentation Definition MUST contain an id property. The value of this property MUST be a string.
// The string SHOULD provide a unique ID for the desired context.
ID string `json:"id" bson:"id" validate:"required"`
// Name The Presentation Definition MAY contain a name property. If present, its value SHOULD be a
// human-friendly string intended to constitute a distinctive designation of the Presentation Definition.
Name string `json:"name,omitempty" bson:"name,omitempty"`
// Purpose The Presentation Definition MAY contain a purpose property. If present, its value MUST be a
// string that describes the purpose for which the Presentation Definition's inputs are being used for.
Purpose string `json:"purpose,omitempty" bson:"purpose,omitempty"`
// InputDescriptors The Presentation Definition MUST contain an input_descriptors property.
// Its value MUST be an array of Input Descriptor Objects.
InputDescriptors []InputDescriptor `json:"input_descriptors" bson:"input_descriptors" validate:"required,dive"`
// SubmissionRequirements The Presentation Definition MAY contain a submission_requirements property.
// If present, its value MUST be an array of Submission Requirement Objects.
SubmissionRequirements []SubmissionRequirement `json:"submission_requirements,omitempty" bson:"submission_requirements,omitempty" validate:"omitempty,dive"`
// Format The Presentation Definition MAY contain a format property. If present, its value MUST be an object
// with one or more properties matching the registered Claim Format Designations.
Format map[string]Format `json:"format,omitempty" bson:"format,omitempty"`
}
type PresentationRequestTemplate ¶
type PresentationRequestTemplate interface {
GetID() string
GetOIDCScopes() []string
GetDCQLQuery() *DCQL
}
PresentationRequestTemplate represents a template for creating presentation requests. This is a minimal interface to avoid import cycles with pkg/configuration.
type PresentationSubmission ¶
type PresentationSubmission struct {
ID string `json:"id" validate:"required"`
DefinitionID string `json:"definition_id" validate:"required"`
DescriptorMap []Descriptor `json:"descriptor_map" validate:"required,dive,required"`
}
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
submission := openid4vp.PresentationSubmission{
ID: "submission-1",
DefinitionID: "definition-1",
DescriptorMap: []openid4vp.Descriptor{
{
ID: "identity_credential",
Path: "$",
Format: "sd_jwt",
},
},
}
fmt.Println("id:", submission.ID)
fmt.Println("definition_id:", submission.DefinitionID)
fmt.Println("descriptors:", len(submission.DescriptorMap))
fmt.Println("first descriptor format:", submission.DescriptorMap[0].Format)
}
Output: id: submission-1 definition_id: definition-1 descriptors: 1 first descriptor format: sd_jwt
type QRReply ¶
type QRReply struct {
Base64Image string `json:"base64_image" bson:"base64_image" validate:"required"`
URI string `json:"uri" bson:"uri" validate:"required"`
}
QRReply is a collection of fields representing a QRReply code TODO(masv): not sure if the type should include uri,request_uri,client_id,session_id
func GenerateQR ¶
type RequestObject ¶
type RequestObject struct {
// ISS MAY be present in the Request Object. However, if it is present, the Wallet MUST ignore it.
ISS string `json:"iss" uri:"iss" validate:"required,url"`
AUD string `json:"aud,omitempty" bson:"aud,omitempty" validate:"required"`
IAT int64 `json:"iat,omitempty" bson:"iat,omitempty" validate:"required"`
// ResponseType REQUIRED. Value MUST be set to "code".
ResponseType string `json:"response_type" uri:"response_type" validate:"required,eq=code"`
// REQUIRED. Defined in [RFC6749]. This specification defines additional requirements to enable the use of Client Identifier Prefixes as described in Section 5.9. The Client Identifier can be created by parties other than the Wallet and it is considered unique within the context of the Wallet when used in combination with the Client Identifier Prefix.
ClientID string `json:"client_id" uri:"client_id" validate:"required"`
// RedirectURI OPTIONAL. As described in Section 3.1.2.
RedirectURI string `json:"redirect_uri,omitempty" uri:"redirect_uri" validate:"omitempty,url"`
// Scope OPTIONAL. The scope of the access request as described by Section 3.3.
Scope string `json:"scope,omitempty" uri:"scope" validate:"omitempty"`
// State REQUIRED under the conditions defined in Section 5.3. Otherwise, state is OPTIONAL. state values MUST only contain ASCII URL safe characters (uppercase and lowercase letters, decimal digits, hyphen, period, underscore, and tilde).
State string `json:"state,omitempty" uri:"state" validate:"omitempty"`
// Nonce REQUIRED. A case-sensitive String representing a value to securely bind Verifiable Presentation(s) provided by the Wallet to the particular transaction. The Verifier MUST create a fresh, cryptographically random number with sufficient entropy for every Authorization Request, store it with its current session, and pass it in the nonce Authorization Request Parameter to the Wallet. See Section 14.1 for details. Values MUST only contain ASCII URL safe characters (uppercase and lowercase letters, decimal digits, hyphen, period, underscore, and tilde).
Nonce string `json:"nonce" uri:"nonce" validate:"required"`
// ResponseMode REQUIRED. Defined in [OAuth.Responses]. This parameter can be used (through the new Response Mode direct_post) to ask the Wallet to send the response to the Verifier via an HTTPS connection (see Section 8.2 for more details). It can also be used to request that the resulting response be encrypted (see Section 8.3 for more details).
ResponseMode string `` /* 126-byte string literal not displayed */
// dcql_query
DCQLQuery *DCQL `json:"dcql_query,omitempty" bson:"dcql_query,omitempty" validate:"omitempty,dive"`
// ClientMetadata OPTIONAL. A JSON object containing the Verifier metadata values. It MUST be UTF-8 encoded.
ClientMetadata *ClientMetadata `json:"client_metadata,omitempty" validate:"omitempty"`
// RequestURIMethod: OPTIONAL. A string determining the HTTP method to be used when the request_uri parameter is included in the same request. Two case-sensitive valid values are defined in this specification: get and post. If request_uri_method value is get, the Wallet MUST send the request to retrieve the Request Object using the HTTP GET method, i.e., as defined in [RFC9101]. If request_uri_method value is post, a supporting Wallet MUST send the request using the HTTP POST method as detailed in Section 5.10. If the request_uri_method parameter is not present, the Wallet MUST process the request_uri parameter as defined in [RFC9101]. Wallets not supporting the post method will send a GET request to the Request URI (default behavior as defined in [RFC9101]). request_uri_method parameter MUST NOT be present if a request_uri parameter is not present. If the Verifier set the request_uri_method parameter value to post and there is no other means to convey its capabilities to the Wallet, it SHOULD add the client_metadata parameter to the Authorization Request. This enables the Wallet to assess the Verifier's capabilities, allowing it to transmit only the relevant capabilities through the wallet_metadata parameter in the Request URI POST request.
RequestURIMethod string `json:"request_uri_method,omitempty" bson:"request_uri_method,omitempty" validate:"omitempty,oneof=get post"`
// TransactionData OPTIONAL. Array of base64url-encoded JSON objects with transaction details the Verifier requests the End-User to authorize (§8.4).
TransactionData []string `json:"transaction_data,omitempty" bson:"transaction_data,omitempty" validate:"omitempty,dive,required"`
// VerifierInfo OPTIONAL. A non-empty array of attestations about the Verifier relevant to the Credential Request. These attestations MAY include Verifier metadata, policies, trust status, or authorizations. Attestations are intended to support authorization decisions, inform Wallet policy enforcement, or enrich the End-User consent dialog.
VerifierInfo []VerifierInfo `json:"verifier_info,omitempty" bson:"verifier_info,omitempty" validate:"omitempty,dive,required"`
// ResponseURI REQUIRED when the Response Mode direct_post is used. The URL to which the Wallet MUST send the Authorization Response using an HTTP POST request as defined by the Response Mode direct_post. The Response URI receives all Authorization Response parameters as defined by the respective Response Type. When the response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present. If the redirect_uri Authorization Request parameter is present when the Response Mode is direct_post, the Wallet MUST return an invalid_request Authorization Response error. The response_uri value MUST be a value that the client would be permitted to use as redirect_uri when following the rules defined in Section 5.9.
ResponseURI string `json:"response_uri,omitempty" uri:"response_uri" validate:"required"`
}
RequestObject is sent by authorization request
func (*RequestObject) CreateAuthorizationRequestURI ¶
func (*RequestObject) Sign ¶
Sign creates a signed JWT representation of the RequestObject according to OpenID4VP specification. The JWT typ header is set to "oauth-authz-req+jwt" as required by OpenID4VP Section 5.2. Uses pki.Signer interface which supports both software keys and HSM.
Parameters:
- ctx: Context for signing operations
- signer: The pki.Signer for signing (supports both software and HSM keys)
- x5c: Optional X.509 certificate chain for key verification
Returns the signed JWT string or an error if signing fails.
type RequestObjectCache ¶
type RequestObjectCache struct {
// contains filtered or unexported fields
}
RequestObjectCache manages short-lived request objects for authorization requests
func NewRequestObjectCache ¶
func NewRequestObjectCache(ttl time.Duration) *RequestObjectCache
NewRequestObjectCache creates and starts a new request object cache with the specified TTL. Request objects are automatically evicted after the TTL expires.
Example ¶
ExampleNewRequestObjectCache demonstrates creating a request object cache
package main
import (
"fmt"
"time"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
// Create a cache with 10-minute TTL
cache := openid4vp.NewRequestObjectCache(10 * time.Minute)
defer cache.Stop()
// Store a request object
requestURI := "urn:ietf:params:oauth:request_uri:6eSG8FrjKQb1Qiwj"
requestObject := &openid4vp.RequestObject{
ResponseType: "vp_token",
ClientID: "https://verifier.example.com",
Nonce: "n-0S6_WzA2Mj",
}
cache.Set(requestURI, requestObject)
// Retrieve the request object
retrieved, found := cache.Get(requestURI)
if found {
fmt.Printf("Found request object for client: %s\n", retrieved.ClientID)
}
}
Output: Found request object for client: https://verifier.example.com
func (*RequestObjectCache) Delete ¶
func (r *RequestObjectCache) Delete(requestURI string)
Delete removes a request object from the cache
func (*RequestObjectCache) Get ¶
func (r *RequestObjectCache) Get(requestURI string) (*RequestObject, bool)
Get retrieves a request object by its request URI
func (*RequestObjectCache) Len ¶
func (r *RequestObjectCache) Len() int
Len returns the number of items currently in the cache
func (*RequestObjectCache) Set ¶
func (r *RequestObjectCache) Set(requestURI string, requestObject *RequestObject)
Set stores a request object with the specified request URI
func (*RequestObjectCache) SetWithTTL ¶
func (r *RequestObjectCache) SetWithTTL(requestURI string, requestObject *RequestObject, ttl time.Duration)
SetWithTTL stores a request object with a custom TTL
Example ¶
ExampleRequestObjectCache_SetWithTTL demonstrates setting a request object with custom TTL
package main
import (
"fmt"
"time"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
cache := openid4vp.NewRequestObjectCache(openid4vp.DefaultRequestObjectTTL)
defer cache.Stop()
// Store a request object with a custom short TTL (e.g., for one-time use)
requestURI := "urn:ietf:params:oauth:request_uri:short-lived"
requestObject := &openid4vp.RequestObject{
ResponseType: "vp_token",
ClientID: "https://verifier.example.com",
Nonce: "n-abc123",
}
// Set with 5-minute TTL instead of default 10 minutes
cache.SetWithTTL(requestURI, requestObject, 5*time.Minute)
fmt.Printf("Stored request object with 5-minute TTL\n")
}
Output: Stored request object with 5-minute TTL
func (*RequestObjectCache) Stop ¶
func (r *RequestObjectCache) Stop()
Stop stops the cache's automatic expiration goroutine
type ResponseParameters ¶
type ResponseParameters struct {
// VPToken REQUIRED. The structure of this parameter depends on the query language used to request the presentations in the Authorization Request:
VPToken string `json:"vp_token,omitempty" bson:"vp_token" validate:"omitempty,required_if=ResponseType vp_token"`
Code string `json:"code,omitempty" bson:"code"`
ISS string `json:"iss,omitempty" bson:"iss"`
State string `json:"state,omitempty" bson:"state"`
IDToken string `json:"id_token,omitempty" bson:"id_token"`
PresentationSubmission *PresentationSubmission `json:"presentation_submission,omitempty" bson:"presentation_submission"`
}
func ResponseParametersFromJSON ¶
func ResponseParametersFromJSON(data []byte) (*ResponseParameters, error)
ResponseParametersFromJSON deserializes the response parameters from JSON
func (*ResponseParameters) BuildCredential ¶
func (r *ResponseParameters) BuildCredential() (map[string]any, error)
BuildCredential unwraps the VPToken from the ResponseParameters
func (*ResponseParameters) ToJSON ¶
func (r *ResponseParameters) ToJSON() ([]byte, error)
ToJSON serializes the response parameters to JSON
func (*ResponseParameters) Validate ¶
func (r *ResponseParameters) Validate() error
Validate validates the response parameters according to OpenID4VP spec Section 8.1
type SDJWTVCFormat ¶
type SDJWTVCFormat struct {
// SDJWTAlgValues is a non-empty array containing cryptographic algorithm identifiers
// supported for the Issuer-signed JWT of an SD-JWT.
SDJWTAlgValues []string `json:"sd-jwt_alg_values,omitempty" yaml:"sd-jwt_alg_values,omitempty"`
// KBJWTAlgValues is a non-empty array containing cryptographic algorithm identifiers
// supported for a Key Binding JWT (KB-JWT).
KBJWTAlgValues []string `json:"kb-jwt_alg_values,omitempty" yaml:"kb-jwt_alg_values,omitempty"`
}
SDJWTVCFormat defines format-specific parameters for IETF SD-JWT VC (dc+sd-jwt).
type StaticVC20KeyResolver ¶
StaticVC20KeyResolver is a simple key resolver that returns a fixed key.
func (*StaticVC20KeyResolver) ResolveKey ¶
func (r *StaticVC20KeyResolver) ResolveKey(ctx context.Context, verificationMethod string) (crypto.PublicKey, error)
ResolveKey returns the static key regardless of verification method.
type SubmissionRequirement ¶
type TransactionData ¶
type TransactionData struct {
// Type REQUIRED. Collision-resistant identifier for the transaction data type.
Type string `json:"type,omitempty" bson:"type,omitempty" validate:"required"`
// CredentialIDS REQUIRED. Credential query IDs that can authorize this transaction.
CredentialIDS []string `json:"credential_ids,omitempty" bson:"credential_ids,omitempty" validate:"required,dive,required"`
}
func (*TransactionData) Base64Encode ¶
func (t *TransactionData) Base64Encode() (string, error)
Base64Encode encodes the TransactionData struct into a base64 URL-encoded string.
type TrustService ¶
type TrustService struct{}
func (*TrustService) ExtractPublicKeyFromX5C ¶
func (ts *TrustService) ExtractPublicKeyFromX5C(x5cBase64 string, ext ...*cryptoutil.Extensions) (any, error)
type TrustedAuthority ¶
type TrustedAuthority struct {
// REQUIRED. A string uniquely identifying the type of information about the issuer trust framework. Types defined by this specification are listed below.
Type string `json:"type" yaml:"type" validate:"required,oneof=aki etsi_tl openid_federation"`
// REQUIRED. A non-empty array of strings, where each string (value) contains information specific to the used Trusted Authorities Query type that allows the identification of an issuer, a trust framework, or a federation that an issuer belongs to.
Values []string `json:"values" yaml:"values" validate:"required,min=1"`
}
func NewTrustedAuthorityAKI ¶
func NewTrustedAuthorityAKI(akiValues ...string) TrustedAuthority
NewTrustedAuthorityAKI creates a TrustedAuthority for Authority Key Identifier matching.
func NewTrustedAuthorityETSI ¶
func NewTrustedAuthorityETSI(tlURLs ...string) TrustedAuthority
NewTrustedAuthorityETSI creates a TrustedAuthority for ETSI Trusted List matching.
func NewTrustedAuthorityOpenIDFederation ¶
func NewTrustedAuthorityOpenIDFederation(trustAnchors ...string) TrustedAuthority
NewTrustedAuthorityOpenIDFederation creates a TrustedAuthority for OpenID Federation matching.
type TrustedAuthorityMatcher ¶
type TrustedAuthorityMatcher interface {
// MatchAKI checks if a credential's certificate chain contains a certificate
// with the given Authority Key Identifier (base64url-encoded).
MatchAKI(credentialCertChain [][]byte, aki string) bool
// MatchETSI checks if a credential's issuer is present in the ETSI Trusted List.
// The tlURL is the URL of the Trusted List or List of Trusted Lists.
MatchETSI(credentialCertChain [][]byte, tlURL string) bool
// MatchOpenIDFederation checks if a credential's issuer is part of an OpenID Federation
// with the given Trust Anchor entity identifier.
MatchOpenIDFederation(issuer string, trustAnchorEntityID string) bool
}
TrustedAuthorityMatcher provides methods for matching credentials against trusted authorities. Implementations should integrate with trust frameworks (go-trust, ETSI TL, etc.).
type VC20CreateRequest ¶
type VC20CreateRequest struct {
// CredentialID is the unique ID for the credential (optional, generated if empty)
CredentialID string
// Types are the credential types (e.g., ["VerifiableCredential", "UniversityDegreeCredential"])
Types []string
// Subject is the credential subject (the entity the credential is about)
Subject map[string]any
// AdditionalContexts are extra JSON-LD contexts to include
AdditionalContexts []string
// ValidFrom is when the credential becomes valid (defaults to now)
ValidFrom time.Time
// ValidUntil is when the credential expires (optional)
ValidUntil *time.Time
// CredentialStatus for revocation (optional)
CredentialStatus map[string]any
}
VC20CreateRequest contains parameters for creating a credential.
type VC20CreateResult ¶
type VC20CreateResult struct {
// CredentialJSON is the signed credential as JSON bytes
CredentialJSON []byte
// CredentialID is the credential's unique ID
CredentialID string
// Issuer is the issuer DID
Issuer string
// ValidFrom is when the credential is valid from
ValidFrom time.Time
// ValidUntil is when the credential expires (nil if no expiration)
ValidUntil *time.Time
}
VC20CreateResult contains the signed credential and metadata.
type VC20Handler ¶
type VC20Handler struct {
// contains filtered or unexported fields
}
VC20Handler handles W3C VC 2.0 Data Integrity credentials in OpenID4VP flows.
func NewVC20Handler ¶
func NewVC20Handler(opts ...VC20HandlerOption) (*VC20Handler, error)
NewVC20Handler creates a new W3C VC 2.0 handler for OpenID4VP.
func (*VC20Handler) CreateCredential ¶
func (h *VC20Handler) CreateCredential(ctx context.Context, req *VC20CreateRequest) (*VC20CreateResult, error)
CreateCredential creates and signs a new W3C VC 2.0 Data Integrity credential.
func (*VC20Handler) VerifyAndExtract ¶
func (h *VC20Handler) VerifyAndExtract(ctx context.Context, vpToken string) (*VC20VerificationResult, error)
VerifyAndExtract verifies a W3C VC VP token and extracts claims.
type VC20HandlerOption ¶
type VC20HandlerOption func(*VC20Handler)
VC20HandlerOption configures a VC20Handler.
func WithVC20AllowedSkew ¶
func WithVC20AllowedSkew(skew time.Duration) VC20HandlerOption
WithVC20AllowedSkew sets the allowed clock skew for time validation.
func WithVC20Clock ¶
func WithVC20Clock(clock func() time.Time) VC20HandlerOption
WithVC20Clock sets the clock function for time validation.
func WithVC20KeyResolver ¶
func WithVC20KeyResolver(resolver VC20KeyResolver) VC20HandlerOption
WithVC20KeyResolver sets the key resolver for VC20 verification.
func WithVC20RevocationCheck ¶
func WithVC20RevocationCheck(check bool) VC20HandlerOption
WithVC20RevocationCheck enables credential status checking.
func WithVC20SignerConfig ¶
func WithVC20SignerConfig(config *VC20SignerConfig) VC20HandlerOption
WithVC20SignerConfig sets the signing configuration for credential issuance.
func WithVC20StaticKey ¶
func WithVC20StaticKey(key crypto.PublicKey) VC20HandlerOption
WithVC20StaticKey sets a static public key for VC20 verification.
func WithVC20TrustedIssuers ¶
func WithVC20TrustedIssuers(issuers []string) VC20HandlerOption
WithVC20TrustedIssuers sets the list of trusted issuers.
type VC20KeyResolver ¶
type VC20KeyResolver interface {
// ResolveKey resolves a verification method to a public key.
// verificationMethod can be:
// - Full DID URL: "did:key:z6Mk...#key-1"
// - DID with fragment: "did:web:example.com#keys-1"
// - HTTP URL: "https://example.com/keys/1"
// Returns crypto.PublicKey which can be *ecdsa.PublicKey or ed25519.PublicKey
ResolveKey(ctx context.Context, verificationMethod string) (crypto.PublicKey, error)
}
VC20KeyResolver resolves verification method URIs to public keys. Implementations can resolve DIDs (did:key, did:web, did:jwk, etc.), fetch JWKS, or use go-trust for policy-based resolution.
type VC20SignerConfig ¶
type VC20SignerConfig struct {
// PrivateKey is the signing key (ecdsa.PrivateKey or ed25519.PrivateKey)
PrivateKey crypto.PrivateKey
// IssuerID is the DID or URI of the issuer (e.g., "did:web:example.com")
IssuerID string
// VerificationMethod is the full verification method URI (e.g., "did:web:example.com#key-1")
VerificationMethod string
// Cryptosuite specifies which suite to use: ecdsa-rdfc-2019, ecdsa-sd-2023, eddsa-rdfc-2022
Cryptosuite string
}
VC20SignerConfig holds issuer signing configuration.
type VC20VerificationResult ¶
type VC20VerificationResult struct {
// Credential metadata
ID string `json:"id,omitempty"`
Issuer string `json:"issuer"`
Subject string `json:"subject,omitempty"`
Types []string `json:"type"`
IssuanceDate time.Time `json:"validFrom"`
ExpirationDate *time.Time `json:"validUntil,omitempty"`
// Credential content
CredentialSubject map[string]any `json:"credentialSubject"`
// Proof metadata
ProofType string `json:"proofType"`
Cryptosuite string `json:"cryptosuite"`
VerificationMethod string `json:"verificationMethod"`
ProofPurpose string `json:"proofPurpose"`
ProofCreated time.Time `json:"proofCreated"`
// Selective disclosure info (for ecdsa-sd-2023)
IsSelectiveDisclosure bool `json:"isSelectiveDisclosure"`
DisclosedPaths []string `json:"disclosedPaths,omitempty"`
// All claims as map for generic access
Claims map[string]any `json:"claims"`
// Raw credential JSON
RawCredential json.RawMessage `json:"rawCredential"`
}
VC20VerificationResult contains the result of W3C VC verification.
func (*VC20VerificationResult) GetClaims ¶
func (r *VC20VerificationResult) GetClaims() map[string]any
GetClaims returns all claims from the credential.
func (*VC20VerificationResult) GetCredentialSubject ¶
func (r *VC20VerificationResult) GetCredentialSubject() map[string]any
GetCredentialSubject returns the credential subject claims.
type VPBuildOptions ¶
type VPBuildOptions struct {
// HolderDID is the DID of the presentation holder
HolderDID string
// VerificationMethod is the full verification method URI (e.g., did:key:z6Mk...#key-1)
VerificationMethod string
// Nonce is the challenge/nonce from the verifier (required for OpenID4VP)
Nonce string
// Domain is the audience/domain for the presentation
Domain string
// Cryptosuite specifies which cryptosuite to use for signing
// Supported: "ecdsa-rdfc-2019", "eddsa-rdfc-2022"
Cryptosuite string
// Created timestamp for the proof (defaults to now)
Created time.Time
}
VPBuildOptions contains options for building a Verifiable Presentation.
type VPBuilder ¶
type VPBuilder struct {
// contains filtered or unexported fields
}
VPBuilder builds W3C Verifiable Presentations for OpenID4VP flows.
func NewVPBuilder ¶
func NewVPBuilder(opts ...VPBuilderOption) *VPBuilder
NewVPBuilder creates a new VPBuilder with the given options.
func (*VPBuilder) BuildVC20Presentation ¶
func (b *VPBuilder) BuildVC20Presentation( credentials [][]byte, privateKey crypto.PrivateKey, opts *VPBuildOptions, ) ([]byte, error)
BuildVC20Presentation creates a signed W3C Verifiable Presentation. The credentials parameter should be JSON bytes of W3C VC 2.0 credentials. The privateKey should match the cryptosuite (ed25519.PrivateKey for EdDSA, *ecdsa.PrivateKey for ECDSA).
type VPBuilderOption ¶
type VPBuilderOption func(*VPBuilder)
VPBuilderOption configures a VPBuilder.
func WithDefaultCryptosuite ¶
func WithDefaultCryptosuite(suite string) VPBuilderOption
WithDefaultCryptosuite sets the default cryptosuite for VP signing.
func WithHolderDID ¶
func WithHolderDID(did string) VPBuilderOption
WithHolderDID sets the holder DID for presentations.
type VPFormatsSupported ¶
type VPFormatsSupported struct {
// LDPVC is the configuration for W3C VC Data Integrity format (ldp_vc)
LDPVC *LDPVCFormat `json:"ldp_vc,omitempty" yaml:"ldp_vc,omitempty"`
// JWTVCJson is the configuration for JWT-based W3C VC format (jwt_vc_json)
JWTVCJson *JWTVCFormat `json:"jwt_vc_json,omitempty" yaml:"jwt_vc_json,omitempty"`
// SDJWT is the configuration for SD-JWT VC format (dc+sd-jwt)
SDJWT *SDJWTVCFormat `json:"dc+sd-jwt,omitempty" yaml:"dc+sd-jwt,omitempty"`
// MsoMdoc is the configuration for ISO mdoc format (mso_mdoc)
MsoMdoc *MsoMdocFormat `json:"mso_mdoc,omitempty" yaml:"mso_mdoc,omitempty"`
}
VPFormatsSupported defines format-specific parameters for Verifier or Wallet metadata. Used in client_metadata and Wallet metadata to indicate supported formats and algorithms.
func NewVC20VPFormatsSupported ¶
func NewVC20VPFormatsSupported(cryptosuites []string) VPFormatsSupported
NewVC20VPFormatsSupported creates a VPFormatsSupported configuration for W3C VC 2.0 Data Integrity format with the specified cryptosuites.
type VPResponse ¶
type VPResponse struct {
VPToken map[string][]string `json:"vp_token,omitempty" bson:"vp_token" validate:"required"`
State string `json:"state,omitempty" bson:"state" validate:"required"`
}
func (*VPResponse) UnmarshalJSON ¶ added in v0.6.2
func (v *VPResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON implements custom JSON unmarshaling for VPResponse. Handles the different vp_token formats wallets may send:
- DCQL object: {"credential_id": "token"} or {"credential_id": ["token1", "token2"]}
- Single string: "token" (mapped to first scope or "_default" key)
type VPTokenValidator ¶
type VPTokenValidator struct {
// Nonce from the Authorization Request
Nonce string
// ClientID (or Origin for DC API)
ClientID string
// ValidateFormat enables SD-JWT format validation (not cryptographic verification).
// This only validates that the JWT structure is parseable — it does NOT verify
// the actual cryptographic signature. Use TrustEvaluator for signature verification.
ValidateFormat bool
// CheckRevocation enables revocation status checks
CheckRevocation bool
// DCQLQuery is the original DCQL query from the request
DCQLQuery *DCQL
// TransactionData from the Authorization Request (raw base64url strings). When non-empty, the validator checks that the KB-JWT contains matching transaction_data_hashes.
TransactionData []string
}
VPTokenValidator validates VP Token according to Section 8.6 NOTE: This validator performs format and protocol validation only. Cryptographic signature verification must be done separately by the caller (e.g. via jwt.Parse with a resolved key). TrustEvaluator handles trust/policy evaluation — it does NOT perform signature verification itself.
func (*VPTokenValidator) Validate ¶
func (v *VPTokenValidator) Validate(vpToken string) error
Validate validates the VP Token according to OpenID4VP spec Section 8.6
type VerificationFailedError ¶
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
err := &openid4vp.VerificationFailedError{
Step: "signature_verification",
Err: fmt.Errorf("invalid signature"),
}
fmt.Println(err.Error())
}
Output: verification failed on 'signature_verification': invalid signature
func (*VerificationFailedError) Error ¶
func (e *VerificationFailedError) Error() string
func (*VerificationFailedError) Unwrap ¶
func (e *VerificationFailedError) Unwrap() error
type VerificationRejectedError ¶
Example ¶
package main
import (
"fmt"
"github.com/SUNET/vc/pkg/openid4vp"
)
func main() {
err := &openid4vp.VerificationRejectedError{
Step: "trust_evaluation",
Reason: "issuer not trusted",
}
fmt.Println(err.Error())
}
Output: verification rejected on 'trust_evaluation': issuer not trusted
func (*VerificationRejectedError) Error ¶
func (e *VerificationRejectedError) Error() string
type VerifiedCredentialBinding ¶ added in v0.7.0
type VerifiedCredentialBinding struct {
// Scope is the credential query ID / scope this credential was presented for.
Scope string
// HolderKeyThumbprint is the RFC 7638 SHA-256 thumbprint of the holder's public key
// (from cnf.jwk for SD-JWT, or device key for mDoc). Empty if no key binding.
HolderKeyThumbprint string
// Claims is the verified credential claims map for attribute-based binding.
Claims map[string]any
}
VerifiedCredentialBinding holds the binding-relevant data extracted from a verified credential.
type VerifierInfo ¶
type VerifierInfo struct {
// Format: REQUIRED. A string that identifies the format of the attestation and how it is encoded. Ecosystems SHOULD use collision-resistant identifiers. Further processing of the attestation is determined by the type of the attestation, which is specified in a format-specific way.
Format string `json:"format" bson:"format" validate:"required"`
// Data: REQUIRED. An object or string containing an attestation (e.g. a JWT). The payload structure is defined on a per format level. It is at the discretion of the Wallet whether it uses the information from verifier_info. Factors that influence such Wallet's decision include, but are not limited to, trust framework the Wallet supports, specific policies defined by the Issuers or ecosystem, and profiles of this specification. If the Wallet uses information from verifier_info, the Wallet MUST validate the signature and ensure binding.
Data string `json:"data" bson:"data" validate:"required"`
// credential_ids: OPTIONAL. A non-empty array of strings each referencing a Credential requested by the Verifier for which the attestation is relevant. Each string matches the id field in a DCQL Credential Query. If omitted, the attestation is relevant to all requested Credentials.
CredentialIDS []string `json:"credential_ids,omitempty" bson:"credential_ids,omitempty" validate:"omitempty,dive,required"`
}
type ZKSystemTypeSpec ¶ added in v0.7.1
type ZKSystemTypeSpec struct {
// ID identifies this specific system+circuit combination
// (e.g. "longfellow-libzk-v1_8_1_4259_2945"). A presented ZK document's
// own `zkSystemId` (pkg/mdoc.ZkDocumentDataMdoc.ZkSystemID) is expected
// to equal one of a request's ZKSystemType[].ID entries - this is how a
// verifier confirms the wallet actually used a circuit it offered,
// rather than some other one.
ID string `json:"id" yaml:"id" validate:"required"`
// System is the ZK proof system identifier (e.g. "longfellow-libzk-v1").
// Params other than "id"/"system" are format-specific (circuit_hash,
// num_attributes, block_enc_hash, block_enc_sig for Longfellow).
System string `json:"system" yaml:"system" validate:"required"`
// Params holds every other member of the wire object, as strings.
// The json:"-"/yaml:"-" tags exist only so the default struct-tag
// reflection codecs never touch this field directly - MarshalJSON/
// UnmarshalJSON and MarshalYAML/UnmarshalYAML below fully own how
// Params is (de)serialized (flattened to the top level, not nested
// under a "params" key/property).
Params map[string]string `json:"-" yaml:"-"`
}
ZKSystemTypeSpec is one entry of a CredentialQuery's `meta.zk_system_type` array — a verifier's declaration of one ZK proof system + circuit variant it is willing to accept, mirroring multipaz's `ZkSystemSpec` wire shape (`{"id": ..., "system": ..., ...params}`, e.g. `{"id": "longfellow-libzk-v1_8_1_4259_2945", "system": "longfellow-libzk-v1", "num_attributes": 1, "circuit_hash": "...", "block_enc_hash": ..., "block_enc_sig": ...}`).
Params is a flat string->string bag (all non-id/system JSON members of the wire object). Numeric wire values (e.g. num_attributes, block_enc_hash) are carried through as their JSON text representation - callers that need a specific field as an int/int64 should parse it themselves. This mirrors the DCQL CredentialQuery model overall: format-specific "meta" properties are intentionally loosely typed at this layer.
func MatchZKSystemType ¶ added in v0.7.1
func MatchZKSystemType(requested []ZKSystemTypeSpec, presentedZkSystemID string) (*ZKSystemTypeSpec, bool)
MatchZKSystemType finds the requested ZKSystemTypeSpec whose ID matches presentedZkSystemID (the `zkSystemId` a wallet actually used, taken from the presented ZkDocumentData). Returns (spec, true) on a match.
This matches by exact ID equality, which is what the DCQL/multipaz wire convention assumes (a wallet either uses the exact circuit variant a verifier offered, in which case it should echo that same ID back, or the presentation doesn't satisfy the request at all). A stricter/looser implementation could instead re-derive a match from System + individual params (circuit_hash, num_attributes) rather than trusting the ID string verbatim - useful if a wallet's own circuit catalog ever assigns different IDs than the verifier's for what is otherwise the same circuit. That is not implemented here; flagging it as a known simplification.
func (ZKSystemTypeSpec) GetParam ¶ added in v0.7.1
func (z ZKSystemTypeSpec) GetParam(key string) string
GetParam returns a system-specific parameter by key, or "" if absent.
func (ZKSystemTypeSpec) MarshalJSON ¶ added in v0.7.1
func (z ZKSystemTypeSpec) MarshalJSON() ([]byte, error)
MarshalJSON implements custom JSON marshaling for ZKSystemTypeSpec. The wire format is a single flat object (`{"id":..,"system":..,<params>}`), not a nested "params" object, mirroring multipaz's own ZkSystemSpec JSON shape - so Params is flattened back to the top level here.
func (ZKSystemTypeSpec) MarshalYAML ¶ added in v0.7.1
func (z ZKSystemTypeSpec) MarshalYAML() (any, error)
MarshalYAML implements gopkg.in/yaml.v2's Marshaler interface for ZKSystemTypeSpec, mirroring MarshalJSON: the config shape is a single flat mapping (`id`, `system`, plus every Params entry), not a nested "params" key.
func (*ZKSystemTypeSpec) UnmarshalJSON ¶ added in v0.7.1
func (z *ZKSystemTypeSpec) UnmarshalJSON(data []byte) error
UnmarshalJSON implements custom JSON unmarshaling for ZKSystemTypeSpec. Every member other than "id"/"system" is captured in Params. Non-string JSON values (numbers, booleans - e.g. `"num_attributes": 1`) are kept as their JSON text representation rather than rejected, since the wire format allows either.
func (*ZKSystemTypeSpec) UnmarshalYAML ¶ added in v0.7.1
func (z *ZKSystemTypeSpec) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML implements gopkg.in/yaml.v2's Unmarshaler interface for ZKSystemTypeSpec, mirroring UnmarshalJSON: every mapping key other than "id"/"system" is captured in Params (as its string form). Without this, the Params field's json:"-"/yaml:"-" tags would make it impossible for a YAML-authored config (e.g. pkg/configuration's presentation request templates, loaded via gopkg.in/yaml.v2) to ever set num_attributes/circuit_hash/etc - only a JSON request body could, since MarshalJSON/UnmarshalJSON already handle that path.
Source Files
¶
- authorization_request_methods.go
- claim_validation.go
- claims_extractor.go
- combined_binding.go
- context.go
- dcql.go
- dcql_resolve.go
- dcql_zk.go
- direct_post.go
- encryption_key_cache.go
- errors.go
- openid4vp.go
- presentation_builder.go
- qr_generator.go
- request_object.go
- request_object_cache.go
- response_parameters.go
- trust_service.go
- validator.go
- vc20_handler.go
- vc20_vp_builder.go
- verification_errors.go
- vp_types.go