From e77e19a418b499508cad6f0be857be6c00ebd3f5 Mon Sep 17 00:00:00 2001 From: talwinder50 Date: Mon, 28 Feb 2022 05:36:31 -0500 Subject: [PATCH] feat: Update to latest aries and support for submission requirement closes #612 Signed-off-by: talwinder50 --- cmd/adapter-rest/go.mod | 2 +- cmd/adapter-rest/go.sum | 4 +- cmd/adapter-rest/startcmd/start_test.go | 4 +- .../startcmd/testdata/outputdescriptors.json | 6 +- go.mod | 2 +- go.sum | 4 +- pkg/memcmdescriptor/provider.go | 31 ++-- pkg/memcmdescriptor/provider_test.go | 31 ++-- pkg/restapi/issuer/operation/operations.go | 44 ++++-- .../issuer/operation/operations_test.go | 80 ++++++++--- .../manifest-config/cmdescriptors.json | 4 +- test/bdd/go.mod | 2 +- test/bdd/go.sum | 4 +- test/bdd/pkg/agent/agent_controller_steps.go | 21 ++- test/bdd/pkg/agent/testdata/vc_prc.json | 11 +- test/bdd/pkg/agent/wallet.go | 132 ++++++++++++++++-- 16 files changed, 292 insertions(+), 90 deletions(-) diff --git a/cmd/adapter-rest/go.mod b/cmd/adapter-rest/go.mod index 342443ac..de0798c0 100644 --- a/cmd/adapter-rest/go.mod +++ b/cmd/adapter-rest/go.mod @@ -10,7 +10,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 + github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20211117223600-626fe1bae44d github.com/hyperledger/aries-framework-go-ext/component/storage/mysql v0.0.0-20210909220549-ce3a2ee13e22 github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20220106195936-a9d6794663ed diff --git a/cmd/adapter-rest/go.sum b/cmd/adapter-rest/go.sum index 2b81f617..6bf59071 100644 --- a/cmd/adapter-rest/go.sum +++ b/cmd/adapter-rest/go.sum @@ -814,8 +814,8 @@ github.com/hyperledger/aries-framework-go v0.1.7-0.20210816113201-26c0665ef2b9/g github.com/hyperledger/aries-framework-go v0.1.7/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211201185059-733a3370f501/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211217135421-f68d5698237a/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 h1:vKeoUmoIArG4rI+8JBrHdfG9FJeOosSA8A2/eyWqViM= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 h1:waARdkRtU8tTQDOhsKZ1/Tdqzu3SiGw4dhho/1Y0P/4= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= github.com/hyperledger/aries-framework-go-ext/component/storage/couchdb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:FtlFhPHMyLORgrPpvWSmEQSNhLiwAQ4V6rqNPfuDj0o= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:aiO9mXZBykIEwmgp9sSdpMuTw0P7b+ZFUltcIB9ZccY= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20211117223600-626fe1bae44d h1:h91rxhZkAjxcIwY08RxUTE+B8WxfiWbRHNl5X98+ziA= diff --git a/cmd/adapter-rest/startcmd/start_test.go b/cmd/adapter-rest/startcmd/start_test.go index 7c6a1bb0..f3e112fd 100644 --- a/cmd/adapter-rest/startcmd/start_test.go +++ b/cmd/adapter-rest/startcmd/start_test.go @@ -52,11 +52,13 @@ var cmDescData = `{ "schema":"https://www.w3.org/2018/credentials/examples/v1" } ], - "input_descriptor":[ + "presentation_definition": { + "input_descriptors":[ { "testing":"prc_input" } ] + } } }` diff --git a/cmd/adapter-rest/startcmd/testdata/outputdescriptors.json b/cmd/adapter-rest/startcmd/testdata/outputdescriptors.json index 28ee7317..99345751 100644 --- a/cmd/adapter-rest/startcmd/testdata/outputdescriptors.json +++ b/cmd/adapter-rest/startcmd/testdata/outputdescriptors.json @@ -70,7 +70,8 @@ } } ], - "input_descriptor":[ + "presentation_definition": { + "input_descriptors":[ { "id":"prc_input", "name":"Permanent Resident Card", @@ -117,7 +118,8 @@ ] } } - ], + ] + }, "options":{ "challenge":"508adef4-b8e0-4edf-a53d-a260371c1423", "domain":"9rf25a28rs96" diff --git a/go.mod b/go.mod index 284dd1b4..a81832dc 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 + github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 github.com/hyperledger/aries-framework-go-ext/component/vdr/orb v0.1.4-0.20211219215001-23cd75276fdc github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20220106195936-a9d6794663ed github.com/hyperledger/aries-framework-go/spi v0.0.0-20220106195936-a9d6794663ed diff --git a/go.sum b/go.sum index c79e81ab..017293fa 100644 --- a/go.sum +++ b/go.sum @@ -809,8 +809,8 @@ github.com/hyperledger/aries-framework-go v0.1.7-0.20210816113201-26c0665ef2b9/g github.com/hyperledger/aries-framework-go v0.1.7/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211201185059-733a3370f501/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211217135421-f68d5698237a/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 h1:vKeoUmoIArG4rI+8JBrHdfG9FJeOosSA8A2/eyWqViM= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 h1:waARdkRtU8tTQDOhsKZ1/Tdqzu3SiGw4dhho/1Y0P/4= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= github.com/hyperledger/aries-framework-go-ext/component/storage/couchdb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:FtlFhPHMyLORgrPpvWSmEQSNhLiwAQ4V6rqNPfuDj0o= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:aiO9mXZBykIEwmgp9sSdpMuTw0P7b+ZFUltcIB9ZccY= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20211117223600-626fe1bae44d h1:h91rxhZkAjxcIwY08RxUTE+B8WxfiWbRHNl5X98+ziA= diff --git a/pkg/memcmdescriptor/provider.go b/pkg/memcmdescriptor/provider.go index f8336aa4..ddc974b2 100644 --- a/pkg/memcmdescriptor/provider.go +++ b/pkg/memcmdescriptor/provider.go @@ -17,11 +17,9 @@ import ( // CMAttachmentDescriptors defines the part of properties of credential manifest type CMAttachmentDescriptors struct { - OutputDesc []*cm.OutputDescriptor `json:"output_descriptor,omitempty"` - // TODO [#Issue-612] Support for submission requirement will put whole presentation_definition here - // instead of input_descriptor - InputDesc []*presexch.InputDescriptor `json:"input_descriptor,omitempty"` - Options map[string]string `json:"options,omitempty"` + OutputDesc []*cm.OutputDescriptor `json:"output_descriptor,omitempty"` + PresentationDefinition *presexch.PresentationDefinition `json:"presentation_definition,omitempty"` + Options map[string]string `json:"options,omitempty"` } // Provider provide credential attachment descriptors ops. @@ -32,7 +30,7 @@ type Provider struct { // New return new provider for credential manifest descriptor provider. func New(cmDescriptorsFile io.Reader) (*Provider, error) { p := &Provider{ - cmDescriptors: map[string]*CMAttachmentDescriptors{}, + cmDescriptors: make(map[string]*CMAttachmentDescriptors), } err := json.NewDecoder(cmDescriptorsFile).Decode(&p.cmDescriptors) @@ -47,10 +45,25 @@ func New(cmDescriptorsFile io.Reader) (*Provider, error) { "descriptors: %w", err) } - if descriptors.InputDesc != nil { - presDef := presexch.PresentationDefinition{ID: uuid.NewString(), InputDescriptors: descriptors.InputDesc} + if descriptors.PresentationDefinition != nil { + var pd *presexch.PresentationDefinition - err := presDef.ValidateSchema() + presBytes, err := json.Marshal(descriptors.PresentationDefinition) + if err != nil { + return nil, fmt.Errorf("failed to marshal presentation "+ + "definition: %w", err) + } + + err = json.Unmarshal(presBytes, &pd) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal presentation"+ + "definition: %w", err) + } + + preDef := presexch.PresentationDefinition{ID: uuid.NewString(), + InputDescriptors: pd.InputDescriptors} + + err = preDef.ValidateSchema() if err != nil { return nil, fmt.Errorf("aries-framework - failed to validate input "+ "descriptors: %w", err) diff --git a/pkg/memcmdescriptor/provider_test.go b/pkg/memcmdescriptor/provider_test.go index 85d5796f..180f8940 100644 --- a/pkg/memcmdescriptor/provider_test.go +++ b/pkg/memcmdescriptor/provider_test.go @@ -25,24 +25,27 @@ var cmOutDescData = `{ // nolint:gochecknoglobals var cmDescData = ` - { +{ "prc":{ - "output_descriptor":[ - { + "output_descriptor":[ + { "id":"udc_output", "schema":"https://www.w3.org/2018/credentials/examples/v1" - } + } ], - "input_descriptor":[ - { - "id":"prc_input", - "schema":[ - { + "presentation_definition":{ + "id":"8246867e-fdce-48de-a825-9d84ec16c6c9", + "input_descriptors":[ + { + "id":"prc_input", + "schema":[ + { "uri":"https://w3id.org/citizenship#PermanentResidentCard" - } - ] - } - ] + } + ] + } + ] + } } } ` @@ -57,11 +60,13 @@ var invalidCMDescData = ` "schema":"https://www.w3.org/2018/credentials/examples/v1" } ], + "presentation_definition": { "input_descriptor":[ { "id":"prc_input" } ] + } } } ` diff --git a/pkg/restapi/issuer/operation/operations.go b/pkg/restapi/issuer/operation/operations.go index f3cf96b2..b0ced577 100644 --- a/pkg/restapi/issuer/operation/operations.go +++ b/pkg/restapi/issuer/operation/operations.go @@ -1222,25 +1222,46 @@ func (o *Operation) saveCredentialAttachmentData(thID string, credOffered creden func (o *Operation) readAndValidateCredentialApplication(msg service.DIDCommAction, credManifest *cm.CredentialManifest) error { - // reading credential application if issuer have sent, out presentation_definition along with manifest - // TODO - Aries Validate signatures and proofs of credential application should be part of - // cm.ValidateCredentialApplicationAttachment function. if credManifest.PresentationDefinition != nil { applicationAttachments, err := getAttachments(msg) if err != nil { return fmt.Errorf("failed to get request credential attachments: %w", err) } - if len(applicationAttachments) != 1 { - return errors.New("invalid request credential message, expected valid credential application") + attachmentAsMap, ok := applicationAttachments[0].Data.JSON.(map[string]interface{}) + if !ok { + return errors.New("couldn't assert attachment data as a map") } - err = cm.ValidateCredentialApplicationAttachment(&applicationAttachments[0], credManifest) + credentialApplicationBytes, err := json.MarshalIndent(attachmentAsMap, "", " ") if err != nil { - return fmt.Errorf("failed to validate credential application attachment: %w", err) + return fmt.Errorf("failed to marshal credential_application object: %w", err) } - return nil + err = o.validateCredentialApplication(credentialApplicationBytes, credManifest) + if err != nil { + return fmt.Errorf("failed to validate credential application: %w", err) + } + } + + return nil +} + +func (o *Operation) validateCredentialApplication(credentialApplicationBytes []byte, + credManifest *cm.CredentialManifest) error { + application, err := verifiable.ParsePresentation(credentialApplicationBytes, + verifiable.WithPresPublicKeyFetcher(verifiable.NewVDRKeyResolver(o.vdriRegistry).PublicKeyFetcher()), + verifiable.WithPresJSONLDDocumentLoader(o.jsonldDocLoader)) + if err != nil { + return fmt.Errorf("failed to parse credential application: %w", err) + } + // TODO issue-#635 Figure out the way to pass the matched credentials result back to the issuer + _, err = cm.ValidateCredentialApplication(application, credManifest, o.jsonldDocLoader, + presexch.WithCredentialOptions(verifiable.WithJSONLDDocumentLoader(o.jsonldDocLoader), + verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(o.vdriRegistry).PublicKeyFetcher())), + ) + if err != nil { + return fmt.Errorf("credential manifest: failed to validate credential application: %w", err) } return nil @@ -1935,11 +1956,8 @@ func (o *Operation) readCredentialManifest(profileData *issuer.ProfileData, credentialManifest.OutputDescriptors = attachmentDescriptors.OutputDesc } - if attachmentDescriptors.InputDesc != nil { - credentialManifest.PresentationDefinition = &presexch.PresentationDefinition{ - ID: uuid.NewString(), - InputDescriptors: attachmentDescriptors.InputDesc, - } + if attachmentDescriptors.PresentationDefinition != nil { + credentialManifest.PresentationDefinition = attachmentDescriptors.PresentationDefinition } } diff --git a/pkg/restapi/issuer/operation/operations_test.go b/pkg/restapi/issuer/operation/operations_test.go index 16c96fd7..c99de214 100644 --- a/pkg/restapi/issuer/operation/operations_test.go +++ b/pkg/restapi/issuer/operation/operations_test.go @@ -75,13 +75,15 @@ var cmDescData = memcmdescriptor.CMAttachmentDescriptors{ Schema: "https://www.w3.org/2018/credentials/examples/v1", }, }, - InputDesc: []*presexch.InputDescriptor{ - { - ID: uuid.NewString(), - Group: []string{"A"}, - Schema: []*presexch.Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), - }}, + PresentationDefinition: &presexch.PresentationDefinition{ + InputDescriptors: []*presexch.InputDescriptor{ + { + ID: uuid.NewString(), + Group: []string{"A"}, + Schema: []*presexch.Schema{{ + URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + }}, + }, }, }, } @@ -96,6 +98,24 @@ var cmOutputDescData = memcmdescriptor.CMAttachmentDescriptors{ }, } +// nolint:gochecknoglobals +var credentialApplicationData = `{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "dc59653c-7e15-4718-80a4-46d6fb01ab7b", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": {} + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [] +}` + func TestNew(t *testing.T) { t.Parallel() @@ -1283,8 +1303,8 @@ func TestReadManifest(t *testing.T) { // nolint:tparallel // data race c.cmDescriptors = &mockCMDescriptorProvider{ createValue: &memcmdescriptor.CMAttachmentDescriptors{ - OutputDesc: nil, - InputDesc: nil, + OutputDesc: nil, + PresentationDefinition: nil, }, found: true, } @@ -4075,12 +4095,14 @@ func TestWACIIssuanceHandler(t *testing.T) { // test failed to get credential manifestID from store testFailure(actionCh, msg, "failed to get credential manifestID from store") + // Adding presentation with no crdential application attachment + app := createCredentialFulfillment(t, c, profile) msg = service.NewDIDCommMsgMap(issuecredsvc.RequestCredentialV3{ Type: issuecredsvc.RequestCredentialMsgTypeV3, Attachments: []decorator.AttachmentV2{ {ID: manifestID, Data: decorator.AttachmentData{ - JSON: &verifiable.Presentation{}, + JSON: app, }}, }, }) @@ -4092,7 +4114,7 @@ func TestWACIIssuanceHandler(t *testing.T) { })) // test read and validate cred application -> credential_application object missing from attachment - testFailure(actionCh, msg, "missing credential_application field") + testFailure(actionCh, msg, "invalid credential application, missing 'credential_application'") application := createCredentialApplication(t, c, manifestID, profile) msg = service.NewDIDCommMsgMap(issuecredsvc.RequestCredentialV3{ @@ -4108,8 +4130,8 @@ func TestWACIIssuanceHandler(t *testing.T) { msg.SetID(thID) // test credential application missing corresponding presentation submission - testFailure(actionCh, msg, "Credential Application attachment is "+ - "missing a corresponding Presentation Submission") + testFailure(actionCh, msg, "failed to parse descriptor map: missing "+ + "'presentation_submission' on verifiable presentation") c.cmDescriptors = mockCMDescriptorsProvider(cmOutputDescData) @@ -4143,6 +4165,22 @@ func TestWACIIssuanceHandler(t *testing.T) { mockMsg.thIDerr = errors.New("thid error") testFailure(actionCh, mockMsg, "failed to read threadID from request credential message") }) + + t.Run("test validate credential application failure", func(t *testing.T) { + t.Parallel() + + c, err := New(config(t)) + require.NoError(t, err) + + err = c.validateCredentialApplication([]byte(`{`), nil) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to parse credential application:"+ + " JSON unmarshalling of verifiable presentation") + + err = c.validateCredentialApplication([]byte(credentialApplicationData), &cm.CredentialManifest{}) + require.Error(t, err) + require.Contains(t, err.Error(), "credential manifest: failed to validate credential application") + }) }) } @@ -4554,13 +4592,15 @@ func prepareCMAttachmentDescriptors(manifestID, presDefID string) *memcmdescript Schema: "https://www.w3.org/2018/credentials/examples/v1", }, }, - InputDesc: []*presexch.InputDescriptor{ - { - ID: presDefID, - Group: []string{"A"}, - Schema: []*presexch.Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), - }}, + PresentationDefinition: &presexch.PresentationDefinition{ + InputDescriptors: []*presexch.InputDescriptor{ + { + ID: presDefID, + Group: []string{"A"}, + Schema: []*presexch.Schema{{ + URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + }}, + }, }, }, } diff --git a/test/bdd/fixtures/testdata/manifest-config/cmdescriptors.json b/test/bdd/fixtures/testdata/manifest-config/cmdescriptors.json index 0d18c924..990a5de6 100644 --- a/test/bdd/fixtures/testdata/manifest-config/cmdescriptors.json +++ b/test/bdd/fixtures/testdata/manifest-config/cmdescriptors.json @@ -131,7 +131,8 @@ } } ], - "input_descriptor":[ + "presentation_definition": { + "input_descriptors":[ { "id":"prc_input", "name":"Permanent Resident Card", @@ -180,4 +181,5 @@ } ] } +} } \ No newline at end of file diff --git a/test/bdd/go.mod b/test/bdd/go.mod index 42f75dd5..5759d605 100644 --- a/test/bdd/go.mod +++ b/test/bdd/go.mod @@ -12,7 +12,7 @@ require ( github.com/cucumber/godog v0.9.0 github.com/fsouza/go-dockerclient v1.7.4 github.com/google/uuid v1.3.0 - github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 + github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 github.com/hyperledger/aries-framework-go-ext/component/vdr/orb v0.1.4-0.20211219215001-23cd75276fdc github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20220106195936-a9d6794663ed github.com/ory/hydra-client-go v1.4.10 diff --git a/test/bdd/go.sum b/test/bdd/go.sum index caf90c5a..c196301f 100644 --- a/test/bdd/go.sum +++ b/test/bdd/go.sum @@ -988,8 +988,8 @@ github.com/hyperledger/aries-framework-go v0.1.7/go.mod h1:uve8/q23AUnq4EM0vBkEr github.com/hyperledger/aries-framework-go v0.1.8-0.20210916154931-0196c3a2d102/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211201185059-733a3370f501/go.mod h1:uve8/q23AUnq4EM0vBkEr1lKZRC67q5RcaHXh0ZOt0Y= github.com/hyperledger/aries-framework-go v0.1.8-0.20211217135421-f68d5698237a/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851 h1:vKeoUmoIArG4rI+8JBrHdfG9FJeOosSA8A2/eyWqViM= -github.com/hyperledger/aries-framework-go v0.1.8-0.20220209203615-cbec86033851/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3 h1:waARdkRtU8tTQDOhsKZ1/Tdqzu3SiGw4dhho/1Y0P/4= +github.com/hyperledger/aries-framework-go v0.1.8-0.20220223054038-ed669027d7f3/go.mod h1:rBMOJVwyHyYbOqbb3IB/ExBkHyvFLht/W81s24GmjcE= github.com/hyperledger/aries-framework-go-ext/component/storage/couchdb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:FtlFhPHMyLORgrPpvWSmEQSNhLiwAQ4V6rqNPfuDj0o= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20210909220549-ce3a2ee13e22/go.mod h1:aiO9mXZBykIEwmgp9sSdpMuTw0P7b+ZFUltcIB9ZccY= github.com/hyperledger/aries-framework-go-ext/component/storage/mongodb v0.0.0-20211117223600-626fe1bae44d h1:h91rxhZkAjxcIwY08RxUTE+B8WxfiWbRHNl5X98+ziA= diff --git a/test/bdd/pkg/agent/agent_controller_steps.go b/test/bdd/pkg/agent/agent_controller_steps.go index 0bead6b0..b26e1596 100644 --- a/test/bdd/pkg/agent/agent_controller_steps.go +++ b/test/bdd/pkg/agent/agent_controller_steps.go @@ -889,6 +889,12 @@ func (a *Steps) ResolveDID(agent, didID string) (*did.Doc, error) { // SaveDID saves the did document. func (a *Steps) SaveDID(agent, friendlyName string, d *did.Doc) error { + requestURL := a.ControllerURLs[agent] + vdr.SaveDIDPath + + return saveDID(requestURL, friendlyName, d) +} + +func saveDID(requestURL, friendlyName string, d *did.Doc) error { bits, err := d.JSONBytes() if err != nil { return fmt.Errorf("failed to marshal did doc: %w", err) @@ -902,8 +908,6 @@ func (a *Steps) SaveDID(agent, friendlyName string, d *did.Doc) error { return fmt.Errorf("failed to marshal request to save did doc: %w", err) } - requestURL := a.ControllerURLs[agent] + vdr.SaveDIDPath - err = bddutil.SendHTTP(http.MethodPost, requestURL, request, nil) if err != nil { return fmt.Errorf("failed to save did at url %s: %w", requestURL, err) @@ -953,6 +957,11 @@ func (a *Steps) AcceptRequestPresentation(agent string, presentation *verifiable func (a *Steps) SignCredential(agent, signingDID string, cred *verifiable.Credential) (*verifiable.Credential, error) { destination := a.ControllerURLs[agent] + return signCredential(destination, signingDID, agent, cred) +} + +func signCredential(destination, signingDID, agent string, + cred *verifiable.Credential) (*verifiable.Credential, error) { inputBits, err := json.Marshal(cred) if err != nil { return nil, fmt.Errorf("'%s' failed to marshal credential: %w", agent, err) @@ -1061,13 +1070,17 @@ func (a *Steps) GeneratePresentation(agent, signingDID, verificationMethod strin // CreateKey creates a key of the given type. // Returns the key's ID and the public key material. func (a *Steps) CreateKey(agent string, t kms.KeyType) (id string, key []byte, err error) { + requestURL := a.ControllerURLs[agent] + kms2.CreateKeySetPath + + return createKey(requestURL, t) +} + +func createKey(requestURL string, t kms.KeyType) (id string, key []byte, err error) { request, err := json.Marshal(&kmscmd.CreateKeySetRequest{KeyType: string(t)}) if err != nil { return "", nil, fmt.Errorf("failed to marshal createKeySet request: %w", err) } - requestURL := a.ControllerURLs[agent] + kms2.CreateKeySetPath - response := &kmscmd.CreateKeySetResponse{} err = bddutil.SendHTTP(http.MethodPost, requestURL, request, response) diff --git a/test/bdd/pkg/agent/testdata/vc_prc.json b/test/bdd/pkg/agent/testdata/vc_prc.json index 0b2237bd..4648103d 100644 --- a/test/bdd/pkg/agent/testdata/vc_prc.json +++ b/test/bdd/pkg/agent/testdata/vc_prc.json @@ -1,13 +1,12 @@ { "@context":[ "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/citizenship/v1", + "https://trustbloc.github.io/context/vc/examples/citizenship-v1.jsonld", "https://w3id.org/security/bbs/v1" ], "id":"urn:uvci:af5vshde843jf831j128fj", "type":[ "VerifiableCredential", - "VaccinationCertificate", "PermanentResidentCard" ], "name":"Permanent Resident Card", @@ -21,13 +20,5 @@ "familyName":"Pasteur", "birthCountry":"Bahamas", "birthDate":"1958-07-17" - }, - "proof":{ - "type":"BbsBlsSignatureProof2020", - "created":"2021-02-18T23:04:28Z", - "nonce":"JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", - "proofPurpose":"assertionMethod", - "proofValue":"AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", - "verificationMethod":"did:example:123#key-1" } } \ No newline at end of file diff --git a/test/bdd/pkg/agent/wallet.go b/test/bdd/pkg/agent/wallet.go index fd15fe7d..51401a08 100644 --- a/test/bdd/pkg/agent/wallet.go +++ b/test/bdd/pkg/agent/wallet.go @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0 package agent import ( + "crypto/ed25519" + "crypto/tls" _ "embed" //nolint // This is needed to use go:embed "encoding/base64" "encoding/json" @@ -18,15 +20,24 @@ import ( "time" "github.com/cucumber/godog" + "github.com/google/uuid" + "github.com/hyperledger/aries-framework-go-ext/component/vdr/orb" "github.com/hyperledger/aries-framework-go/pkg/client/issuecredential" "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" + kms2 "github.com/hyperledger/aries-framework-go/pkg/controller/rest/kms" + "github.com/hyperledger/aries-framework-go/pkg/controller/rest/vdr" "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" issuecredsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential" "github.com/hyperledger/aries-framework-go/pkg/doc/cm" + "github.com/hyperledger/aries-framework-go/pkg/doc/did" + "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" + "github.com/hyperledger/aries-framework-go/pkg/kms" "github.com/hyperledger/aries-framework-go/pkg/wallet" + adapterdid "github.com/trustbloc/edge-adapter/pkg/did" issuerop "github.com/trustbloc/edge-adapter/pkg/restapi/issuer/operation" "github.com/trustbloc/edge-adapter/test/bdd/pkg/bddutil" "github.com/trustbloc/edge-adapter/test/bdd/pkg/context" @@ -46,8 +57,9 @@ const ( requestCredentialPath = walletOperationID + "/request-credential" // time constants - waitForResponseTimeout = 20 * time.Second - tokenExpiry = 20 * time.Minute + waitForResponseTimeout = 20 * time.Second + tokenExpiry = 20 * time.Minute + trustblocDIDMethodDomain = "testnet.orb.local" ) // nolint:gochecknoglobals @@ -348,18 +360,37 @@ func (a *walletSteps) initiateWACIIssuance(walletID, issuerID, auth, controller, return manifest, thID, nil } -func generatePresentationWithCredentialApplication(credentialManifest *cm.CredentialManifest) (*verifiable.Presentation, +func (a *walletSteps) generatePresentationWithCredentialApplication(credentialManifest *cm.CredentialManifest, + issuerID string) (*verifiable.Presentation, error) { presentationWithCA, err := cm.PresentCredentialApplication(credentialManifest) if err != nil { return nil, fmt.Errorf("failed to generate presentation with credential application : %w", err) } - cred := &verifiable.Credential{} + l, err := bddutil.DocumentLoader() + if err != nil { + return nil, fmt.Errorf("failed to init document loader: %w", err) + } + + cred, err := verifiable.ParseCredential(vcPRC, + verifiable.WithDisabledProofCheck(), + verifiable.WithJSONLDDocumentLoader(l)) + if err != nil { + return nil, fmt.Errorf("failed to parse vc : %w", err) + } - err = json.Unmarshal(vcPRC, cred) + // sign the permanent resident card VC + // TODO - the issuer adapter is incorrectly using their public TB DID to sign credential + // https://github.com/trustbloc/edge-adapter/issues/302 + signingDID, err := a.newTrustBlocDID(issuerID) + if err != nil { + return nil, fmt.Errorf("'%s' failed to create a new trustbloc DID: %w", issuerID, err) + } + // TODO this credential should ideally be signed by a different issuer + signedCredential, err := a.signCredential(issuerID, signingDID.ID, cred) if err != nil { - return nil, fmt.Errorf("failed to unmarshal credential : %w", err) + return nil, fmt.Errorf("'%s' failed to sign the VC: %w cred %v", issuerID, err, signedCredential) } presentationWithCA.AddCredentials(cred) @@ -369,7 +400,7 @@ func generatePresentationWithCredentialApplication(credentialManifest *cm.Creden func (a *walletSteps) concludeWACIIssuance(walletID, issuerID, auth, thID, controller string, manifest *cm.CredentialManifest) error { - presentation, err := generatePresentationWithCredentialApplication(manifest) + presentation, err := a.generatePresentationWithCredentialApplication(manifest, walletID) if err != nil { return fmt.Errorf("failed to generate credential application attachment: %w", err) } @@ -403,7 +434,7 @@ func (a *walletSteps) concludeWACIIssuance(walletID, issuerID, auth, thID, contr err = bddutil.SendHTTP(http.MethodPost, controller+requestCredentialPath, requestCredential, &interactionResponse) if err != nil { - return fmt.Errorf("'%s', failed to request credential from wallet : %w", walletID, err) + return fmt.Errorf("'%s', failed to request credential from wallet : %w, %s", walletID, err, string(rawPresentation)) } logger.Infof("wallet[%s] received credential fulfillment response: %+v", interactionResponse) @@ -697,3 +728,88 @@ func walletAuthKey(walletID string) string { func walletRedirectKey(walletID, issuerID string) string { return fmt.Sprintf("wallet_redirect_%s_%s", walletID, issuerID) } + +// SignCredential signs the credential. +func (a *walletSteps) signCredential(agent, signingDID string, + cred *verifiable.Credential) (*verifiable.Credential, error) { + destination := a.ControllerURLs[agent] + + return signCredential(destination, signingDID, agent, cred) +} + +// CreateKey creates a key of the given type. +// Returns the key's ID and the public key material. +func (a *walletSteps) createKey(agent string, t kms.KeyType) (id string, key []byte, err error) { + requestURL := a.ControllerURLs[agent] + kms2.CreateKeySetPath + + return createKey(requestURL, t) +} + +// SaveDID saves the did document. +func (a *walletSteps) saveDID(agent, friendlyName string, d *did.Doc) error { + requestURL := a.ControllerURLs[agent] + vdr.SaveDIDPath + + return saveDID(requestURL, friendlyName, d) +} + +// creating trustbloc did for signing pr card. This is just to simulate wallet has stored signed pr card to use for +// issuing driving license use case +func (a *walletSteps) newTrustBlocDID(agentID string) (*did.Doc, error) { + keys := [3]struct { + keyID string + bits []byte + }{} + + var err error + + for i := range keys { + keys[i].keyID, keys[i].bits, err = a.createKey(agentID, kms.ED25519Type) + if err != nil { + return nil, fmt.Errorf("'%s' failed to create a new key set: %w", agentID, err) + } + } + + orbClient, err := orb.New(nil, orb.WithDomain(trustblocDIDMethodDomain), + orb.WithTLSConfig(&tls.Config{RootCAs: a.bddContext.TLSConfig().RootCAs, MinVersion: tls.VersionTLS12})) + if err != nil { + return nil, fmt.Errorf("failed to init orb VDR %w", err) + } + + didDoc := did.Doc{} + + jwk, err := jwksupport.JWKFromKey(ed25519.PublicKey(keys[0].bits)) + if err != nil { + return nil, fmt.Errorf("failed to create jwk: %w", err) + } + + vm, err := did.NewVerificationMethodFromJWK(keys[0].keyID, adapterdid.JSONWebKey2020, "", jwk) + if err != nil { + return nil, fmt.Errorf("failed to create new vm: %w", err) + } + + didDoc.Authentication = append(didDoc.Authentication, *did.NewReferencedVerification(vm, did.Authentication)) + didDoc.AssertionMethod = append(didDoc.AssertionMethod, *did.NewReferencedVerification(vm, did.AssertionMethod)) + + docResolution, err := orbClient.Create(&didDoc, + vdrapi.WithOption(orb.RecoveryPublicKeyOpt, ed25519.PublicKey(keys[1].bits)), + vdrapi.WithOption(orb.UpdatePublicKeyOpt, ed25519.PublicKey(keys[2].bits)), + vdrapi.WithOption(orb.AnchorOriginOpt, "https://testnet.orb.local"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create new trustbloc did: %w", err) + } + + friendlyName := uuid.New().String() + + resolvedDoc, err := bddutil.ResolveDID(a.bddContext.VDRI, docResolution.DIDDocument.ID, 10) + if err != nil { + return nil, fmt.Errorf("failed to resolve did=%s err: %w", docResolution.DIDDocument.ID, err) + } + + err = a.saveDID(agentID, friendlyName, resolvedDoc) + if err != nil { + return nil, fmt.Errorf("failed to save new trustbloc did: %w", err) + } + + return resolvedDoc, nil +}