github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/presexch/definition.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package presexch
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"sort"
    15  	"strings"
    16  
    17  	"github.com/PaesslerAG/jsonpath"
    18  	"github.com/google/uuid"
    19  	jsonpathkeys "github.com/kawamuray/jsonpath"
    20  	"github.com/piprate/json-gold/ld"
    21  	"github.com/tidwall/gjson"
    22  	"github.com/tidwall/sjson"
    23  	"github.com/xeipuuv/gojsonschema"
    24  
    25  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    26  	"github.com/hyperledger/aries-framework-go/pkg/doc/jose"
    27  	"github.com/hyperledger/aries-framework-go/pkg/doc/jwt"
    28  	"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common"
    29  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    30  )
    31  
    32  const (
    33  
    34  	// All rule`s value.
    35  	All Selection = "all"
    36  	// Pick rule`s value.
    37  	Pick Selection = "pick"
    38  
    39  	// Required predicate`s value.
    40  	Required Preference = "required"
    41  	// Preferred predicate`s value.
    42  	Preferred Preference = "preferred"
    43  
    44  	tmpEnding = "tmp_unique_id_"
    45  
    46  	credentialSchema = "credentialSchema"
    47  
    48  	// FormatJWT presentation exchange format.
    49  	FormatJWT = "jwt"
    50  	// FormatJWTVC presentation exchange format.
    51  	FormatJWTVC = "jwt_vc"
    52  	// FormatJWTVP presentation exchange format.
    53  	FormatJWTVP = "jwt_vp"
    54  	// FormatLDP presentation exchange format.
    55  	FormatLDP = "ldp"
    56  	// FormatLDPVC presentation exchange format.
    57  	FormatLDPVC = "ldp_vc"
    58  	// FormatLDPVP presentation exchange format.
    59  	FormatLDPVP = "ldp_vp"
    60  )
    61  
    62  var errPathNotApplicable = errors.New("path not applicable")
    63  
    64  var logger = log.New("doc/presexch")
    65  
    66  type (
    67  	// Selection can be "all" or "pick".
    68  	Selection string
    69  	// Preference can be "required" or "preferred".
    70  	Preference string
    71  	// StrOrInt type that defines string or integer.
    72  	StrOrInt interface{}
    73  )
    74  
    75  func (v *Preference) isRequired() bool {
    76  	if v == nil {
    77  		return false
    78  	}
    79  
    80  	return *v == Required
    81  }
    82  
    83  // Format describes PresentationDefinition`s Format field.
    84  type Format struct {
    85  	Jwt   *JwtType `json:"jwt,omitempty"`
    86  	JwtVC *JwtType `json:"jwt_vc,omitempty"`
    87  	JwtVP *JwtType `json:"jwt_vp,omitempty"`
    88  	Ldp   *LdpType `json:"ldp,omitempty"`
    89  	LdpVC *LdpType `json:"ldp_vc,omitempty"`
    90  	LdpVP *LdpType `json:"ldp_vp,omitempty"`
    91  }
    92  
    93  func (f *Format) notNil() bool {
    94  	return f != nil &&
    95  		(f.Jwt != nil || f.JwtVC != nil || f.JwtVP != nil || f.Ldp != nil || f.LdpVC != nil || f.LdpVP != nil)
    96  }
    97  
    98  // JwtType contains alg.
    99  type JwtType struct {
   100  	Alg []string `json:"alg,omitempty"`
   101  }
   102  
   103  // LdpType contains proof_type.
   104  type LdpType struct {
   105  	ProofType []string `json:"proof_type,omitempty"`
   106  }
   107  
   108  // PresentationDefinition presentation definitions (https://identity.foundation/presentation-exchange/).
   109  type PresentationDefinition struct {
   110  	// ID unique resource identifier.
   111  	ID string `json:"id,omitempty"`
   112  	// Name human-friendly name that describes what the Presentation Definition pertains to.
   113  	Name string `json:"name,omitempty"`
   114  	// Purpose describes the purpose for which the Presentation Definition’s inputs are being requested.
   115  	Purpose string `json:"purpose,omitempty"`
   116  	Locale  string `json:"locale,omitempty"`
   117  	// Format is an object with one or more properties matching the registered Claim Format Designations
   118  	// (jwt, jwt_vc, jwt_vp, etc.) to inform the Holder of the claim format configurations the Verifier can process.
   119  	Format *Format `json:"format,omitempty"`
   120  	// Frame is used for JSON-LD document framing.
   121  	Frame map[string]interface{} `json:"frame,omitempty"`
   122  	// SubmissionRequirements must conform to the Submission Requirement Format.
   123  	// If not present, all inputs listed in the InputDescriptors array are required for submission.
   124  	SubmissionRequirements []*SubmissionRequirement `json:"submission_requirements,omitempty"`
   125  	InputDescriptors       []*InputDescriptor       `json:"input_descriptors,omitempty"`
   126  }
   127  
   128  // SubmissionRequirement describes input that must be submitted via a Presentation Submission
   129  // to satisfy Verifier demands.
   130  type SubmissionRequirement struct {
   131  	Name       string                   `json:"name,omitempty"`
   132  	Purpose    string                   `json:"purpose,omitempty"`
   133  	Rule       Selection                `json:"rule,omitempty"`
   134  	Count      int                      `json:"count,omitempty"`
   135  	Min        int                      `json:"min,omitempty"`
   136  	Max        int                      `json:"max,omitempty"`
   137  	From       string                   `json:"from,omitempty"`
   138  	FromNested []*SubmissionRequirement `json:"from_nested,omitempty"`
   139  }
   140  
   141  // InputDescriptor input descriptors.
   142  type InputDescriptor struct {
   143  	ID          string                 `json:"id,omitempty"`
   144  	Group       []string               `json:"group,omitempty"`
   145  	Name        string                 `json:"name,omitempty"`
   146  	Purpose     string                 `json:"purpose,omitempty"`
   147  	Metadata    map[string]interface{} `json:"metadata,omitempty"`
   148  	Schema      []*Schema              `json:"schema,omitempty"`
   149  	Constraints *Constraints           `json:"constraints,omitempty"`
   150  	Format      *Format                `json:"format,omitempty"`
   151  }
   152  
   153  // Schema input descriptor schema.
   154  type Schema struct {
   155  	URI      string `json:"uri,omitempty"`
   156  	Required bool   `json:"required,omitempty"`
   157  }
   158  
   159  // Holder describes Constraints`s  holder object.
   160  type Holder struct {
   161  	FieldID   []string    `json:"field_id,omitempty"`
   162  	Directive *Preference `json:"directive,omitempty"`
   163  }
   164  
   165  // Constraints describes InputDescriptor`s Constraints field.
   166  type Constraints struct {
   167  	LimitDisclosure *Preference `json:"limit_disclosure,omitempty"`
   168  	SubjectIsIssuer *Preference `json:"subject_is_issuer,omitempty"`
   169  	IsHolder        []*Holder   `json:"is_holder,omitempty"`
   170  	Fields          []*Field    `json:"fields,omitempty"`
   171  }
   172  
   173  // Field describes Constraints`s Fields field.
   174  type Field struct {
   175  	Path           []string    `json:"path,omitempty"`
   176  	ID             string      `json:"id,omitempty"`
   177  	Purpose        string      `json:"purpose,omitempty"`
   178  	Filter         *Filter     `json:"filter,omitempty"`
   179  	Predicate      *Preference `json:"predicate,omitempty"`
   180  	IntentToRetain bool        `json:"intent_to_retain,omitempty"`
   181  	Optional       bool        `json:"optional,omitempty"`
   182  }
   183  
   184  // Filter describes filter.
   185  type Filter struct {
   186  	Type             *string                `json:"type,omitempty"`
   187  	Format           string                 `json:"format,omitempty"`
   188  	Pattern          string                 `json:"pattern,omitempty"`
   189  	Minimum          StrOrInt               `json:"minimum,omitempty"`
   190  	Maximum          StrOrInt               `json:"maximum,omitempty"`
   191  	MinLength        int                    `json:"minLength,omitempty"`
   192  	MaxLength        int                    `json:"maxLength,omitempty"`
   193  	ExclusiveMinimum StrOrInt               `json:"exclusiveMinimum,omitempty"`
   194  	ExclusiveMaximum StrOrInt               `json:"exclusiveMaximum,omitempty"`
   195  	Const            StrOrInt               `json:"const,omitempty"`
   196  	Enum             []StrOrInt             `json:"enum,omitempty"`
   197  	Not              map[string]interface{} `json:"not,omitempty"`
   198  	Contains         map[string]interface{} `json:"contains,omitempty"`
   199  }
   200  
   201  // MatchedSubmissionRequirement contains information about VCs that matched a presentation definition.
   202  type MatchedSubmissionRequirement struct {
   203  	Name        string
   204  	Purpose     string
   205  	Rule        Selection
   206  	Count       int
   207  	Min         int
   208  	Max         int
   209  	Descriptors []*MatchedInputDescriptor
   210  	Nested      []*MatchedSubmissionRequirement
   211  }
   212  
   213  // MatchedInputDescriptor contains information about VCs that matched an input descriptor of presentation definition.
   214  type MatchedInputDescriptor struct {
   215  	ID         string
   216  	Name       string
   217  	Purpose    string
   218  	MatchedVCs []*verifiable.Credential
   219  }
   220  
   221  // matchRequirementsOpts holds options for the MatchSubmissionRequirement.
   222  type matchRequirementsOpts struct {
   223  	applySelectiveDisclosure bool
   224  	credOpts                 []verifiable.CredentialOpt
   225  }
   226  
   227  // MatchRequirementsOpt is the MatchSubmissionRequirement option.
   228  type MatchRequirementsOpt func(opts *matchRequirementsOpts)
   229  
   230  // WithSelectiveDisclosureApply enables selective disclosure apply on resulting VC.
   231  func WithSelectiveDisclosureApply() MatchRequirementsOpt {
   232  	return func(opts *matchRequirementsOpts) {
   233  		opts.applySelectiveDisclosure = true
   234  	}
   235  }
   236  
   237  // WithSDCredentialOptions used when applying selective disclosure.
   238  func WithSDCredentialOptions(options ...verifiable.CredentialOpt) MatchRequirementsOpt {
   239  	return func(opts *matchRequirementsOpts) {
   240  		opts.credOpts = options
   241  	}
   242  }
   243  
   244  // ValidateSchema validates presentation definition.
   245  func (pd *PresentationDefinition) ValidateSchema() error {
   246  	result, err := gojsonschema.Validate(
   247  		gojsonschema.NewStringLoader(DefinitionJSONSchemaV1),
   248  		gojsonschema.NewGoLoader(struct {
   249  			PD *PresentationDefinition `json:"presentation_definition"`
   250  		}{PD: pd}),
   251  	)
   252  
   253  	if err != nil || !result.Valid() {
   254  		result, err = gojsonschema.Validate(
   255  			gojsonschema.NewStringLoader(DefinitionJSONSchemaV2),
   256  			gojsonschema.NewGoLoader(struct {
   257  				PD *PresentationDefinition `json:"presentation_definition"`
   258  			}{PD: pd}),
   259  		)
   260  	}
   261  
   262  	if err != nil {
   263  		return err
   264  	}
   265  
   266  	if result.Valid() {
   267  		return nil
   268  	}
   269  
   270  	resultErrors := result.Errors()
   271  
   272  	errs := make([]string, len(resultErrors))
   273  	for i := range resultErrors {
   274  		errs[i] = resultErrors[i].String()
   275  	}
   276  
   277  	return errors.New(strings.Join(errs, ","))
   278  }
   279  
   280  type constraintsFilterResult struct {
   281  	credential    *verifiable.Credential
   282  	credentialSrc []byte
   283  	constraints   *Constraints
   284  }
   285  
   286  type requirement struct {
   287  	Name             string
   288  	Purpose          string
   289  	Rule             Selection
   290  	Count            int
   291  	Min              int
   292  	Max              int
   293  	InputDescriptors []*InputDescriptor
   294  	Nested           []*requirement
   295  }
   296  
   297  func (r *requirement) isLenApplicable(val int) bool {
   298  	if r.Count > 0 && val != r.Count {
   299  		return false
   300  	}
   301  
   302  	if r.Min > 0 && r.Min > val {
   303  		return false
   304  	}
   305  
   306  	if r.Max > 0 && r.Max < val {
   307  		return false
   308  	}
   309  
   310  	return true
   311  }
   312  
   313  func contains(data []string, e string) bool {
   314  	for _, el := range data {
   315  		if el == e {
   316  			return true
   317  		}
   318  	}
   319  
   320  	return false
   321  }
   322  
   323  func toRequirement(sr *SubmissionRequirement, descriptors []*InputDescriptor) (*requirement, error) {
   324  	var (
   325  		inputDescriptors []*InputDescriptor
   326  		nested           []*requirement
   327  	)
   328  
   329  	var totalCount int
   330  
   331  	if sr.From != "" {
   332  		for _, descriptor := range descriptors {
   333  			if contains(descriptor.Group, sr.From) {
   334  				inputDescriptors = append(inputDescriptors, descriptor)
   335  			}
   336  		}
   337  
   338  		totalCount = len(inputDescriptors)
   339  		if totalCount == 0 {
   340  			return nil, fmt.Errorf("no descriptors for from: %s", sr.From)
   341  		}
   342  	} else {
   343  		for _, sReq := range sr.FromNested {
   344  			req, err := toRequirement(sReq, descriptors)
   345  			if err != nil {
   346  				return nil, err
   347  			}
   348  			nested = append(nested, req)
   349  		}
   350  
   351  		totalCount = len(nested)
   352  	}
   353  
   354  	count := sr.Count
   355  
   356  	if sr.Rule == All {
   357  		count = totalCount
   358  	}
   359  
   360  	return &requirement{
   361  		Name:             sr.Name,
   362  		Purpose:          sr.Purpose,
   363  		Rule:             sr.Rule,
   364  		Count:            count,
   365  		Min:              sr.Min,
   366  		Max:              sr.Max,
   367  		InputDescriptors: inputDescriptors,
   368  		Nested:           nested,
   369  	}, nil
   370  }
   371  
   372  func makeRequirement(requirements []*SubmissionRequirement, descriptors []*InputDescriptor) (*requirement, error) {
   373  	if len(requirements) == 0 {
   374  		return &requirement{
   375  			Count:            len(descriptors),
   376  			InputDescriptors: descriptors,
   377  		}, nil
   378  	}
   379  
   380  	req := &requirement{
   381  		Count: len(requirements),
   382  	}
   383  
   384  	for _, submissionRequirement := range requirements {
   385  		r, err := toRequirement(submissionRequirement, descriptors)
   386  		if err != nil {
   387  			return nil, err
   388  		}
   389  
   390  		req.Nested = append(req.Nested, r)
   391  	}
   392  
   393  	return req, nil
   394  }
   395  
   396  // CreateVP creates verifiable presentation.
   397  func (pd *PresentationDefinition) CreateVP(credentials []*verifiable.Credential,
   398  	documentLoader ld.DocumentLoader, opts ...verifiable.CredentialOpt) (*verifiable.Presentation, error) {
   399  	applicableCredentials, submission, err := presentationData(pd, credentials, documentLoader, false, opts...)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	vp, err := presentation(applicableCredentials...)
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  
   409  	vp.CustomFields = verifiable.CustomFields{
   410  		submissionProperty: submission,
   411  	}
   412  
   413  	return vp, nil
   414  }
   415  
   416  // CreateVPArray creates a list of verifiable presentations, with one presentation for each provided credential.
   417  // A PresentationSubmission is returned alongside, which uses the presentation list as the root for json paths.
   418  func (pd *PresentationDefinition) CreateVPArray(
   419  	credentials []*verifiable.Credential,
   420  	documentLoader ld.DocumentLoader,
   421  	opts ...verifiable.CredentialOpt,
   422  ) ([]*verifiable.Presentation, *PresentationSubmission, error) {
   423  	applicableCredentials, submission, err := presentationData(pd, credentials, documentLoader, true, opts...)
   424  	if err != nil {
   425  		return nil, nil, err
   426  	}
   427  
   428  	var presentations []*verifiable.Presentation
   429  
   430  	for _, credential := range applicableCredentials {
   431  		vp, e := presentation(credential)
   432  		if e != nil {
   433  			return nil, nil, e
   434  		}
   435  
   436  		presentations = append(presentations, vp)
   437  	}
   438  
   439  	return presentations, submission, nil
   440  }
   441  
   442  func presentationData(
   443  	pd *PresentationDefinition,
   444  	credentials []*verifiable.Credential,
   445  	documentLoader ld.DocumentLoader,
   446  	separatePresentations bool,
   447  	opts ...verifiable.CredentialOpt,
   448  ) ([]*verifiable.Credential, *PresentationSubmission, error) {
   449  	if err := pd.ValidateSchema(); err != nil {
   450  		return nil, nil, err
   451  	}
   452  
   453  	req, err := makeRequirement(pd.SubmissionRequirements, pd.InputDescriptors)
   454  	if err != nil {
   455  		return nil, nil, err
   456  	}
   457  
   458  	format, result, err := pd.applyRequirement(req, credentials, documentLoader, opts...)
   459  	if err != nil {
   460  		return nil, nil, err
   461  	}
   462  
   463  	applicableCredentials, descriptors := merge(format, result, separatePresentations)
   464  
   465  	submission := &PresentationSubmission{
   466  		ID:            uuid.New().String(),
   467  		DefinitionID:  pd.ID,
   468  		DescriptorMap: descriptors,
   469  	}
   470  
   471  	return applicableCredentials, submission, nil
   472  }
   473  
   474  func presentation(credentials ...*verifiable.Credential) (*verifiable.Presentation, error) {
   475  	vp, e := verifiable.NewPresentation(verifiable.WithCredentials(credentials...))
   476  	if e != nil {
   477  		return nil, e
   478  	}
   479  
   480  	vp.Context = append(vp.Context, PresentationSubmissionJSONLDContextIRI)
   481  	vp.Type = append(vp.Type, PresentationSubmissionJSONLDType)
   482  	vp.ID = uuid.NewString()
   483  
   484  	return vp, nil
   485  }
   486  
   487  func makeRequirementsForMatch(requirements []*SubmissionRequirement,
   488  	descriptors []*InputDescriptor) ([]*requirement, error) {
   489  	if len(requirements) == 0 {
   490  		return []*requirement{{
   491  			Name:             "",
   492  			Purpose:          "",
   493  			Rule:             All,
   494  			Count:            len(descriptors),
   495  			InputDescriptors: descriptors,
   496  			Nested:           nil,
   497  		}}, nil
   498  	}
   499  
   500  	var reqs []*requirement
   501  
   502  	for _, submissionRequirement := range requirements {
   503  		r, err := toRequirement(submissionRequirement, descriptors)
   504  		if err != nil {
   505  			return nil, err
   506  		}
   507  
   508  		reqs = append(reqs, r)
   509  	}
   510  
   511  	return reqs, nil
   512  }
   513  
   514  // MatchSubmissionRequirement return information about matching VCs.
   515  func (pd *PresentationDefinition) MatchSubmissionRequirement(credentials []*verifiable.Credential,
   516  	documentLoader ld.DocumentLoader, opts ...MatchRequirementsOpt) ([]*MatchedSubmissionRequirement, error) {
   517  	matchOpts := &matchRequirementsOpts{}
   518  	for _, opt := range opts {
   519  		opt(matchOpts)
   520  	}
   521  
   522  	if err := pd.ValidateSchema(); err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	requirements, err := makeRequirementsForMatch(pd.SubmissionRequirements, pd.InputDescriptors)
   527  	if err != nil {
   528  		return nil, err
   529  	}
   530  
   531  	var matchedReqs []*MatchedSubmissionRequirement
   532  
   533  	for _, req := range requirements {
   534  		matched, err := pd.matchRequirement(req, credentials, documentLoader, matchOpts)
   535  		if err != nil {
   536  			return nil, err
   537  		}
   538  
   539  		matchedReqs = append(matchedReqs, matched)
   540  	}
   541  
   542  	return matchedReqs, nil
   543  }
   544  
   545  // ErrNoCredentials when any credentials do not satisfy requirements.
   546  var ErrNoCredentials = errors.New("credentials do not satisfy requirements")
   547  
   548  // nolint: funlen,gocyclo
   549  func (pd *PresentationDefinition) matchRequirement(req *requirement, creds []*verifiable.Credential,
   550  	documentLoader ld.DocumentLoader, opts *matchRequirementsOpts) (*MatchedSubmissionRequirement, error) {
   551  	matchedReq := &MatchedSubmissionRequirement{
   552  		Name:        req.Name,
   553  		Purpose:     req.Purpose,
   554  		Rule:        req.Rule,
   555  		Count:       req.Count,
   556  		Min:         req.Min,
   557  		Max:         req.Max,
   558  		Descriptors: nil,
   559  		Nested:      nil,
   560  	}
   561  
   562  	for _, descriptor := range req.InputDescriptors {
   563  		framedCreds := creds
   564  
   565  		var err error
   566  
   567  		if opts.applySelectiveDisclosure {
   568  			framedCreds, err = frameCreds(pd.Frame, creds, opts.credOpts...)
   569  			if err != nil {
   570  				return nil, err
   571  			}
   572  		}
   573  
   574  		_, filtered, err := pd.filterCredentialsThatMatchDescriptor(
   575  			framedCreds, descriptor, documentLoader)
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  
   580  		var matchedVCs []*verifiable.Credential
   581  
   582  		if opts.applySelectiveDisclosure {
   583  			matchedVCs, err = limitDisclosure(filtered, opts.credOpts...)
   584  			if err != nil {
   585  				return nil, err
   586  			}
   587  
   588  			// TODO: remove this workaround after refactoring "merge" function to get rid of
   589  			// TODO: the trick with the modification of credential id.
   590  			for _, cred := range matchedVCs {
   591  				cred.ID = trimTmpID(cred.ID)
   592  			}
   593  		} else {
   594  			for _, credRes := range filtered {
   595  				matchedVCs = append(matchedVCs, credRes.credential)
   596  			}
   597  		}
   598  
   599  		matchedReq.Descriptors = append(matchedReq.Descriptors, &MatchedInputDescriptor{
   600  			ID:         descriptor.ID,
   601  			Name:       descriptor.Name,
   602  			Purpose:    descriptor.Purpose,
   603  			MatchedVCs: matchedVCs,
   604  		})
   605  	}
   606  
   607  	for _, nestedReq := range req.Nested {
   608  		nestedMatch, err := pd.matchRequirement(nestedReq, creds, documentLoader, opts)
   609  		if err != nil {
   610  			return nil, err
   611  		}
   612  
   613  		matchedReq.Nested = append(matchedReq.Nested, nestedMatch)
   614  	}
   615  
   616  	return matchedReq, nil
   617  }
   618  
   619  // nolint: gocyclo,funlen,gocognit
   620  func (pd *PresentationDefinition) applyRequirement(req *requirement, creds []*verifiable.Credential,
   621  	documentLoader ld.DocumentLoader,
   622  	opts ...verifiable.CredentialOpt) (string, map[string][]*verifiable.Credential, error) {
   623  	result := make(map[string][]*verifiable.Credential)
   624  	// assume LDPVP format if pd.Format is not set.
   625  	// Usually pd.Format will be set when creds include a non-empty Proofs field since they represent the designated
   626  	// format.
   627  	vpFormat := FormatLDPVP
   628  
   629  	for _, descriptor := range req.InputDescriptors {
   630  		framedCreds, err := frameCreds(pd.Frame, creds, opts...)
   631  		if err != nil {
   632  			return "", nil, err
   633  		}
   634  
   635  		descFormat, filtered, err := pd.filterCredentialsThatMatchDescriptor(
   636  			framedCreds, descriptor, documentLoader)
   637  		if err != nil {
   638  			return "", nil, err
   639  		}
   640  
   641  		if descFormat != "" {
   642  			vpFormat = descFormat
   643  		}
   644  
   645  		filteredCreds, err := limitDisclosure(filtered, opts...)
   646  		if err != nil {
   647  			return "", nil, err
   648  		}
   649  
   650  		if len(filtered) != 0 {
   651  			result[descriptor.ID] = filteredCreds
   652  		}
   653  	}
   654  
   655  	if len(req.InputDescriptors) != 0 {
   656  		if req.isLenApplicable(len(result)) {
   657  			return vpFormat, result, nil
   658  		}
   659  
   660  		return "", nil, ErrNoCredentials
   661  	}
   662  
   663  	var nestedResult []map[string][]*verifiable.Credential
   664  
   665  	// maps credential to descriptors that satisfy requirements
   666  	set := map[string]map[string]string{}
   667  
   668  	for _, r := range req.Nested {
   669  		vpFmt, res, err := pd.applyRequirement(r, creds, documentLoader, opts...)
   670  		if errors.Is(err, ErrNoCredentials) {
   671  			continue
   672  		}
   673  
   674  		if err != nil {
   675  			return "", nil, err
   676  		}
   677  
   678  		for desc, credentials := range res {
   679  			for _, cred := range credentials {
   680  				if _, ok := set[trimTmpID(cred.ID)]; !ok {
   681  					set[trimTmpID(cred.ID)] = map[string]string{}
   682  				}
   683  
   684  				set[trimTmpID(cred.ID)][desc] = cred.ID
   685  			}
   686  		}
   687  
   688  		if len(res) != 0 {
   689  			nestedResult = append(nestedResult, res)
   690  			vpFormat = vpFmt
   691  		}
   692  	}
   693  
   694  	exclude := map[string]struct{}{}
   695  
   696  	for k := range set {
   697  		if !req.isLenApplicable(len(set[k])) {
   698  			for desc, cID := range set[k] {
   699  				exclude[desc+cID] = struct{}{}
   700  			}
   701  		}
   702  	}
   703  
   704  	return vpFormat, mergeNestedResult(nestedResult, exclude), nil
   705  }
   706  
   707  func (pd *PresentationDefinition) filterCredentialsThatMatchDescriptor(creds []*verifiable.Credential,
   708  	descriptor *InputDescriptor,
   709  	documentLoader ld.DocumentLoader) (string, []constraintsFilterResult, error) {
   710  	format := pd.Format
   711  	if descriptor.Format.notNil() {
   712  		format = descriptor.Format
   713  	}
   714  
   715  	vpFormat := ""
   716  	filtered := creds
   717  
   718  	if format.notNil() {
   719  		vpFormat, filtered = filterFormat(format, filtered)
   720  	}
   721  
   722  	// Validate schema only for v1
   723  	if descriptor.Schema != nil {
   724  		filtered = filterSchema(descriptor.Schema, filtered, documentLoader)
   725  	}
   726  
   727  	filteredByConstraints, err := filterConstraints(descriptor.Constraints, filtered)
   728  	if err != nil {
   729  		return "", nil, err
   730  	}
   731  
   732  	return vpFormat, filteredByConstraints, nil
   733  }
   734  
   735  func mergeNestedResult(nr []map[string][]*verifiable.Credential,
   736  	exclude map[string]struct{}) map[string][]*verifiable.Credential {
   737  	result := make(map[string][]*verifiable.Credential)
   738  
   739  	for _, res := range nr {
   740  		for key, credentials := range res {
   741  			set := map[string]struct{}{}
   742  
   743  			var mergedCredentials []*verifiable.Credential
   744  
   745  			for _, credential := range result[key] {
   746  				if _, ok := set[credential.ID]; !ok {
   747  					mergedCredentials = append(mergedCredentials, credential)
   748  					set[credential.ID] = struct{}{}
   749  				}
   750  			}
   751  
   752  			for _, credential := range credentials {
   753  				if _, ok := set[credential.ID]; !ok {
   754  					if _, exist := exclude[key+credential.ID]; !exist {
   755  						mergedCredentials = append(mergedCredentials, credential)
   756  						set[credential.ID] = struct{}{}
   757  					}
   758  				}
   759  			}
   760  
   761  			result[key] = mergedCredentials
   762  		}
   763  	}
   764  
   765  	return result
   766  }
   767  
   768  func getSubjectIDs(subject interface{}) []string { // nolint: gocyclo
   769  	switch s := subject.(type) {
   770  	case string:
   771  		return []string{s}
   772  	case []map[string]interface{}:
   773  		var res []string
   774  
   775  		for i := range s {
   776  			v, ok := s[i]["id"]
   777  			if !ok {
   778  				continue
   779  			}
   780  
   781  			sID, ok := v.(string)
   782  			if !ok {
   783  				continue
   784  			}
   785  
   786  			res = append(res, sID)
   787  		}
   788  
   789  		return res
   790  	case map[string]interface{}:
   791  		v, ok := s["id"]
   792  		if !ok {
   793  			return nil
   794  		}
   795  
   796  		sID, ok := v.(string)
   797  		if !ok {
   798  			return nil
   799  		}
   800  
   801  		return []string{sID}
   802  	case verifiable.Subject:
   803  		return []string{s.ID}
   804  
   805  	case []verifiable.Subject:
   806  		var res []string
   807  		for i := range s {
   808  			res = append(res, s[i].ID)
   809  		}
   810  
   811  		return res
   812  	}
   813  
   814  	return nil
   815  }
   816  
   817  func subjectIsIssuer(credential *verifiable.Credential) bool {
   818  	for _, ID := range getSubjectIDs(credential.Subject) {
   819  		if ID != "" && ID == credential.Issuer.ID {
   820  			return true
   821  		}
   822  	}
   823  
   824  	return false
   825  }
   826  
   827  // nolint: gocyclo,funlen,gocognit
   828  func filterConstraints(constraints *Constraints, creds []*verifiable.Credential) ([]constraintsFilterResult, error) {
   829  	var result []constraintsFilterResult
   830  
   831  	if constraints == nil {
   832  		for _, credential := range creds {
   833  			result = append(result, constraintsFilterResult{
   834  				credential: credential,
   835  			})
   836  		}
   837  
   838  		return result, nil
   839  	}
   840  
   841  	for _, credential := range creds {
   842  		if constraints.SubjectIsIssuer.isRequired() && !subjectIsIssuer(credential) {
   843  			continue
   844  		}
   845  
   846  		var applicable bool
   847  
   848  		// if credential.JWT is set, credential will marshal to a JSON string.
   849  		// temporarily clear credential.JWT to avoid this.
   850  
   851  		var err error
   852  
   853  		credJWT := credential.JWT
   854  
   855  		credentialWithFieldValues := credential
   856  
   857  		if credential.SDJWTHashAlg != "" {
   858  			credentialWithFieldValues, err = credential.CreateDisplayCredential(verifiable.DisplayAllDisclosures())
   859  			if err != nil {
   860  				continue
   861  			}
   862  		}
   863  
   864  		// if credential.JWT is set, credential will marshal to a JSON string.
   865  		// temporarily clear credential.JWT to avoid this.
   866  		credentialWithFieldValues.JWT = ""
   867  
   868  		credentialSrc, err := json.Marshal(credentialWithFieldValues)
   869  		if err != nil {
   870  			continue
   871  		}
   872  
   873  		credentialWithFieldValues.JWT = credJWT
   874  
   875  		var credentialMap map[string]interface{}
   876  
   877  		err = json.Unmarshal(credentialSrc, &credentialMap)
   878  		if err != nil {
   879  			return nil, err
   880  		}
   881  
   882  		for i, field := range constraints.Fields {
   883  			err = filterField(field, credentialMap)
   884  			if errors.Is(err, errPathNotApplicable) {
   885  				applicable = false
   886  
   887  				break
   888  			}
   889  
   890  			if err != nil {
   891  				return nil, fmt.Errorf("filter field.%d: %w", i, err)
   892  			}
   893  
   894  			applicable = true
   895  		}
   896  
   897  		if !applicable {
   898  			continue
   899  		}
   900  
   901  		filterRes := constraintsFilterResult{
   902  			credential:    credential,
   903  			credentialSrc: credentialSrc,
   904  			constraints:   constraints,
   905  		}
   906  
   907  		result = append(result, filterRes)
   908  	}
   909  
   910  	return result, nil
   911  }
   912  
   913  // nolint: gocyclo, funlen
   914  func limitDisclosure(filterResults []constraintsFilterResult,
   915  	opts ...verifiable.CredentialOpt) ([]*verifiable.Credential, error) {
   916  	var result []*verifiable.Credential
   917  
   918  	for _, filtered := range filterResults {
   919  		credential := filtered.credential
   920  		constraints := filtered.constraints
   921  		credentialSrc := filtered.credentialSrc
   922  
   923  		if constraints == nil {
   924  			result = append(result, credential)
   925  			continue
   926  		}
   927  
   928  		var predicate bool
   929  
   930  		for _, field := range constraints.Fields {
   931  			if field.Predicate.isRequired() {
   932  				predicate = true
   933  			}
   934  		}
   935  
   936  		if (constraints.LimitDisclosure.isRequired() || predicate) && credential.SDJWTHashAlg == "" {
   937  			template := credentialSrc
   938  
   939  			var contexts []interface{}
   940  
   941  			for _, ctx := range credential.Context {
   942  				contexts = append(contexts, ctx)
   943  			}
   944  
   945  			contexts = append(contexts, credential.CustomContext...)
   946  
   947  			if constraints.LimitDisclosure.isRequired() {
   948  				var err error
   949  
   950  				template, err = json.Marshal(map[string]interface{}{
   951  					"id":                credential.ID,
   952  					"type":              credential.Types,
   953  					"@context":          contexts,
   954  					"issuer":            credential.Issuer,
   955  					"credentialSubject": toSubject(credential.Subject),
   956  					"issuanceDate":      credential.Issued,
   957  				})
   958  				if err != nil {
   959  					return nil, err
   960  				}
   961  			}
   962  
   963  			var err error
   964  
   965  			credential, err = createNewCredential(constraints, credentialSrc, template, credential, opts...)
   966  			if err != nil {
   967  				return nil, fmt.Errorf("create new credential: %w", err)
   968  			}
   969  
   970  			credential.ID = tmpID(credential.ID)
   971  		}
   972  
   973  		if constraints.LimitDisclosure.isRequired() && credential.SDJWTHashAlg != "" {
   974  			limitedDisclosures, err := getLimitedDisclosures(constraints, credentialSrc, credential)
   975  			if err != nil {
   976  				return nil, err
   977  			}
   978  
   979  			credential.SDJWTDisclosures = limitedDisclosures
   980  		}
   981  
   982  		result = append(result, credential)
   983  	}
   984  
   985  	return result, nil
   986  }
   987  
   988  // nolint: gocyclo,funlen,gocognit
   989  func getLimitedDisclosures(constraints *Constraints, displaySrc []byte, credential *verifiable.Credential) ([]*common.DisclosureClaim, error) { // nolint:lll
   990  	hash, err := common.GetCryptoHash(credential.SDJWTHashAlg)
   991  	if err != nil {
   992  		return nil, err
   993  	}
   994  
   995  	vcJWT := credential.JWT
   996  	credential.JWT = ""
   997  
   998  	credentialSrc, err := json.Marshal(credential)
   999  	if err != nil {
  1000  		return nil, err
  1001  	}
  1002  
  1003  	// revert JWT to original value
  1004  	credential.JWT = vcJWT
  1005  
  1006  	var limitedDisclosures []*common.DisclosureClaim
  1007  
  1008  	for _, f := range constraints.Fields {
  1009  		jPaths, err := getJSONPaths(f.Path, displaySrc)
  1010  		if err != nil {
  1011  			return nil, err
  1012  		}
  1013  
  1014  		for _, path := range jPaths {
  1015  			if strings.Contains(path[0], credentialSchema) {
  1016  				continue
  1017  			}
  1018  
  1019  			parentPath := ""
  1020  
  1021  			key := path[1]
  1022  
  1023  			pathParts := strings.Split(path[1], ".")
  1024  			if len(pathParts) > 1 {
  1025  				parentPath = strings.Join(pathParts[:len(pathParts)-1], ".")
  1026  				key = pathParts[len(pathParts)-1]
  1027  			}
  1028  
  1029  			parentObj, ok := gjson.GetBytes(credentialSrc, parentPath).Value().(map[string]interface{})
  1030  			if !ok {
  1031  				// no selective disclosures at this level, so nothing to add to limited disclosures
  1032  				continue
  1033  			}
  1034  
  1035  			digests, err := common.GetDisclosureDigests(parentObj)
  1036  			if err != nil {
  1037  				return nil, err
  1038  			}
  1039  
  1040  			for _, dc := range credential.SDJWTDisclosures {
  1041  				if dc.Name == key {
  1042  					digest, err := common.GetHash(hash, dc.Disclosure)
  1043  					if err != nil {
  1044  						return nil, err
  1045  					}
  1046  
  1047  					if _, ok := digests[digest]; ok {
  1048  						limitedDisclosures = append(limitedDisclosures, dc)
  1049  					}
  1050  				}
  1051  			}
  1052  		}
  1053  	}
  1054  
  1055  	return limitedDisclosures, nil
  1056  }
  1057  
  1058  func frameCreds(frame map[string]interface{}, creds []*verifiable.Credential,
  1059  	opts ...verifiable.CredentialOpt) ([]*verifiable.Credential, error) {
  1060  	if frame == nil {
  1061  		return creds, nil
  1062  	}
  1063  
  1064  	var result []*verifiable.Credential
  1065  
  1066  	for _, credential := range creds {
  1067  		bbsVC, err := credential.GenerateBBSSelectiveDisclosure(frame, nil, opts...)
  1068  		if err != nil {
  1069  			return nil, err
  1070  		}
  1071  
  1072  		result = append(result, bbsVC)
  1073  	}
  1074  
  1075  	return result, nil
  1076  }
  1077  
  1078  func toSubject(subject interface{}) interface{} {
  1079  	sub, ok := subject.([]verifiable.Subject)
  1080  	if ok && len(sub) == 1 {
  1081  		return verifiable.Subject{ID: sub[0].ID}
  1082  	}
  1083  
  1084  	return subject
  1085  }
  1086  
  1087  func tmpID(id string) string {
  1088  	return id + tmpEnding + uuid.New().String()
  1089  }
  1090  
  1091  func trimTmpID(id string) string {
  1092  	idx := strings.Index(id, tmpEnding)
  1093  	if idx == -1 {
  1094  		return id
  1095  	}
  1096  
  1097  	return id[:idx]
  1098  }
  1099  
  1100  // nolint: funlen,gocognit,gocyclo
  1101  func createNewCredential(constraints *Constraints, src, limitedCred []byte,
  1102  	credential *verifiable.Credential, opts ...verifiable.CredentialOpt) (*verifiable.Credential, error) {
  1103  	var (
  1104  		BBSSupport          = hasBBS(credential)
  1105  		modifiedByPredicate bool
  1106  		explicitPaths       = make(map[string]bool)
  1107  	)
  1108  
  1109  	for _, f := range constraints.Fields {
  1110  		jPaths, err := getJSONPaths(f.Path, src)
  1111  		if err != nil {
  1112  			return nil, err
  1113  		}
  1114  
  1115  		for _, path := range jPaths {
  1116  			if strings.Contains(path[0], credentialSchema) {
  1117  				continue
  1118  			}
  1119  
  1120  			var val interface{} = true
  1121  
  1122  			if !modifiedByPredicate {
  1123  				modifiedByPredicate = f.Predicate.isRequired()
  1124  			}
  1125  
  1126  			if f.Predicate == nil || *f.Predicate != Required {
  1127  				val = gjson.GetBytes(src, path[1]).Value()
  1128  			}
  1129  
  1130  			if constraints.LimitDisclosure.isRequired() && BBSSupport {
  1131  				chunks := strings.Split(path[0], ".")
  1132  				explicitPath := strings.Join(chunks[:len(chunks)-1], ".")
  1133  				explicitPaths[explicitPath] = true
  1134  			}
  1135  
  1136  			limitedCred, err = sjson.SetBytes(limitedCred, path[0], val)
  1137  			if err != nil {
  1138  				return nil, err
  1139  			}
  1140  		}
  1141  	}
  1142  
  1143  	if !constraints.LimitDisclosure.isRequired() || !BBSSupport || modifiedByPredicate {
  1144  		opts = append(opts, verifiable.WithDisabledProofCheck())
  1145  		return verifiable.ParseCredential(limitedCred, opts...)
  1146  	}
  1147  
  1148  	limitedCred, err := enhanceRevealDoc(explicitPaths, limitedCred, src)
  1149  	if err != nil {
  1150  		return nil, err
  1151  	}
  1152  
  1153  	var doc map[string]interface{}
  1154  	if err := json.Unmarshal(limitedCred, &doc); err != nil {
  1155  		return nil, err
  1156  	}
  1157  
  1158  	return credential.GenerateBBSSelectiveDisclosure(doc, []byte(uuid.New().String()), opts...)
  1159  }
  1160  
  1161  func getJSONPaths(keys []string, src []byte) ([][2]string, error) {
  1162  	paths, err := jsonpathkeys.ParsePaths(keys...)
  1163  	if err != nil {
  1164  		return nil, err
  1165  	}
  1166  
  1167  	eval, err := jsonpathkeys.EvalPathsInReader(bytes.NewReader(src), paths)
  1168  	if err != nil {
  1169  		return nil, err
  1170  	}
  1171  
  1172  	var jPaths [][2]string
  1173  
  1174  	set := map[string]int{}
  1175  
  1176  	for {
  1177  		result, ok := eval.Next()
  1178  		if !ok {
  1179  			break
  1180  		}
  1181  
  1182  		jPaths = append(jPaths, getPath(result.Keys, set))
  1183  	}
  1184  
  1185  	return jPaths, nil
  1186  }
  1187  
  1188  func enhanceRevealDoc(explicitPaths map[string]bool, limitedCred, vcBytes []byte) ([]byte, error) {
  1189  	var err error
  1190  
  1191  	limitedCred, err = sjson.SetBytes(limitedCred, "@explicit", true)
  1192  	if err != nil {
  1193  		return nil, err
  1194  	}
  1195  
  1196  	intermPaths := make(map[string]bool)
  1197  
  1198  	for path, fromConstraint := range explicitPaths {
  1199  		limitedCred, err = enhanceRevealField(path, limitedCred, vcBytes)
  1200  		if err != nil {
  1201  			return nil, err
  1202  		}
  1203  
  1204  		if !fromConstraint {
  1205  			continue
  1206  		}
  1207  
  1208  		pathParts := strings.Split(path, ".")
  1209  		combinedPath := ""
  1210  
  1211  		for i := 0; i < len(pathParts)-1; i++ {
  1212  			if i == 0 {
  1213  				combinedPath = pathParts[0]
  1214  			} else {
  1215  				combinedPath += "." + pathParts[i]
  1216  			}
  1217  
  1218  			if _, ok := explicitPaths[combinedPath]; !ok {
  1219  				intermPaths[combinedPath] = false
  1220  			}
  1221  		}
  1222  	}
  1223  
  1224  	for path := range intermPaths {
  1225  		limitedCred, err = enhanceRevealField(path, limitedCred, vcBytes)
  1226  		if err != nil {
  1227  			return nil, err
  1228  		}
  1229  	}
  1230  
  1231  	return limitedCred, nil
  1232  }
  1233  
  1234  func enhanceRevealField(path string, limitedCred, vcBytes []byte) ([]byte, error) {
  1235  	var err error
  1236  
  1237  	limitedCred, err = sjson.SetBytes(limitedCred, path+".@explicit", true)
  1238  	if err != nil {
  1239  		return nil, err
  1240  	}
  1241  
  1242  	for _, cf := range [...]string{"type", "@context"} {
  1243  		specialFieldPath := path + "." + cf
  1244  
  1245  		specialFieldValue := gjson.GetBytes(vcBytes, specialFieldPath)
  1246  		if specialFieldValue.Type == gjson.Null {
  1247  			continue
  1248  		}
  1249  
  1250  		limitedCred, err = sjson.SetBytes(limitedCred, specialFieldPath, specialFieldValue.Value())
  1251  		if err != nil {
  1252  			return nil, err
  1253  		}
  1254  	}
  1255  
  1256  	return limitedCred, nil
  1257  }
  1258  
  1259  func hasBBS(vc *verifiable.Credential) bool {
  1260  	for _, proof := range vc.Proofs {
  1261  		if proof["type"] == "BbsBlsSignature2020" {
  1262  			return true
  1263  		}
  1264  	}
  1265  
  1266  	return false
  1267  }
  1268  
  1269  func hasProofWithType(vc *verifiable.Credential, proofType string) bool {
  1270  	for _, proof := range vc.Proofs {
  1271  		if proof["type"] == proofType {
  1272  			return true
  1273  		}
  1274  	}
  1275  
  1276  	return false
  1277  }
  1278  
  1279  func filterField(f *Field, credential map[string]interface{}) error {
  1280  	var schema gojsonschema.JSONLoader
  1281  
  1282  	if f.Filter != nil {
  1283  		schema = gojsonschema.NewGoLoader(*f.Filter)
  1284  	}
  1285  
  1286  	var lastErr error
  1287  
  1288  	for _, path := range f.Path {
  1289  		patch, err := jsonpath.Get(path, credential)
  1290  		if err == nil {
  1291  			err = validatePatch(schema, patch)
  1292  			if err == nil {
  1293  				return nil
  1294  			}
  1295  
  1296  			lastErr = err
  1297  		} else if f.Optional {
  1298  			return nil
  1299  		} else {
  1300  			lastErr = errPathNotApplicable
  1301  		}
  1302  	}
  1303  
  1304  	return lastErr
  1305  }
  1306  
  1307  func validatePatch(schema gojsonschema.JSONLoader, patch interface{}) error {
  1308  	if schema == nil {
  1309  		return nil
  1310  	}
  1311  
  1312  	raw, err := json.Marshal(patch)
  1313  	if err != nil {
  1314  		return err
  1315  	}
  1316  
  1317  	result, err := gojsonschema.Validate(schema, gojsonschema.NewBytesLoader(raw))
  1318  	if err != nil || !result.Valid() {
  1319  		return errPathNotApplicable
  1320  	}
  1321  
  1322  	return nil
  1323  }
  1324  
  1325  func getPath(keys []interface{}, set map[string]int) [2]string {
  1326  	var (
  1327  		newPath      []string
  1328  		originalPath []string
  1329  	)
  1330  
  1331  	for _, k := range keys {
  1332  		switch v := k.(type) {
  1333  		case int:
  1334  			counterKey := strings.Join(originalPath, ".")
  1335  			originalPath = append(originalPath, fmt.Sprintf("%d", v))
  1336  			mapperKey := strings.Join(originalPath, ".")
  1337  
  1338  			if _, ok := set[mapperKey]; !ok {
  1339  				set[mapperKey] = set[counterKey]
  1340  				set[counterKey]++
  1341  			}
  1342  
  1343  			newPath = append(newPath, fmt.Sprintf("%d", set[mapperKey]))
  1344  		default:
  1345  			originalPath = append(originalPath, fmt.Sprintf("%s", v))
  1346  			newPath = append(newPath, fmt.Sprintf("%s", v))
  1347  		}
  1348  	}
  1349  
  1350  	return [...]string{strings.Join(newPath, "."), strings.Join(originalPath, ".")}
  1351  }
  1352  
  1353  func merge(
  1354  	presentationFormat string,
  1355  	setOfCredentials map[string][]*verifiable.Credential,
  1356  	separatePresentations bool,
  1357  ) ([]*verifiable.Credential, []*InputDescriptorMapping) { //nolint:lll
  1358  	setOfCreds := make(map[string]int)
  1359  	setOfDescriptors := make(map[string]struct{})
  1360  
  1361  	var (
  1362  		result      []*verifiable.Credential
  1363  		descriptors []*InputDescriptorMapping
  1364  	)
  1365  
  1366  	keys := make([]string, 0, len(setOfCredentials))
  1367  	for k := range setOfCredentials {
  1368  		keys = append(keys, k)
  1369  	}
  1370  
  1371  	sort.Strings(keys)
  1372  
  1373  	for _, descriptorID := range keys {
  1374  		credentials := setOfCredentials[descriptorID]
  1375  
  1376  		for _, credential := range credentials {
  1377  			if _, ok := setOfCreds[credential.ID]; !ok {
  1378  				credential.ID = trimTmpID(credential.ID)
  1379  				result = append(result, credential)
  1380  				setOfCreds[credential.ID] = len(result) - 1
  1381  			}
  1382  
  1383  			vcFormat := FormatLDPVC
  1384  			if credential.JWT != "" {
  1385  				vcFormat = FormatJWTVC
  1386  			}
  1387  
  1388  			if _, ok := setOfDescriptors[fmt.Sprintf("%s-%s", credential.ID, credential.ID)]; !ok {
  1389  				desc := &InputDescriptorMapping{
  1390  					ID:     descriptorID,
  1391  					Format: presentationFormat,
  1392  					PathNested: &InputDescriptorMapping{
  1393  						ID:     descriptorID,
  1394  						Format: vcFormat,
  1395  					},
  1396  				}
  1397  
  1398  				if separatePresentations {
  1399  					desc.Path = fmt.Sprintf("$[%d]", setOfCreds[credential.ID])
  1400  					desc.PathNested.Path = "$.verifiableCredential[0]"
  1401  				} else {
  1402  					desc.Path = "$"
  1403  					desc.PathNested.Path = fmt.Sprintf("$.verifiableCredential[%d]", setOfCreds[credential.ID])
  1404  				}
  1405  
  1406  				descriptors = append(descriptors, desc)
  1407  			}
  1408  		}
  1409  	}
  1410  
  1411  	sort.Sort(byID(descriptors))
  1412  
  1413  	return result, descriptors
  1414  }
  1415  
  1416  type byID []*InputDescriptorMapping
  1417  
  1418  func (a byID) Len() int           { return len(a) }
  1419  func (a byID) Less(i, j int) bool { return a[i].ID < a[j].ID }
  1420  func (a byID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  1421  
  1422  //nolint:funlen,gocyclo
  1423  func filterFormat(format *Format, credentials []*verifiable.Credential) (string, []*verifiable.Credential) {
  1424  	var ldpCreds, ldpvcCreds, ldpvpCreds, jwtCreds, jwtvcCreds, jwtvpCreds []*verifiable.Credential
  1425  
  1426  	for _, credential := range credentials {
  1427  		if credByProof(credential, format.Ldp) {
  1428  			ldpCreds = append(ldpCreds, credential)
  1429  		}
  1430  
  1431  		if credByProof(credential, format.LdpVC) {
  1432  			ldpvcCreds = append(ldpvcCreds, credential)
  1433  		}
  1434  
  1435  		if credByProof(credential, format.LdpVP) {
  1436  			ldpvpCreds = append(ldpvpCreds, credential)
  1437  		}
  1438  
  1439  		var (
  1440  			alg    string
  1441  			hasAlg bool
  1442  		)
  1443  
  1444  		if credential.JWT != "" {
  1445  			pJWT, _, err := jwt.Parse(credential.JWT, jwt.WithSignatureVerifier(&noVerifier{}))
  1446  			if err != nil {
  1447  				logger.Warnf("unmarshal credential error: %w", err)
  1448  
  1449  				continue
  1450  			}
  1451  
  1452  			alg, hasAlg = pJWT.Headers.Algorithm()
  1453  		}
  1454  
  1455  		if hasAlg && algMatch(alg, format.Jwt) {
  1456  			jwtCreds = append(jwtCreds, credential)
  1457  		}
  1458  
  1459  		if hasAlg && algMatch(alg, format.JwtVC) {
  1460  			jwtvcCreds = append(jwtvcCreds, credential)
  1461  		}
  1462  
  1463  		if hasAlg && algMatch(alg, format.JwtVP) {
  1464  			jwtvpCreds = append(jwtvpCreds, credential)
  1465  		}
  1466  	}
  1467  
  1468  	if len(ldpCreds) > 0 {
  1469  		return FormatLDP, ldpCreds
  1470  	}
  1471  
  1472  	if len(ldpvcCreds) > 0 {
  1473  		return FormatLDPVC, ldpvcCreds
  1474  	}
  1475  
  1476  	if len(ldpvpCreds) > 0 {
  1477  		return FormatLDPVP, ldpvpCreds
  1478  	}
  1479  
  1480  	if len(jwtCreds) > 0 {
  1481  		return FormatJWT, jwtCreds
  1482  	}
  1483  
  1484  	if len(jwtvcCreds) > 0 {
  1485  		return FormatJWTVC, jwtvcCreds
  1486  	}
  1487  
  1488  	if len(jwtvpCreds) > 0 {
  1489  		return FormatJWTVP, jwtvpCreds
  1490  	}
  1491  
  1492  	return "", nil
  1493  }
  1494  
  1495  // noVerifier is used when no JWT signature verification is needed.
  1496  // To be used with precaution.
  1497  type noVerifier struct{}
  1498  
  1499  func (v noVerifier) Verify(_ jose.Headers, _, _, _ []byte) error {
  1500  	return nil
  1501  }
  1502  
  1503  func algMatch(credAlg string, jwtType *JwtType) bool {
  1504  	if jwtType == nil {
  1505  		return false
  1506  	}
  1507  
  1508  	for _, b := range jwtType.Alg {
  1509  		if strings.EqualFold(credAlg, b) {
  1510  			return true
  1511  		}
  1512  	}
  1513  
  1514  	return false
  1515  }
  1516  
  1517  func credByProof(c *verifiable.Credential, ldp *LdpType) bool {
  1518  	if ldp == nil {
  1519  		return false
  1520  	}
  1521  
  1522  	for _, proofType := range ldp.ProofType {
  1523  		if hasProofWithType(c, proofType) {
  1524  			return true
  1525  		}
  1526  	}
  1527  
  1528  	return false
  1529  }
  1530  
  1531  // nolint: gocyclo
  1532  func filterSchema(schemas []*Schema, credentials []*verifiable.Credential,
  1533  	documentLoader ld.DocumentLoader) []*verifiable.Credential {
  1534  	var result []*verifiable.Credential
  1535  
  1536  	contexts := map[string]*ld.Context{}
  1537  
  1538  	for _, credential := range credentials {
  1539  		schemaSatisfied := map[string]struct{}{}
  1540  
  1541  		for _, ctx := range credential.Context {
  1542  			ctxObj, ok := contexts[ctx]
  1543  			if !ok {
  1544  				context, err := getContext(ctx, documentLoader)
  1545  				if err != nil {
  1546  					logger.Errorf("failed to load context '%s': %s", ctx, err.Error())
  1547  					return nil
  1548  				}
  1549  
  1550  				contexts[ctx] = context
  1551  				ctxObj = context
  1552  			}
  1553  
  1554  			for _, typ := range credential.Types {
  1555  				ids, err := typeFoundInContext(typ, ctxObj)
  1556  				if err != nil {
  1557  					continue
  1558  				}
  1559  
  1560  				for _, id := range ids {
  1561  					schemaSatisfied[id] = struct{}{}
  1562  				}
  1563  			}
  1564  		}
  1565  
  1566  		var applicable bool
  1567  
  1568  		for _, schema := range schemas {
  1569  			_, ok := schemaSatisfied[schema.URI]
  1570  			if ok {
  1571  				applicable = true
  1572  			} else if schema.Required {
  1573  				applicable = false
  1574  				break
  1575  			}
  1576  		}
  1577  
  1578  		if applicable {
  1579  			result = append(result, credential)
  1580  		}
  1581  	}
  1582  
  1583  	return result
  1584  }
  1585  
  1586  func typeFoundInContext(typ string, ctxObj *ld.Context) ([]string, error) {
  1587  	var out []string
  1588  
  1589  	td := ctxObj.GetTermDefinition(typ)
  1590  	if td == nil {
  1591  		return nil, nil
  1592  	}
  1593  
  1594  	id, ok := td["@id"].(string)
  1595  	if ok {
  1596  		out = append(out, id)
  1597  	}
  1598  
  1599  	tdCtx, ok := td["@context"].(map[string]interface{})
  1600  	if !ok {
  1601  		return out, nil
  1602  	}
  1603  
  1604  	extendedCtx, err := ctxObj.Parse(tdCtx)
  1605  	if err != nil {
  1606  		return nil, err
  1607  	}
  1608  
  1609  	iri, err := extendedCtx.ExpandIri(id, false, false, nil, nil)
  1610  	if err != nil {
  1611  		return nil, err
  1612  	}
  1613  
  1614  	out = append(out, iri)
  1615  
  1616  	return out, nil
  1617  }
  1618  
  1619  func getContext(contextURI string, documentLoader ld.DocumentLoader) (*ld.Context, error) {
  1620  	contextURI = strings.SplitN(contextURI, "#", 2)[0]
  1621  
  1622  	remoteDoc, err := documentLoader.LoadDocument(contextURI)
  1623  	if err != nil {
  1624  		return nil, fmt.Errorf("loading document: %w", err)
  1625  	}
  1626  
  1627  	doc, ok := remoteDoc.Document.(map[string]interface{})
  1628  	if !ok {
  1629  		return nil, fmt.Errorf("expects jsonld document to be unmarshaled into map[string]interface{}")
  1630  	}
  1631  
  1632  	ctx, ok := doc["@context"]
  1633  	if !ok {
  1634  		return nil, fmt.Errorf("@context field not found in context %s", contextURI)
  1635  	}
  1636  
  1637  	var opt *ld.JsonLdOptions
  1638  	if documentLoader != nil {
  1639  		opt = ld.NewJsonLdOptions("")
  1640  		opt.DocumentLoader = documentLoader
  1641  	}
  1642  
  1643  	activeCtx, err := ld.NewContext(nil, opt).Parse(ctx)
  1644  	if err != nil {
  1645  		return nil, err
  1646  	}
  1647  
  1648  	return activeCtx, nil
  1649  }