Skip to content

Commit

Permalink
feat: add trust list support (trustbloc#1382)
Browse files Browse the repository at this point in the history
* feat: add trust list support

Signed-off-by: Stas D <[email protected]>

* fix: lint

---------

Signed-off-by: Stas D <[email protected]>
  • Loading branch information
skynet2 authored Aug 21, 2023
1 parent f3207e9 commit 9d11e8c
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 5 deletions.
11 changes: 11 additions & 0 deletions component/profile/reader/file/internal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package file

import profileapi "github.com/trustbloc/vcs/pkg/profile"

var createdIssuers = map[string]*profileapi.Issuer{} // nolint:gochecknoglobals
22 changes: 22 additions & 0 deletions component/profile/reader/file/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

vdrpkg "github.com/hyperledger/aries-framework-go/pkg/vdr"
"github.com/hyperledger/aries-framework-go/pkg/vdr/key"

vcskms "github.com/trustbloc/vcs/pkg/kms"
profileapi "github.com/trustbloc/vcs/pkg/profile"
)
Expand Down Expand Up @@ -119,6 +120,7 @@ func NewIssuerReader(config *Config) (*IssuerReader, error) {

issuerVersion := version.Must(version.NewVersion(v.Data.Version))

createdIssuers[v.Data.ID] = v.Data
issuerProfileVersions[v.Data.ID] = append(issuerProfileVersions[v.Data.ID], issuerVersion)
issuerProfiles[getProfileVersionKey(v.Data.ID, issuerVersion)] = v.Data
}
Expand Down Expand Up @@ -175,6 +177,7 @@ func NewVerifierReader(config *Config) (*VerifierReader, error) {

logger.Info("create verifier profile successfully", log.WithID(v.Data.ID))

r.setTrustList(v.Data)
// Set version as it come.
r.verifiers[fmt.Sprintf("%s_%s", v.Data.ID, v.Data.Version)] = v.Data

Expand All @@ -189,6 +192,25 @@ func NewVerifierReader(config *Config) (*VerifierReader, error) {
return &r, nil
}

func (p *VerifierReader) setTrustList(verifier *profileapi.Verifier) {
if verifier == nil || verifier.Checks == nil || len(createdIssuers) == 0 ||
len(verifier.Checks.Credential.IssuerTrustList) == 0 {
return
}

var updatedList []string
for _, item := range verifier.Checks.Credential.IssuerTrustList {
issuer, ok := createdIssuers[item]
if !ok {
continue
}

updatedList = append(updatedList, issuer.SigningDID.DID)
}

verifier.Checks.Credential.IssuerTrustList = updatedList
}

// GetProfile returns profile with given id.
func (p *VerifierReader) GetProfile(
profileID profileapi.ID, profileVersion profileapi.Version) (*profileapi.Verifier, error) {
Expand Down
1 change: 1 addition & 0 deletions pkg/profile/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ type CredentialChecks struct {
CredentialExpiry bool `json:"credentialExpiry,omitempty"`
Strict bool `json:"strict,omitempty"`
LinkedDomain bool `json:"linkedDomain,omitempty"`
IssuerTrustList []string `json:"issuerTrustList,omitempty"`
}

// SigningDID contains information about profile signing did.
Expand Down
33 changes: 32 additions & 1 deletion pkg/service/verifypresentation/verifypresentation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
"github.com/piprate/json-gold/ld"
"github.com/samber/lo"
"github.com/trustbloc/logutil-go/pkg/log"

"github.com/trustbloc/vcs/internal/logfields"
Expand Down Expand Up @@ -102,6 +103,15 @@ func (s *Service) VerifyPresentation( //nolint:funlen,gocognit
logger.Debugc(ctx, "Checks.Presentation.Proof", log.WithDuration(time.Since(st)))
}

if len(profile.Checks.Credential.IssuerTrustList) > 0 {
err := s.checkIssuerTrustList(ctx, lazyCredentials, profile.Checks.Credential.IssuerTrustList)
if err != nil {
result = append(result, PresentationVerificationCheckResult{
Check: "issuerTrustList",
Error: err.Error(),
})
}
}
if profile.Checks.Credential.CredentialExpiry {
err := s.checkCredentialExpiry(ctx, lazyCredentials)
if err != nil {
Expand Down Expand Up @@ -233,7 +243,7 @@ func (s *Service) checkCredentialExpiry(ctx context.Context, lazy []*LazyCredent
credential, ok := input.Raw().(*verifiable.Credential)
if !ok {
logger.Warnc(ctx, fmt.Sprintf("can not validate expiry. unexpected type %v",
reflect.TypeOf(input).String()))
reflect.TypeOf(input.Raw()).String()))
return nil
}
if credential.Expired != nil && time.Now().UTC().After(credential.Expired.Time) {
Expand All @@ -244,6 +254,27 @@ func (s *Service) checkCredentialExpiry(ctx context.Context, lazy []*LazyCredent
return nil
}

func (s *Service) checkIssuerTrustList(_ context.Context, lazy []*LazyCredential, trustList []string) error {
for _, input := range lazy {
var issuerID string
switch cred := input.Raw().(type) {
case *verifiable.Credential:
issuerID = cred.Issuer.ID
case map[string]interface{}:
issuerID = fmt.Sprint(cred["issuer"])
default:
return fmt.Errorf("can not validate issuer trust list. unexpected type %v",
reflect.TypeOf(input.Raw()).String())
}

if !lo.Contains(trustList, issuerID) {
return fmt.Errorf("issuer with id: %v is not a member of trustlist", issuerID)
}
}

return nil
}

func (s *Service) validatePresentationProof(targetPresentation interface{}, opts *Options) error {
var final *verifiable.Presentation
switch pres := targetPresentation.(type) {
Expand Down
46 changes: 42 additions & 4 deletions pkg/service/verifypresentation/verifypresentation_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func TestService_VerifyPresentation(t *testing.T) {
Format: nil,
CredentialExpiry: true,
Strict: true,
IssuerTrustList: []string{"https://example.edu/issuers/14"},
},
},
},
Expand Down Expand Up @@ -175,6 +176,7 @@ func TestService_VerifyPresentation(t *testing.T) {
want: nil,
wantErr: false,
},

{
name: "Error credentials",
fields: fields{
Expand Down Expand Up @@ -212,10 +214,11 @@ func TestService_VerifyPresentation(t *testing.T) {
Format: nil,
},
Credential: profileapi.CredentialChecks{
Proof: true,
Status: true,
LinkedDomain: true,
Format: nil,
Proof: true,
Status: true,
LinkedDomain: true,
Format: nil,
IssuerTrustList: []string{"random"},
},
},
},
Expand All @@ -225,6 +228,10 @@ func TestService_VerifyPresentation(t *testing.T) {
},
},
want: []PresentationVerificationCheckResult{
{
Check: "issuerTrustList",
Error: "issuer with id: https://example.edu/issuers/14 is not a member of trustlist",
},
{
Check: "credentialProof",
Error: "some error",
Expand Down Expand Up @@ -812,3 +819,34 @@ func TestCredentialStrict(t *testing.T) {
assert.NoError(t, s.checkCredentialStrict(context.TODO(), []*LazyCredential{l}))
assert.ElementsMatch(t, []string{"type", "degree"}, s.GetClaimKeys()["credentialID"])
}

func TestCheckTrustList(t *testing.T) {
s := New(&Config{})

t.Run("from credentials", func(t *testing.T) {
cred := &verifiable.Credential{Issuer: verifiable.Issuer{
ID: "123432123",
}}

err := s.checkIssuerTrustList(
context.TODO(),
[]*LazyCredential{NewLazyCredential(cred)},
[]string{"a"},
)

assert.ErrorContains(t, err, "issuer with id: 123432123 is not a member of trustlist")
})

t.Run("invalid type", func(t *testing.T) {
cred := &verifiable.Presentation{}

err := s.checkIssuerTrustList(
context.TODO(),
[]*LazyCredential{NewLazyCredential(cred)},
[]string{"a"},
)

assert.ErrorContains(t, err,
"can not validate issuer trust list. unexpected type *verifiable.Presentation")
})
}
23 changes: 23 additions & 0 deletions test/bdd/features/oidc4vc_api.feature
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,30 @@ Feature: OIDC4VC REST API
# LDP issuer, LDP verifier, no limit disclosure and schema match in PD query.
| i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |

Scenario: OIDC credential issuance and verification Pre Auth flow with trustlist (Success)
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
And Issuer with id "bank_issuer_sdjwt_v5/v1.0" is authorized as a Profile user
And User holds credential "CrudeProductCredential" with templateID "crudeProductCredentialTemplateID"

When User interacts with Wallet to initiate credential issuance using pre authorization code flow
Then credential is issued
Then User interacts with Verifier and initiate OIDC4VP interaction under "v_myprofile_jwt_whitelist/v1.0" profile for organization "test_org" with presentation definition ID "3c8b1d9a-limit-disclosure-optional-fields" and fields "unit_of_measure_barrel,api_gravity,category,supplier_address"
And Verifier from organization "test_org" retrieves interactions claims
Then we wait 2 seconds
And Verifier form organization "test_org" requests deleted interactions claims

# Error cases

Scenario: OIDC credential issuance and verification Pre Auth flow with trustlist (Fail)
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
And Issuer with id "bank_issuer/v1.0" is authorized as a Profile user
And User holds credential "CrudeProductCredential" with templateID "crudeProductCredentialTemplateID"

When User interacts with Wallet to initiate credential issuance using pre authorization code flow
Then credential is issued
Then User interacts with Verifier and initiate OIDC4VP interaction under "v_myprofile_jwt_whitelist/v1.0" profile for organization "test_org" with presentation definition ID "3c8b1d9a-limit-disclosure-optional-fields" and fields "unit_of_measure_barrel,api_gravity,category,supplier_address" and receives "is not a member of trustlist" error


Scenario: OIDC credential issuance and verification Pre Auth flow (Invalid Claims)
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
And Issuer with id "bank_issuer/v1.0" is authorized as a Profile user
Expand Down
Loading

0 comments on commit 9d11e8c

Please sign in to comment.