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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package presexch_test
     8  
     9  import (
    10  	"crypto/sha256"
    11  	_ "embed"
    12  	"encoding/json"
    13  	"fmt"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/google/uuid"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/hyperledger/aries-framework-go/pkg/crypto/primitive/bbs12381g2pub"
    21  	"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
    22  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
    23  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
    24  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020"
    25  	"github.com/hyperledger/aries-framework-go/pkg/doc/util"
    26  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    27  )
    28  
    29  var (
    30  	//go:embed testdata/presentation_definition.json
    31  	presentationDefinition []byte
    32  
    33  	//go:embed testdata/submission_requirements_pd.json
    34  	submissionRequirementsPD []byte
    35  
    36  	//go:embed testdata/nested_submission_requirements_pd.json
    37  	nestedSubmissionRequirementsPD []byte
    38  
    39  	//go:embed testdata/university_degree.jwt
    40  	universityDegreeVC []byte
    41  
    42  	//go:embed testdata/permanent_resident_card.jwt
    43  	permanentResidentCardVC []byte
    44  
    45  	//go:embed testdata/drivers_license.jwt
    46  	driverLicenseVC []byte
    47  
    48  	//go:embed testdata/drivers_license2.jwt
    49  	driverLicenseVC2 []byte
    50  
    51  	//go:embed testdata/verified_employee.jwt
    52  	verifiedEmployeeVC []byte
    53  )
    54  
    55  const (
    56  	driversLicenseVCType = "DriversLicense"
    57  )
    58  
    59  func TestInstance_GetSubmissionRequirements(t *testing.T) {
    60  	docLoader := createTestJSONLDDocumentLoader(t)
    61  
    62  	contents := [][]byte{
    63  		universityDegreeVC,
    64  		permanentResidentCardVC,
    65  		driverLicenseVC,
    66  		driverLicenseVC2,
    67  		verifiedEmployeeVC,
    68  	}
    69  
    70  	var credentials []*verifiable.Credential
    71  
    72  	for _, credContent := range contents {
    73  		cred, credErr := verifiable.ParseCredential(credContent, verifiable.WithDisabledProofCheck(),
    74  			verifiable.WithJSONLDDocumentLoader(docLoader))
    75  		require.NoError(t, credErr)
    76  
    77  		credentials = append(credentials, cred)
    78  	}
    79  
    80  	t.Run("Success", func(t *testing.T) {
    81  		pdQuery := &presexch.PresentationDefinition{}
    82  		err := json.Unmarshal(presentationDefinition, pdQuery)
    83  		require.NoError(t, err)
    84  
    85  		requirements, err := pdQuery.MatchSubmissionRequirement(
    86  			credentials,
    87  			docLoader,
    88  		)
    89  
    90  		require.NoError(t, err)
    91  		require.Len(t, requirements, 1)
    92  
    93  		require.Len(t, requirements[0].Descriptors, 3)
    94  
    95  		for _, desc := range requirements[0].Descriptors {
    96  			if desc.ID == driversLicenseVCType {
    97  				require.Len(t, desc.MatchedVCs, 2)
    98  			}
    99  		}
   100  	})
   101  
   102  	t.Run("Success with submission requirements", func(t *testing.T) {
   103  		pdQuery := &presexch.PresentationDefinition{}
   104  		err := json.Unmarshal(submissionRequirementsPD, pdQuery)
   105  		require.NoError(t, err)
   106  
   107  		requirements, err := pdQuery.MatchSubmissionRequirement(
   108  			credentials,
   109  			docLoader,
   110  		)
   111  
   112  		require.NoError(t, err)
   113  		require.Len(t, requirements, 1)
   114  
   115  		require.Len(t, requirements[0].Descriptors, 3)
   116  
   117  		for _, desc := range requirements[0].Descriptors {
   118  			if desc.ID == driversLicenseVCType {
   119  				require.Len(t, desc.MatchedVCs, 2)
   120  			}
   121  		}
   122  	})
   123  
   124  	t.Run("Success with nested submission requirements", func(t *testing.T) {
   125  		pdQuery := &presexch.PresentationDefinition{}
   126  		err := json.Unmarshal(nestedSubmissionRequirementsPD, pdQuery)
   127  		require.NoError(t, err)
   128  
   129  		requirements, err := pdQuery.MatchSubmissionRequirement(
   130  			credentials,
   131  			docLoader,
   132  		)
   133  
   134  		require.NoError(t, err)
   135  		require.Len(t, requirements, 1)
   136  
   137  		require.Len(t, requirements[0].Descriptors, 0)
   138  		require.Len(t, requirements[0].Nested, 2)
   139  
   140  		for _, req := range requirements[0].Nested {
   141  			if req.Name == "VerifiedEmployee or Degree" {
   142  				require.Len(t, req.Descriptors, 2)
   143  			}
   144  
   145  			if req.Name == driversLicenseVCType {
   146  				require.Len(t, req.Descriptors, 1)
   147  				require.Len(t, req.Descriptors[0].MatchedVCs, 2)
   148  			}
   149  		}
   150  	})
   151  
   152  	t.Run("Limit disclosure BBS+", func(t *testing.T) {
   153  		required := presexch.Required
   154  
   155  		pd := &presexch.PresentationDefinition{
   156  			ID: uuid.New().String(),
   157  			InputDescriptors: []*presexch.InputDescriptor{{
   158  				Schema: []*presexch.Schema{{
   159  					URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
   160  				}},
   161  				ID: uuid.New().String(),
   162  				Constraints: &presexch.Constraints{
   163  					LimitDisclosure: &required,
   164  					Fields: []*presexch.Field{{
   165  						Path:   []string{"$.credentialSubject.degree.degreeSchool"},
   166  						Filter: &presexch.Filter{Type: &strFilterType},
   167  					}},
   168  				},
   169  			}},
   170  		}
   171  
   172  		vc := &verifiable.Credential{
   173  			ID: "https://issuer.oidp.uscis.gov/credentials/83627465",
   174  			Context: []string{
   175  				verifiable.ContextURI,
   176  				"https://www.w3.org/2018/credentials/examples/v1",
   177  				"https://w3id.org/security/bbs/v1",
   178  			},
   179  			Types: []string{
   180  				"VerifiableCredential",
   181  				"UniversityDegreeCredential",
   182  			},
   183  			Subject: verifiable.Subject{
   184  				ID: "did:example:b34ca6cd37bbf23",
   185  				CustomFields: map[string]interface{}{
   186  					"name":   "Jayden Doe",
   187  					"spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1",
   188  					"degree": map[string]interface{}{
   189  						"degree":       "MIT",
   190  						"degreeSchool": "MIT school",
   191  						"type":         "BachelorDegree",
   192  					},
   193  				},
   194  			},
   195  			Issued: &util.TimeWrapper{
   196  				Time: time.Now(),
   197  			},
   198  			Expired: &util.TimeWrapper{
   199  				Time: time.Now().AddDate(1, 0, 0),
   200  			},
   201  			Issuer: verifiable.Issuer{
   202  				ID: "did:example:489398593",
   203  			},
   204  			CustomFields: map[string]interface{}{
   205  				"identifier":  "83627465",
   206  				"name":        "Permanent Resident Card",
   207  				"description": "Government of Example Permanent Resident Card.",
   208  			},
   209  		}
   210  
   211  		publicKey, privateKey, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil)
   212  		require.NoError(t, err)
   213  
   214  		srcPublicKey, err := publicKey.Marshal()
   215  		require.NoError(t, err)
   216  
   217  		signer, err := newBBSSigner(privateKey)
   218  		require.NoError(t, err)
   219  
   220  		lddl := createTestJSONLDDocumentLoader(t)
   221  
   222  		require.NoError(t, vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{
   223  			SignatureType:           "BbsBlsSignature2020",
   224  			SignatureRepresentation: verifiable.SignatureProofValue,
   225  			Suite:                   bbsblssignature2020.New(suite.WithSigner(signer)),
   226  			VerificationMethod:      "did:example:123456#key1",
   227  		}, jsonld.WithDocumentLoader(lddl)))
   228  
   229  		matched, err := pd.MatchSubmissionRequirement([]*verifiable.Credential{vc}, lddl,
   230  			presexch.WithSelectiveDisclosureApply(),
   231  			presexch.WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(lddl),
   232  				verifiable.WithPublicKeyFetcher(verifiable.SingleKey(srcPublicKey, "Bls12381G2Key2020"))),
   233  		)
   234  		require.NoError(t, err)
   235  		require.Len(t, matched, 1)
   236  		require.Equal(t, 1, len(matched[0].Descriptors))
   237  		require.Equal(t, 1, len(matched[0].Descriptors[0].MatchedVCs))
   238  
   239  		matchedVC := matched[0].Descriptors[0].MatchedVCs[0]
   240  
   241  		require.Equal(t, vc.ID, matchedVC.ID)
   242  
   243  		subject := matchedVC.Subject.([]verifiable.Subject)[0]
   244  		degree := subject.CustomFields["degree"]
   245  		require.NotNil(t, degree)
   246  
   247  		degreeMap, ok := degree.(map[string]interface{})
   248  		require.True(t, ok)
   249  
   250  		require.Equal(t, "MIT school", degreeMap["degreeSchool"])
   251  		require.Equal(t, "BachelorDegree", degreeMap["type"])
   252  		require.Empty(t, degreeMap["degree"])
   253  		require.Equal(t, "did:example:b34ca6cd37bbf23", subject.ID)
   254  		require.Empty(t, subject.CustomFields["spouse"])
   255  		require.Empty(t, matchedVC.CustomFields["name"])
   256  
   257  		require.NotEmpty(t, matchedVC.Proofs)
   258  	})
   259  
   260  	t.Run("Checks schema", func(t *testing.T) {
   261  		pd := &presexch.PresentationDefinition{ID: uuid.New().String()}
   262  
   263  		vp, err := pd.MatchSubmissionRequirement(nil, nil)
   264  
   265  		require.EqualError(t, err, "presentation_definition: input_descriptors is required")
   266  		require.Nil(t, vp)
   267  	})
   268  
   269  	t.Run("Checks submission requirements (no descriptor)", func(t *testing.T) {
   270  		issuerID := uuid.New().String()
   271  
   272  		pd := &presexch.PresentationDefinition{
   273  			ID: uuid.New().String(),
   274  			SubmissionRequirements: []*presexch.SubmissionRequirement{
   275  				{
   276  					Rule: "all",
   277  					From: "A",
   278  				},
   279  				{
   280  					Rule:  "pick",
   281  					Count: 1,
   282  					FromNested: []*presexch.SubmissionRequirement{
   283  						{
   284  							Rule: "all",
   285  							From: "teenager",
   286  						},
   287  					},
   288  				},
   289  			},
   290  			InputDescriptors: []*presexch.InputDescriptor{{
   291  				ID:    uuid.New().String(),
   292  				Group: []string{"A"},
   293  				Schema: []*presexch.Schema{{
   294  					URI: verifiable.ContextURI,
   295  				}},
   296  				Constraints: &presexch.Constraints{
   297  					SubjectIsIssuer: &subIsIssuerRequired,
   298  					Fields: []*presexch.Field{{
   299  						Path: []string{"$.first_name", "$.last_name"},
   300  					}},
   301  				},
   302  			}},
   303  		}
   304  
   305  		result, err := pd.MatchSubmissionRequirement([]*verifiable.Credential{
   306  			{
   307  				Context: []string{verifiable.ContextURI},
   308  				Types:   []string{verifiable.VCType},
   309  				ID:      uuid.New().String(),
   310  				CustomFields: map[string]interface{}{
   311  					"first_name": "Jesse",
   312  				},
   313  			}, {
   314  				ID:      uuid.New().String(),
   315  				Subject: []verifiable.Subject{{ID: issuerID}},
   316  				Issuer:  verifiable.Issuer{ID: issuerID},
   317  				CustomFields: map[string]interface{}{
   318  					"first_name": "Jesse",
   319  					"last_name":  "Travis",
   320  					"age":        17,
   321  				},
   322  			},
   323  		}, docLoader)
   324  
   325  		require.EqualError(t, err, "no descriptors for from: teenager")
   326  		require.Nil(t, result)
   327  	})
   328  }