Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
fix: now get submission requirements can apply SD on result VCs (#3570)
Browse files Browse the repository at this point in the history
Signed-off-by: Volodymyr Kubiv <volodymyr.kubiv@euristiq.com>
  • Loading branch information
vkubiv authored Apr 27, 2023
1 parent 3fd663d commit a357f88
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 16 deletions.
77 changes: 61 additions & 16 deletions pkg/doc/presexch/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,29 @@ type MatchedInputDescriptor struct {
MatchedVCs []*verifiable.Credential
}

// matchRequirementsOpts holds options for the MatchSubmissionRequirement.
type matchRequirementsOpts struct {
applySelectiveDisclosure bool
credOpts []verifiable.CredentialOpt
}

// MatchRequirementsOpt is the MatchSubmissionRequirement option.
type MatchRequirementsOpt func(opts *matchRequirementsOpts)

// WithSelectiveDisclosureApply enables selective disclosure apply on resulting VC.
func WithSelectiveDisclosureApply() MatchRequirementsOpt {
return func(opts *matchRequirementsOpts) {
opts.applySelectiveDisclosure = true
}
}

// WithSDCredentialOptions used when applying selective disclosure.
func WithSDCredentialOptions(options ...verifiable.CredentialOpt) MatchRequirementsOpt {
return func(opts *matchRequirementsOpts) {
opts.credOpts = options
}
}

// ValidateSchema validates presentation definition.
func (pd *PresentationDefinition) ValidateSchema() error {
result, err := gojsonschema.Validate(
Expand Down Expand Up @@ -489,7 +512,12 @@ func makeRequirementsForMatch(requirements []*SubmissionRequirement,

// MatchSubmissionRequirement return information about matching VCs.
func (pd *PresentationDefinition) MatchSubmissionRequirement(credentials []*verifiable.Credential,
documentLoader ld.DocumentLoader) ([]*MatchedSubmissionRequirement, error) {
documentLoader ld.DocumentLoader, opts ...MatchRequirementsOpt) ([]*MatchedSubmissionRequirement, error) {
matchOpts := &matchRequirementsOpts{}
for _, opt := range opts {
opt(matchOpts)
}

if err := pd.ValidateSchema(); err != nil {
return nil, err
}
Expand All @@ -502,7 +530,7 @@ func (pd *PresentationDefinition) MatchSubmissionRequirement(credentials []*veri
var matchedReqs []*MatchedSubmissionRequirement

for _, req := range requirements {
matched, err := pd.matchRequirement(req, credentials, documentLoader)
matched, err := pd.matchRequirement(req, credentials, documentLoader, matchOpts)
if err != nil {
return nil, err
}
Expand All @@ -516,8 +544,9 @@ func (pd *PresentationDefinition) MatchSubmissionRequirement(credentials []*veri
// ErrNoCredentials when any credentials do not satisfy requirements.
var ErrNoCredentials = errors.New("credentials do not satisfy requirements")

// nolint: funlen
func (pd *PresentationDefinition) matchRequirement(req *requirement, creds []*verifiable.Credential,
documentLoader ld.DocumentLoader) (*MatchedSubmissionRequirement, error) {
documentLoader ld.DocumentLoader, opts *matchRequirementsOpts) (*MatchedSubmissionRequirement, error) {
matchedReq := &MatchedSubmissionRequirement{
Name: req.Name,
Purpose: req.Purpose,
Expand All @@ -529,31 +558,47 @@ func (pd *PresentationDefinition) matchRequirement(req *requirement, creds []*ve
Nested: nil,
}

if len(req.InputDescriptors) != 0 {
for _, descriptor := range req.InputDescriptors {
_, filtered, err := pd.filterCredentialsThatMatchDescriptor(
creds, descriptor, documentLoader)
for _, descriptor := range req.InputDescriptors {
framedCreds := creds

var err error

if opts.applySelectiveDisclosure {
framedCreds, err = frameCreds(pd.Frame, creds, opts.credOpts...)
if err != nil {
return nil, err
}
}

_, filtered, err := pd.filterCredentialsThatMatchDescriptor(
framedCreds, descriptor, documentLoader)
if err != nil {
return nil, err
}

var matchedVCs []*verifiable.Credential

var matchedVCs []*verifiable.Credential
if opts.applySelectiveDisclosure {
matchedVCs, err = limitDisclosure(filtered, opts.credOpts...)
if err != nil {
return nil, err
}
} else {
for _, credRes := range filtered {
matchedVCs = append(matchedVCs, credRes.credential)
}

matchedReq.Descriptors = append(matchedReq.Descriptors, &MatchedInputDescriptor{
ID: descriptor.ID,
Name: descriptor.Name,
Purpose: descriptor.Purpose,
MatchedVCs: matchedVCs,
})
}

matchedReq.Descriptors = append(matchedReq.Descriptors, &MatchedInputDescriptor{
ID: descriptor.ID,
Name: descriptor.Name,
Purpose: descriptor.Purpose,
MatchedVCs: matchedVCs,
})
}

for _, nestedReq := range req.Nested {
nestedMatch, err := pd.matchRequirement(nestedReq, creds, documentLoader)
nestedMatch, err := pd.matchRequirement(nestedReq, creds, documentLoader, opts)
if err != nil {
return nil, err
}
Expand Down
114 changes: 114 additions & 0 deletions pkg/doc/presexch/match_submission_requirements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ SPDX-License-Identifier: Apache-2.0
package presexch_test

import (
"crypto/sha256"
_ "embed"
"encoding/json"
"fmt"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/require"

"github.com/hyperledger/aries-framework-go/pkg/crypto/primitive/bbs12381g2pub"
"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
)

Expand Down Expand Up @@ -141,6 +149,112 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) {
}
})

t.Run("Limit disclosure BBS+", func(t *testing.T) {
required := presexch.Required

pd := &presexch.PresentationDefinition{
ID: uuid.New().String(),
InputDescriptors: []*presexch.InputDescriptor{{
Schema: []*presexch.Schema{{
URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
}},
ID: uuid.New().String(),
Constraints: &presexch.Constraints{
LimitDisclosure: &required,
Fields: []*presexch.Field{{
Path: []string{"$.credentialSubject.degree.degreeSchool"},
Filter: &presexch.Filter{Type: &strFilterType},
}},
},
}},
}

vc := &verifiable.Credential{
ID: "https://issuer.oidp.uscis.gov/credentials/83627465",
Context: []string{
verifiable.ContextURI,
"https://www.w3.org/2018/credentials/examples/v1",
"https://w3id.org/security/bbs/v1",
},
Types: []string{
"VerifiableCredential",
"UniversityDegreeCredential",
},
Subject: verifiable.Subject{
ID: "did:example:b34ca6cd37bbf23",
CustomFields: map[string]interface{}{
"name": "Jayden Doe",
"spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1",
"degree": map[string]interface{}{
"degree": "MIT",
"degreeSchool": "MIT school",
"type": "BachelorDegree",
},
},
},
Issued: &util.TimeWrapper{
Time: time.Now(),
},
Expired: &util.TimeWrapper{
Time: time.Now().AddDate(1, 0, 0),
},
Issuer: verifiable.Issuer{
ID: "did:example:489398593",
},
CustomFields: map[string]interface{}{
"identifier": "83627465",
"name": "Permanent Resident Card",
"description": "Government of Example Permanent Resident Card.",
},
}

publicKey, privateKey, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil)
require.NoError(t, err)

srcPublicKey, err := publicKey.Marshal()
require.NoError(t, err)

signer, err := newBBSSigner(privateKey)
require.NoError(t, err)

lddl := createTestJSONLDDocumentLoader(t)

require.NoError(t, vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{
SignatureType: "BbsBlsSignature2020",
SignatureRepresentation: verifiable.SignatureProofValue,
Suite: bbsblssignature2020.New(suite.WithSigner(signer)),
VerificationMethod: "did:example:123456#key1",
}, jsonld.WithDocumentLoader(lddl)))

matched, err := pd.MatchSubmissionRequirement([]*verifiable.Credential{vc}, lddl,
presexch.WithSelectiveDisclosureApply(),
presexch.WithSDCredentialOptions(verifiable.WithJSONLDDocumentLoader(lddl),
verifiable.WithPublicKeyFetcher(verifiable.SingleKey(srcPublicKey, "Bls12381G2Key2020"))),
)
require.NoError(t, err)
require.Len(t, matched, 1)
require.Equal(t, 1, len(matched[0].Descriptors))
require.Equal(t, 1, len(matched[0].Descriptors[0].MatchedVCs))

matchedVC := matched[0].Descriptors[0].MatchedVCs[0]

subject := matchedVC.Subject.([]verifiable.Subject)[0]
degree := subject.CustomFields["degree"]
require.NotNil(t, degree)

degreeMap, ok := degree.(map[string]interface{})
require.True(t, ok)

require.Equal(t, "MIT school", degreeMap["degreeSchool"])
require.Equal(t, "BachelorDegree", degreeMap["type"])
require.Empty(t, degreeMap["degree"])
require.Equal(t, "did:example:b34ca6cd37bbf23", subject.ID)
require.Empty(t, subject.CustomFields["spouse"])
require.Empty(t, matchedVC.CustomFields["name"])

require.NotEmpty(t, matchedVC.Proofs)
})

t.Run("Checks schema", func(t *testing.T) {
pd := &presexch.PresentationDefinition{ID: uuid.New().String()}

Expand Down

0 comments on commit a357f88

Please sign in to comment.