Skip to content

Commit

Permalink
chore: OIDC4VC BDD test for Data Integrity issuer (trustbloc#1399)
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK authored Sep 5, 2023
1 parent d5232ce commit 029c1e6
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 46 deletions.
2 changes: 1 addition & 1 deletion cmd/vc-rest/startcmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ func buildEchoHandler(
TransactionManager: oidc4vpTxManager,
RequestObjectPublicStore: requestObjectStoreService,
KMSRegistry: kmsRegistry,
PublicKeyFetcher: verifiable.NewVDRKeyResolver(conf.VDR).PublicKeyFetcher(),
VDR: conf.VDR,
DocumentLoader: documentLoader,
ProfileService: verifierProfileSvc,
PresentationVerifier: verifyPresentationSvc,
Expand Down
33 changes: 29 additions & 4 deletions pkg/service/oidc4vp/oidc4vp_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ import (
"github.com/valyala/fastjson"

"github.com/hyperledger/aries-framework-go/component/kmscrypto/doc/jose"
"github.com/hyperledger/aries-framework-go/component/models/dataintegrity"
"github.com/hyperledger/aries-framework-go/component/models/dataintegrity/suite/ecdsa2019"
"github.com/hyperledger/aries-framework-go/component/models/jwt"
"github.com/hyperledger/aries-framework-go/component/models/presexch"
"github.com/hyperledger/aries-framework-go/component/models/verifiable"
vdrapi "github.com/hyperledger/aries-framework-go/component/vdr/api"
kmsapi "github.com/hyperledger/aries-framework-go/spi/kms"

"github.com/trustbloc/vcs/internal/logfields"
"github.com/trustbloc/vcs/pkg/doc/vc"
"github.com/trustbloc/vcs/pkg/doc/vc/crypto"
vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable"
"github.com/trustbloc/vcs/pkg/event/spi"
vcskms "github.com/trustbloc/vcs/pkg/kms"
Expand Down Expand Up @@ -110,7 +114,7 @@ type Config struct {
EventSvc eventService
EventTopic string
PresentationVerifier presentationVerifier
PublicKeyFetcher verifiable.PublicKeyFetcher
VDR vdrapi.Registry

RedirectURL string
TokenLifetime time.Duration
Expand All @@ -130,7 +134,7 @@ type Service struct {
documentLoader ld.DocumentLoader
profileService profileService
presentationVerifier presentationVerifier
publicKeyFetcher verifiable.PublicKeyFetcher
vdr vdrapi.Registry

redirectURL string
tokenLifetime time.Duration
Expand Down Expand Up @@ -172,7 +176,7 @@ func NewService(cfg *Config) *Service {
presentationVerifier: cfg.PresentationVerifier,
redirectURL: cfg.RedirectURL,
tokenLifetime: cfg.TokenLifetime,
publicKeyFetcher: cfg.PublicKeyFetcher,
vdr: cfg.VDR,
metrics: metrics,
}
}
Expand Down Expand Up @@ -443,6 +447,21 @@ func (s *Service) DeleteClaims(_ context.Context, claimsID string) error {
return s.transactionManager.DeleteReceivedClaims(claimsID)
}

func (s *Service) getDataIntegrityVerifier() (*dataintegrity.Verifier, error) {
verifySuite := ecdsa2019.NewVerifierInitializer(&ecdsa2019.VerifierInitializerOptions{
LDDocumentLoader: s.documentLoader,
})

verifier, err := dataintegrity.NewVerifier(&dataintegrity.Options{
DIDResolver: s.vdr,
}, verifySuite)
if err != nil {
return nil, fmt.Errorf("new verifier: %w", err)
}

return verifier, nil
}

func (s *Service) extractClaimData(
ctx context.Context,
tx *Transaction,
Expand All @@ -457,11 +476,17 @@ func (s *Service) extractClaimData(
token.Presentation.JWT = ""
presentations = append(presentations, token.Presentation)
}
diVerifier, err := s.getDataIntegrityVerifier()
if err != nil {
return fmt.Errorf("get data integrity verifier: %w", err)
}

opts := []presexch.MatchOption{
presexch.WithCredentialOptions(
verifiable.WithDataIntegrityVerifier(diVerifier),
verifiable.WithExpectedDataIntegrityFields(crypto.Authentication, "", ""),
verifiable.WithJSONLDDocumentLoader(s.documentLoader),
verifiable.WithPublicKeyFetcher(s.publicKeyFetcher)),
verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(s.vdr).PublicKeyFetcher())),
presexch.WithDisableSchemaValidation(),
}

Expand Down
108 changes: 72 additions & 36 deletions pkg/service/oidc4vp/oidc4vp_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ import (
"github.com/hyperledger/aries-framework-go/component/kmscrypto/crypto/tinkcrypto"
"github.com/hyperledger/aries-framework-go/component/kmscrypto/doc/jose/jwk"
"github.com/hyperledger/aries-framework-go/component/kmscrypto/doc/util/fingerprint"
"github.com/hyperledger/aries-framework-go/component/kmscrypto/doc/util/jwkkid"
"github.com/hyperledger/aries-framework-go/component/kmscrypto/kms/localkms"
mockkms "github.com/hyperledger/aries-framework-go/component/kmscrypto/mock/kms"
"github.com/hyperledger/aries-framework-go/component/kmscrypto/secretlock/noop"
"github.com/hyperledger/aries-framework-go/component/models/did"
ldcontext "github.com/hyperledger/aries-framework-go/component/models/ld/context"
lddocloader "github.com/hyperledger/aries-framework-go/component/models/ld/documentloader"
"github.com/hyperledger/aries-framework-go/component/models/presexch"
"github.com/hyperledger/aries-framework-go/component/models/signature/suite"
"github.com/hyperledger/aries-framework-go/component/models/signature/verifier"
util "github.com/hyperledger/aries-framework-go/component/models/util/time"
"github.com/hyperledger/aries-framework-go/component/models/verifiable"
ariesmockstorage "github.com/hyperledger/aries-framework-go/component/storageutil/mock/storage"
vdrapi "github.com/hyperledger/aries-framework-go/component/vdr/api"
vdrmock "github.com/hyperledger/aries-framework-go/component/vdr/mock"
ariescrypto "github.com/hyperledger/aries-framework-go/spi/crypto"
"github.com/hyperledger/aries-framework-go/spi/kms"

Expand Down Expand Up @@ -249,7 +252,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
txManager := NewMockTransactionManager(gomock.NewController(t))
profileService := NewMockProfileService(gomock.NewController(t))
presentationVerifier := NewMockPresentationVerifier(gomock.NewController(t))
vp, pd, issuer, pubKeyFetcher, loader := newVPWithPD(t, keyManager, crypto)
vp, pd, issuer, vdr, loader := newVPWithPD(t, keyManager, crypto)

s := oidc4vp.NewService(&oidc4vp.Config{
EventSvc: &mockEvent{},
Expand All @@ -258,7 +261,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
PresentationVerifier: presentationVerifier,
ProfileService: profileService,
DocumentLoader: loader,
PublicKeyFetcher: pubKeyFetcher,
VDR: vdr,
})

txManager.EXPECT().GetByOneTimeToken("nonce1").AnyTimes().Return(&oidc4vp.Transaction{
Expand Down Expand Up @@ -343,20 +346,20 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {

testLoader := testutil.DocumentLoader(t)

vp1, issuer1, pubKeyFetcher1 := newVPWithPS(t, keyManager, crypto, mergedPS, "PhDDegree")
vp2, issuer2, pubKeyFetcher2 := newVPWithPS(t, keyManager, crypto, mergedPS, "BachelorDegree")
vp1, issuer1, vdr1 := newVPWithPS(t, keyManager, crypto, mergedPS, "PhDDegree")
vp2, issuer2, vdr2 := newVPWithPS(t, keyManager, crypto, mergedPS, "BachelorDegree")

combinedFetcher := func(issuerID string, keyID string) (*verifier.PublicKey, error) {
switch issuerID {
case issuer1:
return pubKeyFetcher1(issuerID, keyID)
combinedDIDResolver := &vdrmock.VDRegistry{
ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
switch didID {
case issuer1:
return vdr1.Resolve(didID, opts...)
case issuer2:
return vdr2.Resolve(didID, opts...)
}

case issuer2:
return pubKeyFetcher2(issuerID, keyID)
}

return nil, fmt.Errorf("unexpected issuer")
}
return nil, fmt.Errorf("unexpected issuer")
}}

txManager2 := NewMockTransactionManager(gomock.NewController(t))

Expand All @@ -367,7 +370,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
PresentationVerifier: presentationVerifier,
ProfileService: profileService,
DocumentLoader: testLoader,
PublicKeyFetcher: combinedFetcher,
VDR: combinedDIDResolver,
})

txManager2.EXPECT().GetByOneTimeToken("nonce1").AnyTimes().Return(&oidc4vp.Transaction{
Expand Down Expand Up @@ -430,20 +433,20 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {

testLoader := testutil.DocumentLoader(t)

vp1, issuer1, pubKeyFetcher1 := newVPWithPS(t, keyManager, crypto, mergedPS, "PhDDegree")
vp2, issuer2, pubKeyFetcher2 := newVPWithPS(t, keyManager, crypto, mergedPS, "BachelorDegree")
vp1, issuer1, vdr1 := newVPWithPS(t, keyManager, crypto, mergedPS, "PhDDegree")
vp2, issuer2, vdr2 := newVPWithPS(t, keyManager, crypto, mergedPS, "BachelorDegree")

combinedFetcher := func(issuerID string, keyID string) (*verifier.PublicKey, error) {
switch issuerID {
case issuer1:
return pubKeyFetcher1(issuerID, keyID)
combinedDIDResolver := &vdrmock.VDRegistry{
ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
switch didID {
case issuer1:
return vdr1.Resolve(didID, opts...)
case issuer2:
return vdr2.Resolve(didID, opts...)
}

case issuer2:
return pubKeyFetcher2(issuerID, keyID)
}

return nil, fmt.Errorf("unexpected issuer")
}
return nil, fmt.Errorf("unexpected issuer")
}}

txManager2 := NewMockTransactionManager(gomock.NewController(t))

Expand All @@ -454,7 +457,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
PresentationVerifier: presentationVerifier,
ProfileService: profileService,
DocumentLoader: testLoader,
PublicKeyFetcher: combinedFetcher,
VDR: combinedDIDResolver,
})

txManager2.EXPECT().GetByOneTimeToken("nonce1").AnyTimes().Return(&oidc4vp.Transaction{
Expand Down Expand Up @@ -580,7 +583,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
PresentationVerifier: errPresentationVerifier,
ProfileService: profileService,
DocumentLoader: loader,
PublicKeyFetcher: pubKeyFetcher,
VDR: vdr,
})

err := withError.VerifyOIDCVerifiablePresentation(context.Background(), "txID1",
Expand Down Expand Up @@ -623,7 +626,7 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) {
PresentationVerifier: presentationVerifier,
ProfileService: profileService,
DocumentLoader: loader,
PublicKeyFetcher: pubKeyFetcher,
VDR: vdr,
})

err := withError.VerifyOIDCVerifiablePresentation(context.Background(), "txID1",
Expand Down Expand Up @@ -794,7 +797,7 @@ func (m *mockEvent) Publish(_ context.Context, _ string, _ ...*spi.Event) error

func newVPWithPD(t *testing.T, keyManager kms.KeyManager, crypto ariescrypto.Crypto) (
*verifiable.Presentation, *presexch.PresentationDefinition, string,
verifiable.PublicKeyFetcher, *lddocloader.DocumentLoader) {
vdrapi.Registry, *lddocloader.DocumentLoader) {
uri := randomURI()

customType := "CustomType"
Expand Down Expand Up @@ -824,7 +827,7 @@ func newVPWithPD(t *testing.T, keyManager kms.KeyManager, crypto ariescrypto.Cry

func newVPWithPS(t *testing.T, keyManager kms.KeyManager, crypto ariescrypto.Crypto,
ps *presexch.PresentationSubmission, value string) (
*verifiable.Presentation, string, verifiable.PublicKeyFetcher) {
*verifiable.Presentation, string, vdrapi.Registry) {
expected, issuer, pubKeyFetcher := newSignedJWTVC(t, keyManager, crypto, nil, "degree", value)

return newVP(t, ps,
Expand Down Expand Up @@ -897,7 +900,7 @@ func newDegreeVC(issuer string, degreeType string, ctx []string) *verifiable.Cre

func newSignedJWTVC(t *testing.T,
keyManager kms.KeyManager, crypto ariescrypto.Crypto, ctx []string,
vcType string, value string) (*verifiable.Credential, string, verifiable.PublicKeyFetcher) {
vcType string, value string) (*verifiable.Credential, string, vdrapi.Registry) {
t.Helper()

keyID, kh, err := keyManager.Create(kms.ED25519Type)
Expand All @@ -909,10 +912,19 @@ func newSignedJWTVC(t *testing.T,
require.NoError(t, err)
require.Equal(t, kms.ED25519Type, kt)

pubKeyFetcher := verifiable.SingleKey(pubKey, kms.ED25519)
key, err := jwkkid.BuildJWK(pubKey, kms.ED25519)
require.NoError(t, err)

issuer, verMethod := fingerprint.CreateDIDKeyByCode(fingerprint.ED25519PubKeyMultiCodec, pubKey)

verificationMethod, err := did.NewVerificationMethodFromJWK(verMethod, "JsonWebKey2020", issuer, key)
require.NoError(t, err)

didResolver := &vdrmock.VDRegistry{
ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
return makeMockDIDResolution(issuer, verificationMethod, did.Authentication), nil
}}

var vc *verifiable.Credential

switch vcType {
Expand All @@ -935,7 +947,31 @@ func newSignedJWTVC(t *testing.T,

vc.JWT = jws

return vc, issuer, pubKeyFetcher
return vc, issuer, didResolver
}

func makeMockDIDResolution(id string, vm *did.VerificationMethod, vr did.VerificationRelationship) *did.DocResolution {
ver := []did.Verification{{
VerificationMethod: *vm,
Relationship: vr,
}}

doc := &did.Doc{
ID: id,
}

switch vr { //nolint:exhaustive
case did.VerificationRelationshipGeneral:
doc.VerificationMethod = []did.VerificationMethod{*vm}
case did.Authentication:
doc.Authentication = ver
case did.AssertionMethod:
doc.AssertionMethod = ver
}

return &did.DocResolution{
DIDDocument: doc,
}
}

func randomURI() string {
Expand Down
13 changes: 13 additions & 0 deletions pkg/service/verifycredential/verifycredential_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,19 @@ func TestService_ValidateCredentialProof(t *testing.T) {
},
wantErr: true,
},
{
name: "Invalid credentials",
args: args{
getVcByte: func() []byte {
return []byte(`{..`)
},
proofChallenge: crypto.Challenge,
proofDomain: "some value",
vcInVPValidation: false,
isJWT: true,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
12 changes: 7 additions & 5 deletions test/bdd/features/oidc4vc_api.feature
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ Feature: OIDC4VC REST API
And Verifier form organization "test_org" requests deleted interactions claims

Examples:
| issuerProfile | credentialType | clientRegistrationMethod | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
| issuerProfile | credentialType | clientRegistrationMethod | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
# SDJWT issuer, JWT verifier, no limit disclosure in PD query.
| bank_issuer/v1.0 | UniversityDegreeCredential | dynamic | universityDegreeTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id |
| bank_issuer/v1.0 | UniversityDegreeCredential | dynamic | universityDegreeTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id |
# SDJWT issuer, JWT verifier, limit disclosure and optional fields in PD query.
| bank_issuer/v1.0 | CrudeProductCredential | discoverable | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
| bank_issuer/v1.0 | CrudeProductCredential | discoverable | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
# JWT issuer, JWT verifier, no limit disclosure and optional fields in PD query.
| i_myprofile_ud_es256k_jwt/v1.0 | PermanentResidentCard | pre-registered | permanentResidentCardTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-optional-fields | lpr_category_id,registration_city,commuter_classification |
| i_myprofile_ud_es256k_jwt/v1.0 | PermanentResidentCard | pre-registered | permanentResidentCardTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-optional-fields | lpr_category_id,registration_city,commuter_classification |
# LDP Data Integrity issuer, LDP verifier, no limit disclosure and schema match in PD query.
| i_myprofile_ud_di_ecdsa-2019/v1.0 | PermanentResidentCard | pre-registered | permanentResidentCardTemplateID | v_myprofile_ldp/v1.0 | 062759b1-no-limit-disclosure-optional-fields | lpr_category_id,registration_city,commuter_classification |
# LDP issuer, LDP verifier, no limit disclosure and schema match in PD query.
| i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | pre-registered | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |
| i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | pre-registered | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |

Scenario Outline: OIDC credential issuance and verification Pre Auth flow
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
Expand Down
Loading

0 comments on commit 029c1e6

Please sign in to comment.