presexch

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2024 License: Apache-2.0 Imports: 21 Imported by: 16

README

Presentation Exchange

This document shows how to use Presentation Exchange by examples. Code examples can be found here.

  1. This example demonstrates predicate and limit_disclosure usage. The presentation definition below requires field age to be greater or equal to 18. Also, we have limit_disclosure=true which requires that output data is limited to the entries specified in the fields property. The predicate means that the result should be expressed as a boolean value.
{
  "id": "31e2f0f1-6b70-411d-b239-56aed5321884",
  "purpose": "To sell you a drink we need to know that you are an adult.",
  "input_descriptors": [
    {
      "id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
      "purpose": "Your age should be greater or equal to 18.",
      "schema": [
        {
          "uri": "https://www.w3.org/TR/vc-data-model/#types"
        }
      ],
      "constraints": {
        "limit_disclosure": true,
        "fields": [
          {
            "path": [
              "$.age"
            ],
            "filter": {
              "type": "integer",
              "minimum": 18
            },
            "predicate": "required"
          }
        ]
      }
    }
  ]
}

Let's say we have such a credential in our database.

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1"
  ],
  "age": 21,
  "credentialSchema": [
    {
      "id": "https://www.w3.org/TR/vc-data-model/#types"
    }
  ],
  "first_name": "Jesse",
  "id": "2dc74354-e965-4883-be5e-bfec48bf60c7",
  "issuer": "",
  "last_name": "Pinkman",
  "type": "VerifiableCredential"
}

As a result, we will have the following verifiable presentation:

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://identity.foundation/presentation-exchange/submission/v1"
  ],
  "presentation_submission": {
    "id": "accd5adf-1dbf-4ed9-9ba2-d687476126cb",
    "definition_id": "31e2f0f1-6b70-411d-b239-56aed5321884",
    "descriptor_map": [
      {
        "id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
        "format": "ldp_vp",
        "path": "$.verifiableCredential[0]"
      }
    ]
  },
  "type": [
    "VerifiablePresentation",
    "PresentationSubmission"
  ],
  "verifiableCredential": [
    {
      "@context": [
        "https://www.w3.org/2018/credentials/v1"
      ],
      "age": true,
      "credentialSchema": [
        {
          "id": "https://www.w3.org/TR/vc-data-model/#types"
        }
      ],
      "credentialSubject": null,
      "id": "2dc74354-e965-4883-be5e-bfec48bf60c7",
      "issuer": "",
      "type": "VerifiableCredential"
    }
  ]
}

As you can see the VP has a credential without first_name and last_name (because of limit_disclosure). Also, instead of age, we have a boolean value (because of predicate).

Documentation

Index

Examples

Constants

View Source
const (
	// PresentationSubmissionJSONLDContextIRI is the JSONLD context of presentation submissions.
	PresentationSubmissionJSONLDContextIRI = "https://identity.foundation/presentation-exchange/submission/v1"
	// CredentialApplicationJSONLDContextIRI is the JSONLD context of credential application
	// which also contains presentation submission details.
	CredentialApplicationJSONLDContextIRI = "https://identity.foundation/credential-manifest/application/v1"
	// PresentationSubmissionJSONLDType is the JSONLD type of presentation submissions.
	PresentationSubmissionJSONLDType = "PresentationSubmission"
	// CredentialApplicationJSONLDType is the JSONLD type of credential application.
	CredentialApplicationJSONLDType = "CredentialApplication"
)
View Source
const (

	// All rule`s value.
	All Selection = "all"
	// Pick rule`s value.
	Pick Selection = "pick"

	// Required predicate`s value.
	Required Preference = "required"
	// Preferred predicate`s value.
	Preferred Preference = "preferred"

	// FormatJWT presentation exchange format.
	FormatJWT = "jwt"
	// FormatJWTVC presentation exchange format.
	FormatJWTVC = "jwt_vc"
	// FormatJWTVP presentation exchange format.
	FormatJWTVP = "jwt_vp"
	// FormatLDP presentation exchange format.
	FormatLDP = "ldp"
	// FormatLDPVC presentation exchange format.
	FormatLDPVC = "ldp_vc"
	// FormatLDPVP presentation exchange format.
	FormatLDPVP = "ldp_vp"
)
View Source
const DefinitionJSONSchemaV1 = `` /* 13163-byte string literal not displayed */

DefinitionJSONSchemaV1 is the JSONSchema definition for PresentationDefinition. nolint:lll https://github.com/decentralized-identity/presentation-exchange/blob/9a6abc6d2b0f08b6339c9116132fa94c4c834418/test/presentation-definition/schema.json

View Source
const DefinitionJSONSchemaV2 = `` /* 7559-byte string literal not displayed */

DefinitionJSONSchemaV2 is the JSONSchema definition for PresentationDefinition. nolint:lll

Variables

View Source
var ErrNoCredentials = errors.New("credentials do not satisfy requirements")

ErrNoCredentials when any credentials do not satisfy requirements.

Functions

func ExtractArrayValuesForSDJWTV5 added in v1.2.0

func ExtractArrayValuesForSDJWTV5(obj map[string]interface{}) []string

ExtractArrayValuesForSDJWTV5 extracts array values for SD JWT V5.

Types

type Constraints

type Constraints struct {
	LimitDisclosure *Preference `json:"limit_disclosure,omitempty"`
	SubjectIsIssuer *Preference `json:"subject_is_issuer,omitempty"`
	IsHolder        []*Holder   `json:"is_holder,omitempty"`
	Fields          []*Field    `json:"fields,omitempty"`
}

Constraints describes InputDescriptor`s Constraints field.

type CwtType added in v1.2.0

type CwtType struct {
	Alg []string `json:"alg,omitempty"`
}

CwtType contains alg.

type Field

type Field struct {
	Path           []string    `json:"path,omitempty"`
	ID             string      `json:"id,omitempty"`
	Purpose        string      `json:"purpose,omitempty"`
	Filter         *Filter     `json:"filter,omitempty"`
	Predicate      *Preference `json:"predicate,omitempty"`
	IntentToRetain bool        `json:"intent_to_retain,omitempty"`
	Optional       bool        `json:"optional,omitempty"`
}

Field describes Constraints`s Fields field.

type Filter

type Filter struct {
	Type             *string                `json:"type,omitempty"`
	Format           string                 `json:"format,omitempty"`
	Pattern          string                 `json:"pattern,omitempty"`
	Minimum          StrOrInt               `json:"minimum,omitempty"`
	Maximum          StrOrInt               `json:"maximum,omitempty"`
	MinLength        int                    `json:"minLength,omitempty"`
	MaxLength        int                    `json:"maxLength,omitempty"`
	ExclusiveMinimum StrOrInt               `json:"exclusiveMinimum,omitempty"`
	ExclusiveMaximum StrOrInt               `json:"exclusiveMaximum,omitempty"`
	Const            StrOrInt               `json:"const,omitempty"`
	Enum             []StrOrInt             `json:"enum,omitempty"`
	Not              map[string]interface{} `json:"not,omitempty"`
	Contains         map[string]interface{} `json:"contains,omitempty"`
}

Filter describes filter.

type Format

type Format struct {
	Jwt   *JwtType `json:"jwt,omitempty"`
	JwtVC *JwtType `json:"jwt_vc,omitempty"`
	JwtVP *JwtType `json:"jwt_vp,omitempty"`

	Ldp   *LdpType `json:"ldp,omitempty"`
	LdpVC *LdpType `json:"ldp_vc,omitempty"`
	LdpVP *LdpType `json:"ldp_vp,omitempty"`

	CwtVC *CwtType `json:"cwt_vc,omitempty"`
	CwtVP *CwtType `json:"cwt_vp,omitempty"`
}

Format describes PresentationDefinition`s Format field.

type Holder

type Holder struct {
	FieldID   []string    `json:"field_id,omitempty"`
	Directive *Preference `json:"directive,omitempty"`
}

Holder describes Constraints`s holder object.

type InputDescriptor

type InputDescriptor struct {
	ID          string                 `json:"id,omitempty"`
	Group       []string               `json:"group,omitempty"`
	Name        string                 `json:"name,omitempty"`
	Purpose     string                 `json:"purpose,omitempty"`
	Metadata    map[string]interface{} `json:"metadata,omitempty"`
	Schema      []*Schema              `json:"schema,omitempty"`
	Constraints *Constraints           `json:"constraints,omitempty"`
	Format      *Format                `json:"format,omitempty"`
}

InputDescriptor input descriptors.

type InputDescriptorMapping

type InputDescriptorMapping struct {
	ID         string                  `json:"id,omitempty"`
	Format     string                  `json:"format,omitempty"`
	Path       string                  `json:"path,omitempty"`
	PathNested *InputDescriptorMapping `json:"path_nested,omitempty"`
}

InputDescriptorMapping maps an InputDescriptor to a verifiable credential pointed to by the JSONPath in `Path`.

type JwtType

type JwtType struct {
	Alg []string `json:"alg,omitempty"`
}

JwtType contains alg.

type LdpType

type LdpType struct {
	ProofType []string `json:"proof_type,omitempty"`
}

LdpType contains proof_type.

type MatchOption

type MatchOption func(*MatchOptions)

MatchOption is an option that sets an option for when matching.

func WithCredentialOptions

func WithCredentialOptions(options ...verifiable.CredentialOpt) MatchOption

WithCredentialOptions used when parsing the embedded credentials.

func WithDisableSchemaValidation

func WithDisableSchemaValidation() MatchOption

WithDisableSchemaValidation used to disable schema validation.

func WithMergedSubmission

func WithMergedSubmission(submission *PresentationSubmission) MatchOption

WithMergedSubmission provides a presentation submission that's external to the Presentations being matched, which contains the descriptor mapping for each Presentation.

If there are multiple Presentations, this merged submission should use the Presentation array as the JSON Path root when referencing the contained Presentations and the Credentials within.

func WithMergedSubmissionMap

func WithMergedSubmissionMap(submissionMap map[string]interface{}) MatchOption

WithMergedSubmissionMap provides a presentation submission that's external to the Presentations being matched, which contains the descriptor mapping for each Presentation. This submission is expected to be in the map[string]interface{} format used by json.Unmarshal.

If there are multiple Presentations, this merged submission should use the Presentation array as the JSON Path root when referencing the contained Presentations and the Credentials within.

type MatchOptions

type MatchOptions struct {
	CredentialOptions       []verifiable.CredentialOpt
	DisableSchemaValidation bool
	MergedSubmission        *PresentationSubmission
	MergedSubmissionMap     map[string]interface{}
}

MatchOptions is a holder of options that can set when matching a submission against definitions.

type MatchRequirementsOpt

type MatchRequirementsOpt func(opts *matchRequirementsOpts)

MatchRequirementsOpt is the MatchSubmissionRequirement option.

func WithDefaultPresentationFormat added in v1.1.2

func WithDefaultPresentationFormat(format string) MatchRequirementsOpt

WithDefaultPresentationFormat sets the default presentation format.

func WithSDBBSProofCreator added in v1.1.0

func WithSDBBSProofCreator(sdBBSProofCreator *verifiable.BBSProofCreator) MatchRequirementsOpt

WithSDBBSProofCreator used when applying selective disclosure.

func WithSDCredentialOptions

func WithSDCredentialOptions(options ...verifiable.CredentialOpt) MatchRequirementsOpt

WithSDCredentialOptions used when applying selective disclosure.

func WithSelectiveDisclosureApply

func WithSelectiveDisclosureApply() MatchRequirementsOpt

WithSelectiveDisclosureApply enables selective disclosure apply on resulting VC.

type MatchValue

type MatchValue struct {
	PresentationID string
	Credential     *verifiable.Credential
	DescriptorID   string
}

MatchValue holds a matched credential from PresentationDefinition.Match, along with the ID of the presentation that held the matched credential.

type MatchedInputDescriptor

type MatchedInputDescriptor struct {
	ID          string
	Name        string
	Purpose     string
	Constraints *Constraints
	Schemas     []*Schema
	MatchedVCs  []*verifiable.Credential
}

MatchedInputDescriptor contains information about VCs that matched an input descriptor of presentation definition.

type MatchedSubmissionRequirement

type MatchedSubmissionRequirement struct {
	Name        string
	Purpose     string
	Rule        Selection
	Count       int
	Min         int
	Max         int
	Descriptors []*MatchedInputDescriptor
	Nested      []*MatchedSubmissionRequirement
}

MatchedSubmissionRequirement contains information about VCs that matched a presentation definition.

type Preference

type Preference string

Preference can be "required" or "preferred".

type PresentationDefinition

type PresentationDefinition struct {
	// ID unique resource identifier.
	ID string `json:"id,omitempty"`
	// Name human-friendly name that describes what the Presentation Definition pertains to.
	Name string `json:"name,omitempty"`
	// Purpose describes the purpose for which the Presentation Definition’s inputs are being requested.
	Purpose string `json:"purpose,omitempty"`
	Locale  string `json:"locale,omitempty"`
	// Format is an object with one or more properties matching the registered Claim Format Designations
	// (jwt, jwt_vc, jwt_vp, etc.) to inform the Holder of the claim format configurations the Verifier can process.
	Format *Format `json:"format,omitempty"`
	// Frame is used for JSON-LD document framing.
	Frame map[string]interface{} `json:"frame,omitempty"`
	// SubmissionRequirements must conform to the Submission Requirement Format.
	// If not present, all inputs listed in the InputDescriptors array are required for submission.
	SubmissionRequirements []*SubmissionRequirement `json:"submission_requirements,omitempty"`
	InputDescriptors       []*InputDescriptor       `json:"input_descriptors,omitempty"`
}

PresentationDefinition presentation definitions (https://identity.foundation/presentation-exchange/).

func (*PresentationDefinition) CreateVP

func (pd *PresentationDefinition) CreateVP(credentials []*verifiable.Credential,
	documentLoader ld.DocumentLoader, opts ...MatchRequirementsOpt) (*verifiable.Presentation, error)

CreateVP creates verifiable presentation.

Example (LimitedDisclosureSkipsNonSDVCs)

nolint:gocyclo

required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To provide mauve alerts, we need to verify your authorization to receive mauve alert warnings.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "mauve_alert",
		Purpose: "You are authorized to receive warnings for mauve alerts.",
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{
				{
					Path: []string{"$.credentialSubject.warn_alert", "$.vc.credentialSubject.warn_alert", "$.warn_alert"},
					// Predicate: &required,
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "mauve",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

makeVC := func(id string) *verifiable.Credential {
	vc, makeErr := verifiable.CreateCredential(verifiable.CredentialContents{
		ID:      id,
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Schemas: []verifiable.TypedID{{
			ID:   "hub://did:foo:123/Collections/schema.us.gov/passport.json",
			Type: "JsonSchemaValidator2018",
		}},

		Subject: parseSubject(map[string]interface{}{
			"id":         "did:example:ebfeb1f712ebc6f1c276e12ec21",
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"warn_alert": "mauve",
		}),
	}, nil)
	if makeErr != nil {
		panic(fmt.Errorf("make vc :%w", makeErr))
	}

	return vc
}

proofCreators, _, err := testsupport.NewKMSSignersAndVerifierErr([]*testsupport.SigningKey{
	{Type: kms.ECDSAP256TypeIEEEP1363, PublicKeyID: "#key-1"},
})
if err != nil {
	panic(err)
}

jwtSrc := makeVC("http://example.edu/credentials/888")

claims, err := jwtSrc.JWTClaims(false)
if err != nil {
	panic(err)
}

credJWT, err := claims.MarshalJWSString(verifiable.ECDSASecp256r1, proofCreators[0], "#key-1")
if err != nil {
	panic(err)
}

jwtVC, err := verifiable.ParseCredential([]byte(credJWT), verifiable.WithDisabledProofCheck())
if err != nil {
	panic(err)
}

sdJWTSrc := makeVC("http://example.edu/credentials/999")

joseSig, err := jwt.NewJOSESigner(jwt.SignParameters{
	KeyID:  "#key-1",
	JWTAlg: "ES256",
}, proofCreators[0])
if err != nil {
	panic(err)
}

marshaledSDJWTVC, err := sdJWTSrc.MarshalWithDisclosure(verifiable.DiscloseAll(), verifiable.DisclosureSigner(joseSig, "#key-1"))
if err != nil {
	panic(err)
}

sdJWTVC, err := verifiable.ParseCredential([]byte(marshaledSDJWTVC), verifiable.WithDisabledProofCheck())
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP([]*verifiable.Credential{
	jwtVC,
	sdJWTVC,
	makeVC("http://example.edu/credentials/777"),
}, loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

presentedVC := vp.Credentials()[0]

vcBytes, err := json.MarshalIndent(presentedVC.ToRawJSON(), "", "\t")
if err != nil {
	panic(err)
}

vcStr := string(vcBytes)

for _, sd := range presentedVC.Contents().Subject[0].CustomFields["_sd"].([]interface{}) {
	vcStr = strings.ReplaceAll(vcStr, sd.(string), "DUMMY")
}

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

presentedJWT, err := presentedVC.ToJWTString()
if err != nil {
	panic(err)
}

vpStr := strings.ReplaceAll(string(vpBytes), presentedJWT, "DUMMY")

fmt.Println(vpStr)
fmt.Println(vcStr)
fmt.Println(prettifyDisclosures(presentedVC.SDJWTDisclosures()))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "mauve_alert",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "mauve_alert",
					"format": "jwt_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		"DUMMY"
	]
}
{
	"@context": [
		"https://www.w3.org/2018/credentials/v1"
	],
	"_sd_alg": "sha-256",
	"credentialSchema": {
		"id": "hub://did:foo:123/Collections/schema.us.gov/passport.json",
		"type": "JsonSchemaValidator2018"
	},
	"credentialSubject": {
		"_sd": [
			"DUMMY",
			"DUMMY",
			"DUMMY"
		],
		"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"
	},
	"id": "http://example.edu/credentials/999",
	"issuanceDate": "0001-01-01T00:00:00Z",
	"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
	"type": "VerifiableCredential"
}
('warn_alert': mauve)
Example (MultipleMatches)
pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.age"},
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}, {
		ID:      "first_name_descriptor",
		Purpose: "First name must be either Andrew or Jesse",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.first_name"},
				Filter: &Filter{
					Type:    &strFilterType,
					Pattern: "Andrew|Jesse",
				},
			}},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:777",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:777"}},
		CustomFields: map[string]interface{}{
			"first_name": "Andrew",
			"last_name":  "Hanks",
			"age":        25,
		},
	},
	{
		ID:      "http://example.edu/credentials/888",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:888",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:888"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			},
			{
				"id": "first_name_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "first_name_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "first_name_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "first_name_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 25,
			"credentialSubject": {
				"id": "did:example:777"
			},
			"first_name": "Andrew",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"last_name": "Hanks",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 21,
			"credentialSubject": {
				"id": "did:example:888"
			},
			"first_name": "Jesse",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"last_name": "Pinkman",
			"type": "VerifiableCredential"
		}
	]
}
Example (MultipleMatchesDisclosure)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.age"},
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}, {
		ID:      "first_name_descriptor",
		Purpose: "First name must be either Andrew or Jesse",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path: []string{"$.first_name"},
				Filter: &Filter{
					Type:    &strFilterType,
					Pattern: "Andrew|Jesse",
				},
			}},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:777",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:777"}},
		CustomFields: map[string]interface{}{
			"first_name": "Andrew",
			"last_name":  "Hanks",
			"age":        25,
		},
	},
	{
		ID:      "http://example.edu/credentials/888",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:888",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:888"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			},
			{
				"id": "first_name_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "first_name_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[2]"
				}
			},
			{
				"id": "first_name_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "first_name_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[3]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 25,
			"credentialSubject": {
				"id": "did:example:777"
			},
			"first_name": "Andrew",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"last_name": "Hanks",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 21,
			"credentialSubject": {
				"id": "did:example:888"
			},
			"first_name": "Jesse",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"last_name": "Pinkman",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": "did:example:777",
			"first_name": "Andrew",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": "did:example:888",
			"first_name": "Jesse",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"type": "VerifiableCredential"
		}
	]
}
Example (SubmissionRequirements)
pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	SubmissionRequirements: []*SubmissionRequirement{
		// {
		// 	Rule: "all",
		// 	FromNested: []*SubmissionRequirement{
		{
			Rule: "all",
			From: "A",
		},
		{
			Rule:    "pick",
			Purpose: "We need your photo to identify you.",
			Count:   1,
			FromNested: []*SubmissionRequirement{
				{
					Rule: "all",
					From: "drivers_license_image",
				},
				{
					Rule: "all",
					From: "passport_image",
				},
			},
		},
		// 	},
		// },
	},
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Group:   []string{"A"},
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.age"},
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}, {
		ID:      "drivers_license_image_descriptor",
		Group:   []string{"drivers_license_image"},
		Purpose: "We need your photo to identify you",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.photo"},
				Filter: &Filter{
					Type:   &strFilterType,
					Format: "uri",
				},
			}},
		},
	}, {
		ID:      "passport_image_descriptor",
		Group:   []string{"passport_image"},
		Purpose: "We need your image to identify you",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.image"},
				Filter: &Filter{
					Type:   &strFilterType,
					Format: "uri",
				},
			}},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:777",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:777"}},
		CustomFields: map[string]interface{}{
			"first_name": "Andrew",
			"last_name":  "Hanks",
			"image":      "http://image.com/user777",
			"age":        25,
		},
	},
	{
		ID:      "http://example.edu/credentials/888",
		Context: []string{"https://www.w3.org/2018/credentials/v1"},
		Types:   []string{"VerifiableCredential"},
		Issuer: &verifiable.Issuer{
			ID: "did:example:888",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:888"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"photo":      "http://image.com/user777",
			"age":        21,
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			},
			{
				"id": "drivers_license_image_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "drivers_license_image_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 25,
			"credentialSubject": {
				"id": "did:example:777"
			},
			"first_name": "Andrew",
			"id": "http://example.edu/credentials/777",
			"image": "http://image.com/user777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"last_name": "Hanks",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 21,
			"credentialSubject": {
				"id": "did:example:888"
			},
			"first_name": "Jesse",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"last_name": "Pinkman",
			"photo": "http://image.com/user777",
			"type": "VerifiableCredential"
		}
	]
}
Example (SubmissionRequirements2)
// TODO: this example demonstrates a bug, need to investigate
//  - create a unit test that simplifies this for investigation
//  - describe the issue for Rolson
pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "The following accreditations and clearances are required to proceed with your application.",
	SubmissionRequirements: []*SubmissionRequirement{
		{
			Rule:    "pick",
			Purpose: "We need a photo from government ID for your badge.",
			Count:   1,
			FromNested: []*SubmissionRequirement{
				{
					Rule: "all",
					From: "drivers_license_image",
				},
				{
					Rule: "all",
					From: "passport_image",
				},
			},
		},
		{
			Rule:    "all",
			Purpose: "We need to validate your flight experience.",
			FromNested: []*SubmissionRequirement{
				{
					Rule:  "pick",
					Count: 1,
					From:  "flight_training",
				},
				{
					Rule: "pick",
					Min:  1,
					From: "pilot_employment",
				},
			},
		},
	},
	InputDescriptors: []*InputDescriptor{
		{
			ID:      "drivers_license_image_descriptor",
			Group:   []string{"drivers_license_image"},
			Purpose: "We need your photo to identify you",
			Schema: []*Schema{{
				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
			}},
			Constraints: &Constraints{
				Fields: []*Field{{
					Path: []string{"$.photo"},
					Filter: &Filter{
						Type:   &strFilterType,
						Format: "uri",
					},
				}},
			},
		},
		{
			ID:      "passport_image_descriptor",
			Group:   []string{"passport_image"},
			Purpose: "We need your photo to identify you",
			Schema: []*Schema{{
				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
			}},
			Constraints: &Constraints{
				Fields: []*Field{{
					Path: []string{"$.image"},
					Filter: &Filter{
						Type:   &strFilterType,
						Format: "uri",
					},
				}},
			},
		},
		{
			ID:    "flight_training_1",
			Group: []string{"flight_training"},
			Schema: []*Schema{{
				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
			}},
			Constraints: &Constraints{
				Fields: []*Field{{
					Path: []string{"$.credentialSubject.pilot_id"},
					Filter: &Filter{
						Type: &strFilterType,
					},
				}, {
					Path: []string{"$.credentialSubject.expiry"},
					Filter: &Filter{
						Type: &strFilterType,
					},
				}},
			},
		},
		{
			ID:    "employment_private_1",
			Group: []string{"pilot_employment"},
			Schema: []*Schema{{
				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
			}},
			Constraints: &Constraints{
				Fields: []*Field{{
					Path: []string{"$.credentialSubject.pilot_id"},
					Filter: &Filter{
						Type: &strFilterType,
					},
				}, {
					Path: []string{"$.credentialSubject.employed_since"},
					Filter: &Filter{
						Type: &strFilterType,
					},
				}},
			},
		},
		{
			ID:    "employment_gov",
			Group: []string{"pilot_employment"},
			Schema: []*Schema{{
				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
			}},
			Constraints: &Constraints{
				Fields: []*Field{{
					Path: []string{"$.credentialSubject.clearance_level"},
					Filter: &Filter{
						Type: &arrFilterType,
					},
				}, {
					Path: []string{"$.credentialSubject.travel_authorization.subnational"},
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "Allow",
					},
				}},
			},
		},
	},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.dmv/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:777",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:777"}},
		CustomFields: map[string]interface{}{
			"first_name": "Andrew",
			"last_name":  "Hanks",
			"photo":      "https://image.com/user777",
			"DOB":        "5/18/86",
		},
	},
	{
		ID:      "https://example.gov/credentials/888",
		Context: []string{"https://www.w3.org/2018/credentials/v1"},
		Types:   []string{"VerifiableCredential", "GovSecureEmployeeCredential"},
		Issuer: &verifiable.Issuer{
			ID: "did:example:888",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{
			ID: "did:example:888",
			CustomFields: map[string]interface{}{
				"clearance_level": []string{"Public", "Low-Security", "Facility-Supervised"},
				"employee_id":     "2956348576547",
				"travel_authorization": map[string]interface{}{
					"subnational":           "Allow",
					"international_default": "S2-Signoff",
				},
			},
		}},
	},
	{
		ID:      "https://example.faa/credentials/123",
		Context: []string{"https://www.w3.org/2018/credentials/v1"},
		Types:   []string{"VerifiableCredential", "FlightCertificationCredential"},
		Issuer: &verifiable.Issuer{
			ID: "did:example:123",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{
			ID: "did:example:123",
			CustomFields: map[string]interface{}{
				"pilot_id":                "4358793",
				"accrediting_institution": "Nowheresville Community College",
				"licensed_vehicles":       []string{"hang_glider", "kite", "Cessna 152"},
				"expiry":                  "2027-12-30",
			},
		}},
	},
	{
		ID:      "https://example.business/credentials/employee/12345",
		Context: []string{"https://www.w3.org/2018/credentials/v1"},
		Types:   []string{"VerifiableCredential", "EmployeeCredential"},
		Issuer: &verifiable.Issuer{
			ID: "did:example:12345",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{
			ID: "did:example:12345",
			CustomFields: map[string]interface{}{
				"employed_since": "2021-07-06",
				"employed_until": "2021-07-07",
				"role":           "Management Consultant",
				"reference":      "did:example:10101",
				"pilot_id":       "4358793",
			}},
		},
	},
	{
		ID:      "https://example.co.website/credentials/employee/7",
		Context: []string{"https://www.w3.org/2018/credentials/v1"},
		Types:   []string{"VerifiableCredential", "EmployeeCredential"},
		Issuer: &verifiable.Issuer{
			ID: "did:example:7",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{
			ID: "did:example:7",
			CustomFields: map[string]interface{}{
				"employed_since": "2017-01-01",
				"employed_until": "2021-08-09",
				"role":           "Customer Relationships Manager",
				"employer":       "Chucky Pilot's Bar & Grill",
				"pilot_points":   172,
				"pilot_id":       "Bravo-7",
			}},
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "drivers_license_image_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "drivers_license_image_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "employment_private_1",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "employment_private_1",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			},
			{
				"id": "employment_private_1",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "employment_private_1",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[2]"
				}
			},
			{
				"id": "flight_training_1",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "flight_training_1",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[3]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"DOB": "5/18/86",
			"credentialSubject": {
				"id": "did:example:777"
			},
			"first_name": "Andrew",
			"id": "http://example.dmv/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"last_name": "Hanks",
			"photo": "https://image.com/user777",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"employed_since": "2021-07-06",
				"employed_until": "2021-07-07",
				"id": "did:example:12345",
				"pilot_id": "4358793",
				"reference": "did:example:10101",
				"role": "Management Consultant"
			},
			"id": "https://example.business/credentials/employee/12345",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:12345",
			"type": [
				"VerifiableCredential",
				"EmployeeCredential"
			]
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"employed_since": "2017-01-01",
				"employed_until": "2021-08-09",
				"employer": "Chucky Pilot's Bar \u0026 Grill",
				"id": "did:example:7",
				"pilot_id": "Bravo-7",
				"pilot_points": 172,
				"role": "Customer Relationships Manager"
			},
			"id": "https://example.co.website/credentials/employee/7",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:7",
			"type": [
				"VerifiableCredential",
				"EmployeeCredential"
			]
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"accrediting_institution": "Nowheresville Community College",
				"expiry": "2027-12-30",
				"id": "did:example:123",
				"licensed_vehicles": [
					"hang_glider",
					"kite",
					"Cessna 152"
				],
				"pilot_id": "4358793"
			},
			"id": "https://example.faa/credentials/123",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:123",
			"type": [
				"VerifiableCredential",
				"FlightCertificationCredential"
			]
		}
	]
}
Example (SubmissionRequirementsLimitDisclosure)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	SubmissionRequirements: []*SubmissionRequirement{
		{
			Rule: "all",
			From: "A",
		},
		{
			Rule:    "pick",
			Purpose: "We need your photo to identify you",
			Count:   1,
			FromNested: []*SubmissionRequirement{
				{
					Rule: "all",
					From: "drivers_license_image",
				},
				{
					Rule: "all",
					From: "passport_image",
				},
			},
		},
	},
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Group:   []string{"A"},
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			Fields: []*Field{{
				Path: []string{"$.age"},
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}, {
		ID:      "drivers_license_image_descriptor",
		Group:   []string{"drivers_license_image"},
		Purpose: "We need your photo to identify you",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path: []string{"$.photo"},
				Filter: &Filter{
					Type:   &strFilterType,
					Format: "uri",
				},
			}},
		},
	}, {
		ID:      "passport_image_descriptor",
		Group:   []string{"passport_image"},
		Purpose: "We need your image to identify you",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path: []string{"$.image"},
				Filter: &Filter{
					Type:   &strFilterType,
					Format: "uri",
				},
			}},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:777",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:777"}},
		CustomFields: map[string]interface{}{
			"first_name": "Andrew",
			"last_name":  "Hanks",
			"image":      "http://image.com/user777",
			"age":        25,
		},
	},
	{
		ID:      "http://example.edu/credentials/888",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:888",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:888"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"photo":      "http://image.com/user777",
			"age":        21,
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

//TODO: check why presentation some times serialize subjects as string, according to spec they should be objects
fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			},
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[1]"
				}
			},
			{
				"id": "drivers_license_image_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "drivers_license_image_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[2]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 25,
			"credentialSubject": {
				"id": "did:example:777"
			},
			"first_name": "Andrew",
			"id": "http://example.edu/credentials/777",
			"image": "http://image.com/user777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:777",
			"last_name": "Hanks",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": 21,
			"credentialSubject": {
				"id": "did:example:888"
			},
			"first_name": "Jesse",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"last_name": "Pinkman",
			"photo": "http://image.com/user777",
			"type": "VerifiableCredential"
		},
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": "did:example:888",
			"id": "http://example.edu/credentials/888",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:888",
			"photo": "http://image.com/user777",
			"type": "VerifiableCredential"
		}
	]
}
Example (V1)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path:      []string{"$.age"},
				Predicate: &required,
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": true,
			"credentialSubject": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (V1_With_LDPVC_FormatAndProof)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path:      []string{"$.age"},
				Predicate: &required,
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}},
	Format: &Format{
		LdpVC: &LdpType{ProofType: []string{"JsonWebSignature2020"}},
	},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		},
		Proofs: []verifiable.Proof{{"type": "JsonWebSignature2020"}},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vc",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": true,
			"credentialSubject": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (V1_With_LDP_FormatAndProof)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Schema: []*Schema{{
			URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
		}},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{{
				Path:      []string{"$.age"},
				Predicate: &required,
				Filter: &Filter{
					Type:    &intFilterType,
					Minimum: 18,
				},
			}},
		},
	}},
	Format: &Format{
		Ldp: &LdpType{ProofType: []string{"JsonWebSignature2020"}},
	},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}},
		CustomFields: map[string]interface{}{
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		},
		Proofs: []verifiable.Proof{{"type": "JsonWebSignature2020"}},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"age": true,
			"credentialSubject": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (V2)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{
				{
					Path:      []string{"$.credentialSubject.age", "$.vc.credentialSubject.age", "$.age"},
					Predicate: &required,
					Filter: &Filter{
						Type:    &intFilterType,
						Minimum: 18,
					},
				},
				{
					Path: []string{"$.credentialSchema[0].id", "$.credentialSchema.id", "$.vc.credentialSchema.id"},
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "hub://did:foo:123/Collections/schema.us.gov/passport.json",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Schemas: []verifiable.TypedID{{
			ID:   "hub://did:foo:123/Collections/schema.us.gov/passport.json",
			Type: "JsonSchemaValidator2018",
		}},

		Subject: parseSubject(map[string]interface{}{
			"id":         "did:example:ebfeb1f712ebc6f1c276e12ec21",
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		}),
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"age": true,
				"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"
			},
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (WithFormatInInputDescriptor)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Format: &Format{
			LdpVP: &LdpType{
				ProofType: []string{"Ed25519Signature2018"},
			},
		},
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{
				{
					Path:      []string{"$.credentialSubject.age", "$.vc.credentialSubject.age", "$.age"},
					Predicate: &required,
					Filter: &Filter{
						Type:    &intFilterType,
						Minimum: 18,
					},
				},
				{
					Path: []string{"$.credentialSchema[0].id", "$.credentialSchema.id", "$.vc.credentialSchema.id"},
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "hub://did:foo:123/Collections/schema.us.gov/passport.json",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Schemas: []verifiable.TypedID{{
			ID:   "hub://did:foo:123/Collections/schema.us.gov/passport.json",
			Type: "JsonSchemaValidator2018",
		}},

		Subject: parseSubject(map[string]interface{}{
			"id":         "did:example:ebfeb1f712ebc6f1c276e12ec21",
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		}),
		Proofs: []verifiable.Proof{
			{"type": "Ed25519Signature2018"},
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"age": true,
				"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"
			},
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (WithFrame)
vcJSON := `
	{
	 "@context": [
	   "https://www.w3.org/2018/credentials/v1",
	   "https://w3id.org/citizenship/v1",
	   "https://w3id.org/security/bbs/v1"
	 ],
	 "id": "https://issuer.oidp.uscis.gov/credentials/83627465",
	 "type": [
	   "VerifiableCredential",
	   "PermanentResidentCard"
	 ],
	 "issuer": "did:example:489398593",
	 "identifier": "83627465",
	 "name": "Permanent Resident Card",
	 "description": "Government of Example Permanent Resident Card.",
	 "issuanceDate": "2019-12-03T12:19:52Z",
	 "expirationDate": "2029-12-03T12:19:52Z",
	 "credentialSubject": {
	   "id": "did:example:b34ca6cd37bbf23",
	   "type": [
	     "PermanentResident",
	     "Person"
	   ],
	   "givenName": "JOHN",
	   "familyName": "SMITH",
	   "gender": "Male",
	   "image": "data:image/png;base64,iVBORw0KGgokJggg==",
	   "residentSince": "2015-01-01",
	   "lprCategory": "C09",
	   "lprNumber": "999-999-999",
	   "commuterClassification": "C1",
	   "birthCountry": "Bahamas",
	   "birthDate": "1958-07-17"
	 }
	}
	`

frameJSONWithMissingIssuer := `
{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://w3id.org/citizenship/v1",
    "https://w3id.org/security/bbs/v1"
  ],
  "type": ["VerifiableCredential", "PermanentResidentCard"],
  "@explicit": true,
  "identifier": {},
  "issuer": {},
  "issuanceDate": {},
  "credentialSubject": {
    "@explicit": true,
    "type": ["PermanentResident", "Person"],
    "givenName": {},
    "familyName": {},
    "gender": {},
	"birthCountry": {}
  }
}
`

frameDoc, err := jsonutil.ToMap(frameJSONWithMissingIssuer)
if err != nil {
	panic(err)
}

required := Required

pd := &PresentationDefinition{
	ID:    "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Frame: frameDoc,
	InputDescriptors: []*InputDescriptor{{
		ID: "country_descriptor",
		Constraints: &Constraints{
			Fields: []*Field{
				{
					Path:      []string{"$.credentialSubject.birthCountry", "$.vc.credentialSubject.birthCountry"},
					Predicate: &required,
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "Bahamas",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vc, err := verifiable.ParseCredential([]byte(vcJSON), verifiable.WithDisabledProofCheck(),
	verifiable.WithJSONLDDocumentLoader(loader))
if err != nil {
	panic(err)
}

pubKey, privKey, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil)
if err != nil {
	panic(err)
}

pubKeyBytes, err := pubKey.Marshal()
if err != nil {
	panic(err)
}

signVCWithBBS(privKey, vc, loader)

bbsProofCreator := &verifiable.BBSProofCreator{
	ProofDerivation:            bbs12381g2pub.New(),
	VerificationMethodResolver: testsupport.NewSingleKeyResolver("did:example:123456#key1", pubKeyBytes, "Bls12381G2Key2020", ""),
}

vp, err := pd.CreateVP([]*verifiable.Credential{vc}, loader,
	WithSDBBSProofCreator(bbsProofCreator),
	WithSDCredentialOptions(
		verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy
vp.Credentials()[0].Proofs()[0]["created"] = dummy
vp.Credentials()[0].Proofs()[0]["proofValue"] = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "country_descriptor",
				"format": "ldp_vp",
				"path": "$",
				"path_nested": {
					"id": "country_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1",
				"https://w3id.org/citizenship/v1",
				"https://w3id.org/security/bbs/v1"
			],
			"credentialSubject": {
				"birthCountry": true,
				"familyName": "SMITH",
				"gender": "Male",
				"givenName": "JOHN",
				"id": "did:example:b34ca6cd37bbf23",
				"type": [
					"Person",
					"PermanentResident"
				]
			},
			"id": "https://issuer.oidp.uscis.gov/credentials/83627465",
			"identifier": "83627465",
			"issuanceDate": "2019-12-03T12:19:52Z",
			"issuer": "did:example:489398593",
			"proof": {
				"created": "DUMMY",
				"nonce": "",
				"proofPurpose": "assertionMethod",
				"proofValue": "DUMMY",
				"type": "BbsBlsSignatureProof2020",
				"verificationMethod": "did:example:123456#key1"
			},
			"type": [
				"PermanentResidentCard",
				"VerifiableCredential"
			]
		}
	]
}
Example (With_LdpVC_Format)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	Format: &Format{
		LdpVC: &LdpType{
			ProofType: []string{"Ed25519Signature2018"},
		},
	},
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{
				{
					Path:      []string{"$.credentialSubject.age", "$.vc.credentialSubject.age", "$.age"},
					Predicate: &required,
					Filter: &Filter{
						Type:    &intFilterType,
						Minimum: 18,
					},
				},
				{
					Path: []string{"$.credentialSchema[0].id", "$.credentialSchema.id", "$.vc.credentialSchema.id"},
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "hub://did:foo:123/Collections/schema.us.gov/passport.json",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Schemas: []verifiable.TypedID{{
			ID:   "hub://did:foo:123/Collections/schema.us.gov/passport.json",
			Type: "JsonSchemaValidator2018",
		}},

		Subject: parseSubject(map[string]interface{}{
			"id":         "did:example:ebfeb1f712ebc6f1c276e12ec21",
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		}),
		Proofs: []verifiable.Proof{
			{"type": "Ed25519Signature2018"},
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp_vc",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"age": true,
				"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"
			},
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}
Example (With_Ldp_Format)
required := Required

pd := &PresentationDefinition{
	ID:      "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
	Purpose: "To sell you a drink we need to know that you are an adult.",
	Format: &Format{
		Ldp: &LdpType{
			ProofType: []string{"Ed25519Signature2018"},
		},
	},
	InputDescriptors: []*InputDescriptor{{
		ID:      "age_descriptor",
		Purpose: "Your age should be greater or equal to 18.",
		Constraints: &Constraints{
			LimitDisclosure: &required,
			Fields: []*Field{
				{
					Path:      []string{"$.credentialSubject.age", "$.vc.credentialSubject.age", "$.age"},
					Predicate: &required,
					Filter: &Filter{
						Type:    &intFilterType,
						Minimum: 18,
					},
				},
				{
					Path: []string{"$.credentialSchema[0].id", "$.credentialSchema.id", "$.vc.credentialSchema.id"},
					Filter: &Filter{
						Type:  &strFilterType,
						Const: "hub://did:foo:123/Collections/schema.us.gov/passport.json",
					},
				},
			},
		},
	}},
}

loader, err := ldtestutil.DocumentLoader()
if err != nil {
	panic(err)
}

vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{
	{
		ID:      "http://example.edu/credentials/777",
		Context: []string{verifiable.ContextURI},
		Types:   []string{verifiable.VCType},
		Issuer: &verifiable.Issuer{
			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
		},
		Issued: &utiltime.TimeWrapper{
			Time: time.Time{},
		},
		Schemas: []verifiable.TypedID{{
			ID:   "hub://did:foo:123/Collections/schema.us.gov/passport.json",
			Type: "JsonSchemaValidator2018",
		}},

		Subject: parseSubject(map[string]interface{}{
			"id":         "did:example:ebfeb1f712ebc6f1c276e12ec21",
			"first_name": "Jesse",
			"last_name":  "Pinkman",
			"age":        21,
		}),
		Proofs: []verifiable.Proof{
			{"type": "Ed25519Signature2018"},
		},
	},
}), loader, WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(loader)))
if err != nil {
	panic(err)
}

vp.ID = dummy
vp.CustomFields["presentation_submission"].(*PresentationSubmission).ID = dummy

vpBytes, err := json.MarshalIndent(vp, "", "\t")
if err != nil {
	panic(err)
}

fmt.Println(string(vpBytes))
Output:

{
	"@context": [
		"https://www.w3.org/2018/credentials/v1",
		"https://identity.foundation/presentation-exchange/submission/v1"
	],
	"id": "DUMMY",
	"presentation_submission": {
		"id": "DUMMY",
		"definition_id": "c1b88ce1-8460-4baf-8f16-4759a2f055fd",
		"descriptor_map": [
			{
				"id": "age_descriptor",
				"format": "ldp",
				"path": "$",
				"path_nested": {
					"id": "age_descriptor",
					"format": "ldp_vc",
					"path": "$.verifiableCredential[0]"
				}
			}
		]
	},
	"type": [
		"VerifiablePresentation",
		"PresentationSubmission"
	],
	"verifiableCredential": [
		{
			"@context": [
				"https://www.w3.org/2018/credentials/v1"
			],
			"credentialSubject": {
				"age": true,
				"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"
			},
			"id": "http://example.edu/credentials/777",
			"issuanceDate": "0001-01-01T00:00:00Z",
			"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
			"type": "VerifiableCredential"
		}
	]
}

func (*PresentationDefinition) CreateVPArray

func (pd *PresentationDefinition) CreateVPArray(
	credentials []*verifiable.Credential,
	documentLoader ld.DocumentLoader,
	opts ...MatchRequirementsOpt,
) ([]*verifiable.Presentation, *PresentationSubmission, error)

CreateVPArray creates a list of verifiable presentations, with one presentation for each provided credential. A PresentationSubmission is returned alongside, which uses the presentation list as the root for json paths.

func (*PresentationDefinition) Match

func (pd *PresentationDefinition) Match(vpList []*verifiable.Presentation,
	contextLoader ld.DocumentLoader, options ...MatchOption) ([]*MatchValue, error)

Match returns the credentials matched against the InputDescriptors ids.

Example

Example of a Verifier verifying the presentation submission of a Holder.

// verifier sends their presentation definitions to the holder
verifierDefinitions := &PresentationDefinition{
	InputDescriptors: []*InputDescriptor{
		{
			ID: "banking",
			Schema: []*Schema{{
				URI: "https://example.org/examples#Customer",
			}},
		},
		{
			ID: "residence",
			Schema: []*Schema{{
				URI: "https://example.org/examples#Street",
			}},
		},
	},
}

// holder fetches their credentials
accountCredential := fetchVC([]string{"https://example.context.jsonld/account"}, []string{"Customer"})
addressCredential := fetchVC([]string{"https://example.context.jsonld/address"}, []string{"Street"})

// holder builds their presentation submission against the verifier's definitions
vp, err := newPresentationSubmission(
	&PresentationSubmission{DescriptorMap: []*InputDescriptorMapping{
		{
			ID:   "banking",
			Path: "$.verifiableCredential[0]",
		},
		{
			ID:   "residence",
			Path: "$.verifiableCredential[1]",
		},
	}},
	accountCredential, addressCredential,
)
if err != nil {
	panic(err)
}

// holder sends VP over the wire to the verifier
vpBytes, err := json.Marshal(vp)
if err != nil {
	panic(err)
}

// load json-ld context
loader, err := ldtestutil.DocumentLoader(
	ldcontext.Document{
		URL:     "https://example.context.jsonld/account",
		Content: []byte(exampleJSONLDContext),
	},
	ldcontext.Document{
		URL:     "https://example.context.jsonld/address",
		Content: []byte(exampleJSONLDContext),
	},
)
if err != nil {
	panic(err)
}

// verifier parses the vp
// note: parsing this VP without verifying the proof just for example purposes.
//       Always verify proofs in production!
receivedVP, err := verifiable.ParsePresentation(vpBytes,
	verifiable.WithPresDisabledProofCheck(),
	verifiable.WithPresJSONLDDocumentLoader(loader))
if err != nil {
	panic(err)
}

// verifier matches the received VP against their definitions
matched, err := verifierDefinitions.Match(
	[]*verifiable.Presentation{receivedVP}, loader,
	WithCredentialOptions(
		verifiable.WithDisabledProofCheck(),
		verifiable.WithJSONLDDocumentLoader(loader)),
)
if err != nil {
	panic(fmt.Errorf("presentation submission did not match definitions: %w", err))
}

for _, descriptor := range verifierDefinitions.InputDescriptors {
	for _, match := range matched {
		if match.DescriptorID == descriptor.ID {
			fmt.Printf(
				"verifier received the '%s' credential for the input descriptor id '%s'\n",
				match.Credential.Contents().Context[1], descriptor.ID)
		}
	}
}
Output:

verifier received the 'https://example.context.jsonld/account' credential for the input descriptor id 'banking'
verifier received the 'https://example.context.jsonld/address' credential for the input descriptor id 'residence'
Example (Jwt_vp_path_workaround)
// verifier sends their presentation definitions to the holder
verifierDefinitions := &PresentationDefinition{
	InputDescriptors: []*InputDescriptor{
		{
			ID: "banking",
			Schema: []*Schema{{
				URI: "https://example.org/examples#Customer",
			}},
		},
	},
}

vc := fetchVC([]string{"https://example.context.jsonld/account"}, []string{"Customer"})

// holder builds their presentation submission against the verifier's definitions
vp, err := newPresentationSubmission(
	&PresentationSubmission{DescriptorMap: []*InputDescriptorMapping{
		{
			ID:   "banking",
			Path: "$.verifiableCredential[0]", // use invalid path (missing .vp) to demonstrate the workaround
		},
	}},
	vc,
)
if err != nil {
	panic(err)
}

claims, err := vp.JWTClaims([]string{""}, false)
if err != nil {
	panic(err)
}

unsecuredJWT, err := claims.MarshalUnsecuredJWT()
if err != nil {
	panic(err)
}

vp.JWT = unsecuredJWT

// holder sends VP over the wire to the verifier
vpBytes, err := json.Marshal(vp)
if err != nil {
	panic(err)
}

// load json-ld context
loader, err := ldtestutil.DocumentLoader(
	ldcontext.Document{
		URL:     "https://example.context.jsonld/account",
		Content: []byte(exampleJSONLDContext),
	},
)
if err != nil {
	panic(err)
}

receivedVP, err := verifiable.ParsePresentation(vpBytes,
	verifiable.WithPresDisabledProofCheck(),
	verifiable.WithPresJSONLDDocumentLoader(loader),
)
if err != nil {
	panic(err)
}

// verifier matches the received VP against their definitions
matched, err := verifierDefinitions.Match(
	[]*verifiable.Presentation{receivedVP}, loader,
	WithCredentialOptions(
		verifiable.WithDisabledProofCheck(),
		verifiable.WithJSONLDDocumentLoader(loader)),
)
if err != nil {
	panic(fmt.Errorf("presentation submission did not match definitions: %w", err))
}

for _, descriptor := range verifierDefinitions.InputDescriptors {
	for _, match := range matched {
		if match.DescriptorID == descriptor.ID {
			fmt.Printf(
				"verifier received the '%s' credential for the input descriptor id '%s'\n",
				match.Credential.Contents().Context[1], descriptor.ID)
		}
	}
}
Output:

verifier received the 'https://example.context.jsonld/account' credential for the input descriptor id 'banking'

func (*PresentationDefinition) MatchSubmissionRequirement

func (pd *PresentationDefinition) MatchSubmissionRequirement(credentials []*verifiable.Credential,
	documentLoader ld.DocumentLoader, opts ...MatchRequirementsOpt) ([]*MatchedSubmissionRequirement, error)

MatchSubmissionRequirement return information about matching VCs.

func (*PresentationDefinition) ValidateSchema

func (pd *PresentationDefinition) ValidateSchema() error

ValidateSchema validates presentation definition.

type PresentationSubmission

type PresentationSubmission struct {
	// ID unique resource identifier.
	ID     string `json:"id,omitempty"`
	Locale string `json:"locale,omitempty"`
	// DefinitionID links the submission to its definition and must be the id value of a valid Presentation Definition.
	DefinitionID  string                    `json:"definition_id,omitempty"`
	DescriptorMap []*InputDescriptorMapping `json:"descriptor_map"`
}

PresentationSubmission is the container for the descriptor_map: https://identity.foundation/presentation-exchange/#presentation-submission.

type Schema

type Schema struct {
	URI      string `json:"uri,omitempty"`
	Required bool   `json:"required,omitempty"`
}

Schema input descriptor schema.

type Selection

type Selection string

Selection can be "all" or "pick".

type StrOrInt

type StrOrInt interface{}

StrOrInt type that defines string or integer.

type SubmissionRequirement

type SubmissionRequirement struct {
	Name       string                   `json:"name,omitempty"`
	Purpose    string                   `json:"purpose,omitempty"`
	Rule       Selection                `json:"rule,omitempty"`
	Count      int                      `json:"count,omitempty"`
	Min        int                      `json:"min,omitempty"`
	Max        int                      `json:"max,omitempty"`
	From       string                   `json:"from,omitempty"`
	FromNested []*SubmissionRequirement `json:"from_nested,omitempty"`
}

SubmissionRequirement describes input that must be submitted via a Presentation Submission to satisfy Verifier demands.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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