diff --git a/pkg/client/connection/client.go b/pkg/client/connection/client.go index 9cfb5885bc..9f956604cf 100644 --- a/pkg/client/connection/client.go +++ b/pkg/client/connection/client.go @@ -11,6 +11,7 @@ import ( "github.com/google/uuid" + "github.com/hyperledger/aries-framework-go/pkg/common/log" "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/middleware" "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/peerdid" @@ -22,6 +23,8 @@ import ( "github.com/hyperledger/aries-framework-go/spi/storage" ) +var logger = log.New("aries-framework/pkg/client/connection") + type provider interface { VDRegistry() vdr.Registry DIDRotator() *middleware.DIDCommMessageMiddleware @@ -80,6 +83,7 @@ func (c *Client) RotateDID(connectionID, signingKID string, opts ...RotateDIDOpt } // CreateConnectionV2 creates a DIDComm V2 connection with the given DID. +//nolint:funlen func (c *Client) CreateConnectionV2(myDID, theirDID string, opts ...CreateConnectionOption) (string, error) { theirDocRes, err := c.vdr.Resolve(theirDID) if err != nil { @@ -105,16 +109,31 @@ func (c *Client) CreateConnectionV2(myDID, theirDID string, opts ...CreateConnec connID := uuid.New().String() + uri, err := destination.ServiceEndpoint.URI() + if err != nil { + logger.Debugf("create destination from serviceEndpoint.URI() failed: %w, using value: %s", err, uri) + } + + accept, err := destination.ServiceEndpoint.Accept() + if err != nil { + logger.Debugf("create destination from serviceEndpoint.Accept() failed: %w, using value %v", err, accept) + } + + routingKeys, err := destination.ServiceEndpoint.RoutingKeys() + if err != nil { + logger.Debugf("create destination from serviceEndpoint.RoutingKeys() failed: %w, using value %v", err, routingKeys) + } + connRec := connection.Record{ ConnectionID: connID, State: connection.StateNameCompleted, TheirDID: theirDID, MyDID: myDID, - ServiceEndPoint: model.Endpoint{ - URI: destination.ServiceEndpoint.URI, - Accept: destination.ServiceEndpoint.Accept, - RoutingKeys: destination.ServiceEndpoint.RoutingKeys, - }, + ServiceEndPoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ + URI: uri, + Accept: accept, + RoutingKeys: routingKeys, + }}), RecipientKeys: destination.RecipientKeys, Namespace: connection.MyNSPrefix, DIDCommVersion: service.V2, diff --git a/pkg/client/didexchange/client.go b/pkg/client/didexchange/client.go index 3f21401633..b39603e645 100644 --- a/pkg/client/didexchange/client.go +++ b/pkg/client/didexchange/client.go @@ -437,6 +437,8 @@ func (c *Client) CreateConnection(myDID string, theirDID *did.Doc, options ...Co conn.ServiceEndPoint = destination.ServiceEndpoint conn.RecipientKeys = destination.RecipientKeys + conn.RoutingKeys = destination.RoutingKeys + conn.MediaTypeProfiles = destination.MediaTypeProfiles err = c.didexchangeSvc.CreateConnection(conn.Record, theirDID) if err != nil { diff --git a/pkg/client/didexchange/client_test.go b/pkg/client/didexchange/client_test.go index 474ad7226f..718b8a7481 100644 --- a/pkg/client/didexchange/client_test.go +++ b/pkg/client/didexchange/client_test.go @@ -773,13 +773,13 @@ func TestClient_CreateConnection(t *testing.T) { require.NoError(t, err) // empty ServiceEndpoint to trigger CreateDestination error - theirDID.Service[0].ServiceEndpoint.URI = "" + theirDID.Service[0].ServiceEndpoint = model.NewDIDCommV1Endpoint("") _, err = c.CreateConnection(myDID.ID, theirDID, WithTheirLabel(label), WithThreadID(threadID), WithParentThreadID(parentThreadID), WithInvitationID(invitationID), WithInvitationDID(invitationDID), WithImplicit(implicit)) require.Contains(t, err.Error(), "createConnection: failed to create destination: "+ - "create destination: no service endpoint URI on didcomm service block in diddoc:") + "create destination: service endpoint URI on didcomm v1 service block in diddoc error:") }) } @@ -1516,7 +1516,7 @@ func newPeerDID(t *testing.T) *did.Doc { d, err := ctx.VDRegistry().Create( peer.DIDMethod, &did.Doc{Service: []did.Service{{ Type: "did-communication", - ServiceEndpoint: model.Endpoint{URI: "http://agent.example.com/didcomm"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://agent.example.com/didcomm"), }}, VerificationMethod: []did.VerificationMethod{getSigningKey()}}) require.NoError(t, err) diff --git a/pkg/client/messaging/client_test.go b/pkg/client/messaging/client_test.go index f33e8bf2ad..c93d99581b 100644 --- a/pkg/client/messaging/client_test.go +++ b/pkg/client/messaging/client_test.go @@ -172,11 +172,9 @@ func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo { name: "send message to destination", option: SendByDestination(&service.Destination{ - RecipientKeys: []string{"test"}, - ServiceEndpoint: model.Endpoint{ - URI: "sdfsdf", - RoutingKeys: []string{"test"}, - }, + RecipientKeys: []string{"test"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("dfsdf"), + RoutingKeys: []string{"test"}, }), }, } @@ -269,11 +267,9 @@ func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo name: "send message to destination", option: []SendMessageOpions{ SendByDestination(&service.Destination{ - RecipientKeys: []string{"test"}, - ServiceEndpoint: model.Endpoint{ - URI: "sdfsdf", - RoutingKeys: []string{"test"}, - }, + RecipientKeys: []string{"test"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"), + RoutingKeys: []string{"test"}, }), WaitForResponse(context.Background(), "sample-response-type"), }, @@ -422,11 +418,9 @@ func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo { name: "send message to destination - failure 1", option: SendByDestination(&service.Destination{ - RecipientKeys: []string{"test"}, - ServiceEndpoint: model.Endpoint{ - URI: "sdfsdf", - RoutingKeys: []string{"test"}, - }, + RecipientKeys: []string{"test"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"), + RoutingKeys: []string{"test"}, }), messenger: &mocksvc.MockMessenger{ErrSendToDestination: fmt.Errorf("sample-err-01")}, errorMsg: "sample-err-01", @@ -435,11 +429,9 @@ func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo name: "send message to destination - failure 2", kms: &mockkms.KeyManager{CrAndExportPubKeyErr: fmt.Errorf("sample-kmserr-01")}, option: SendByDestination(&service.Destination{ - RecipientKeys: []string{"test"}, - ServiceEndpoint: model.Endpoint{ - URI: "sdfsdf", - RoutingKeys: []string{"test"}, - }, + RecipientKeys: []string{"test"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"), + RoutingKeys: []string{"test"}, }), errorMsg: "sample-kmserr-01", }, @@ -454,7 +446,7 @@ func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo option: SendByTheirDID("theirDID-001"), vdr: &mockvdr.MockVDRegistry{ ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (doc *did.DocResolution, e error) { - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, errorMsg: "invalid payload data format", diff --git a/pkg/client/outofband/client.go b/pkg/client/outofband/client.go index 9a5fcbc104..0697bac1a4 100644 --- a/pkg/client/outofband/client.go +++ b/pkg/client/outofband/client.go @@ -370,12 +370,13 @@ func validateServices(svcs ...interface{}) error { // DidDocServiceFunc returns a function that returns a DID doc `service` entry. // Used when no service entries are specified when creating messages. -//nolint:funlen +//nolint:funlen,gocyclo func (c *Client) didServiceBlockFunc(p Provider) func(routerConnID string, accept []string) (*did.Service, error) { return func(routerConnID string, accept []string) (*did.Service, error) { var ( keyType kms.KeyType didCommServiceType string + sp model.Endpoint ) useDIDCommV2 := isDIDCommV2(accept) @@ -384,9 +385,14 @@ func (c *Client) didServiceBlockFunc(p Provider) func(routerConnID string, accep if useDIDCommV2 { keyType = p.KeyAgreementType() didCommServiceType = vdr.DIDCommV2ServiceType + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ + URI: p.ServiceEndpoint(), + Accept: p.MediaTypeProfiles(), + }}) } else { keyType = p.KeyType() didCommServiceType = vdr.DIDCommServiceType + sp = model.NewDIDCommV1Endpoint(p.ServiceEndpoint()) } if string(keyType) == "" { @@ -418,7 +424,7 @@ func (c *Client) didServiceBlockFunc(p Provider) func(routerConnID string, accep ID: uuid.New().String(), Type: didCommServiceType, RecipientKeys: []string{didKey}, - ServiceEndpoint: model.Endpoint{URI: p.ServiceEndpoint()}, + ServiceEndpoint: sp, }, nil } @@ -432,15 +438,33 @@ func (c *Client) didServiceBlockFunc(p Provider) func(routerConnID string, accep return nil, fmt.Errorf("didServiceBlockFunc: create invitation - failed to add key to the router : %w", err) } - return &did.Service{ - ID: uuid.New().String(), - Type: didCommServiceType, - RecipientKeys: []string{didKey}, - ServiceEndpoint: model.Endpoint{ + var svc *did.Service + + if useDIDCommV2 { + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ URI: serviceEndpoint, + Accept: accept, RoutingKeys: routingKeys, - }, - }, nil + }}) + svc = &did.Service{ + ID: uuid.New().String(), + Type: didCommServiceType, + RecipientKeys: []string{didKey}, + ServiceEndpoint: sp, + } + } else { + sp = model.NewDIDCommV1Endpoint(serviceEndpoint) + svc = &did.Service{ + ID: uuid.New().String(), + Type: didCommServiceType, + RecipientKeys: []string{didKey}, + ServiceEndpoint: sp, + RoutingKeys: routingKeys, + Accept: accept, + } + } + + return svc, nil } } diff --git a/pkg/client/outofband/client_test.go b/pkg/client/outofband/client_test.go index 7fdaf492bb..b598910249 100644 --- a/pkg/client/outofband/client_test.go +++ b/pkg/client/outofband/client_test.go @@ -75,15 +75,13 @@ func TestCreateInvitation(t *testing.T) { }) t.Run("includes the diddoc Service block returned by provider", func(t *testing.T) { expected := &did.Service{ - ID: uuid.New().String(), - Type: uuid.New().String(), - Priority: 0, - RecipientKeys: []string{uuid.New().String()}, - ServiceEndpoint: commonmodel.Endpoint{ - URI: uuid.New().String(), - RoutingKeys: []string{uuid.New().String()}, - }, - Properties: nil, + ID: uuid.New().String(), + Type: uuid.New().String(), + Priority: 0, + RecipientKeys: []string{uuid.New().String()}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint(uuid.New().String()), + RoutingKeys: []string{uuid.New().String()}, + Properties: nil, } c, err := New(withTestProvider()) require.NoError(t, err) @@ -129,26 +127,31 @@ func TestCreateInvitation(t *testing.T) { c.didDocSvcFunc = func(conn string, accept []string) (*did.Service, error) { require.Equal(t, expectedConn, conn) - var serviceType string + var svc *did.Service if isDIDCommV2(accept) { - serviceType = vdr.DIDCommV2ServiceType + svc = &did.Service{ + ServiceEndpoint: commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{ + {URI: expectedConn, Accept: accept}, + }), + Type: vdr.DIDCommV2ServiceType, + } } else { - serviceType = vdr.DIDCommServiceType + svc = &did.Service{ + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint(expectedConn), + Accept: accept, + Type: vdr.DIDCommServiceType, + } } - return &did.Service{ - ServiceEndpoint: commonmodel.Endpoint{ - URI: expectedConn, - Accept: accept, - }, - Type: serviceType, - }, nil + return svc, nil } inv, err := c.CreateInvitation(nil, WithRouterConnections(expectedConn)) require.NoError(t, err) - require.Equal(t, expectedConn, inv.Services[0].(*did.Service).ServiceEndpoint.URI) + uri, err := inv.Services[0].(*did.Service).ServiceEndpoint.URI() + require.NoError(t, err) + require.Equal(t, expectedConn, uri) }) t.Run("WithGoal", func(t *testing.T) { c, err := New(withTestProvider()) @@ -164,15 +167,13 @@ func TestCreateInvitation(t *testing.T) { c, err := New(withTestProvider()) require.NoError(t, err) expected := &did.Service{ - ID: uuid.New().String(), - Type: uuid.New().String(), - Priority: 0, - RecipientKeys: []string{uuid.New().String()}, - ServiceEndpoint: commonmodel.Endpoint{ - URI: uuid.New().String(), - RoutingKeys: []string{uuid.New().String()}, - }, - Properties: nil, + ID: uuid.New().String(), + Type: uuid.New().String(), + Priority: 0, + RecipientKeys: []string{uuid.New().String()}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint(uuid.New().String()), + RoutingKeys: []string{uuid.New().String()}, + Properties: nil, } inv, err := c.CreateInvitation([]interface{}{expected}) require.NoError(t, err) @@ -193,15 +194,13 @@ func TestCreateInvitation(t *testing.T) { require.NoError(t, err) didRef := "did:example:234" svc := &did.Service{ - ID: uuid.New().String(), - Type: uuid.New().String(), - Priority: 0, - RecipientKeys: []string{uuid.New().String()}, - ServiceEndpoint: commonmodel.Endpoint{ - URI: uuid.New().String(), - RoutingKeys: []string{uuid.New().String()}, - }, - Properties: nil, + ID: uuid.New().String(), + Type: uuid.New().String(), + Priority: 0, + RecipientKeys: []string{uuid.New().String()}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint(uuid.New().String()), + RoutingKeys: []string{uuid.New().String()}, + Properties: nil, } inv, err := c.CreateInvitation([]interface{}{svc, didRef}) require.NoError(t, err) diff --git a/pkg/common/model/endpoint.go b/pkg/common/model/endpoint.go index 9916c154ea..8a074f703a 100644 --- a/pkg/common/model/endpoint.go +++ b/pkg/common/model/endpoint.go @@ -6,8 +6,32 @@ SPDX-License-Identifier: Apache-2.0 package model -// Endpoint contains endpoint specific content. +import ( + "encoding/json" + "fmt" + "net/url" +) + +// ServiceEndpoint api for fetching ServiceEndpoint content based off of a DIDComm V1, V2 or DIDCore format. +type ServiceEndpoint interface { + URI() (string, error) + Accept() ([]string, error) + RoutingKeys() ([]string, error) +} + +// Endpoint contains endpoint specific content. Content of ServiceEndpoint api above will be used by priority: +// 1- DIDcomm V2 +// 2- DIDComm V1 +// 3- DIDCore +// To force lower priority endpoint content, avoid setting higher priority data during Unmarshal() execution. type Endpoint struct { + rawDIDCommV2 []DIDCommV2Endpoint + rawDIDCommV1 string + rawObj interface{} +} + +// DIDCommV2Endpoint contains ServiceEndpoint data specifically for DIDcommV2 and is wrapped in Endpoint as an array. +type DIDCommV2Endpoint struct { // URI contains the endpoint URI. URI string `json:"uri"` // Accept contains the MediaType profiles accepted by this endpoint. @@ -15,3 +39,151 @@ type Endpoint struct { // RoutingKeys contains the list of keys trusted as routing keys for the mediators/routers of this endpoint. RoutingKeys []string `json:"routingKeys,omitempty"` } + +// NewDIDCommV2Endpoint creates a DIDCommV2 endpoint with the given array of endpoints. At the time of writing this +// comment, only the first endpoint is effective in the API. Additional logic is required to use a different index. +func NewDIDCommV2Endpoint(endpoints []DIDCommV2Endpoint) Endpoint { + endpoint := Endpoint{rawDIDCommV2: []DIDCommV2Endpoint{}} + endpoint.rawDIDCommV2 = append(endpoint.rawDIDCommV2, endpoints...) + + return endpoint +} + +// NewDIDCommV1Endpoint creates a DIDCommV1 endpoint. +func NewDIDCommV1Endpoint(uri string) Endpoint { + return Endpoint{ + rawDIDCommV1: uri, + } +} + +// NewDIDCoreEndpoint creates a generic DIDCore endpoint. +func NewDIDCoreEndpoint(genericEndpoint interface{}) Endpoint { + return Endpoint{ + rawObj: genericEndpoint, + } +} + +// URI is the URI of a service endpoint. +// It will return the value based on the underlying endpoint type in the following order: +// 1- DIDComm V2 URI (currently the first element's URI). TODO enhance API to pass in an optional index. +// 2- DIDComm V1 URI +// 3- DIDCore's first element printed as string for now. (not used by AFGO at the time of this writing, but can be +// enhanced if needed). +func (s *Endpoint) URI() (string, error) { + // TODO for now, returning URI of first element. Add mechanism to fetch from appropriate index. + if len(s.rawDIDCommV2) > 0 { + return s.rawDIDCommV2[0].URI, nil + } + + if s.rawDIDCommV1 != "" { + return stripQuotes(s.rawDIDCommV1), nil + } + + if s.rawObj != nil { + switch o := s.rawObj.(type) { + case []string: + return o[0], nil + case [][]byte: + return string(o[0]), nil + case []interface{}: + return fmt.Sprintf("%s", o[0]), nil + default: + return "", fmt.Errorf("unrecognized DIDCore endpoint object %s", o) + } + } + + return "", fmt.Errorf("endpoint URI not found") +} + +// Accept is the DIDComm V2 Accept field of a service endpoint. +func (s *Endpoint) Accept() ([]string, error) { + // TODO for now, returning Accept of first element. Add mechanism to fetch appropriate value. + if len(s.rawDIDCommV2) > 0 { + return s.rawDIDCommV2[0].Accept, nil + } + + return nil, fmt.Errorf("endpoint Accept not found") +} + +// RoutingKeys is the DIDComm V2 RoutingKeys field of a service endpoint. +func (s *Endpoint) RoutingKeys() ([]string, error) { + // TODO for now, returning RoutingKeys of first element. Add mechanism to fetch appropriate value. + if len(s.rawDIDCommV2) > 0 { + return s.rawDIDCommV2[0].RoutingKeys, nil + } + + return nil, fmt.Errorf("endpoint RoutingKeys not found") +} + +// MarshalJSON marshals the content of Endpoint into a valid JSON []byte. Order of data is: +// 1. DIDCommV2 format if found +// 2. DIDCommV1 format if found +// 3. DIDCore generic format if found +// 4. JSON "Null" as fallback. +func (s *Endpoint) MarshalJSON() ([]byte, error) { + if len(s.rawDIDCommV2) > 0 { + return json.Marshal(s.rawDIDCommV2) + } + + if s.rawDIDCommV1 != "" { + return []byte(fmt.Sprintf("%q", s.rawDIDCommV1)), nil + } + + if s.rawObj != nil { + return json.Marshal(s.rawObj) + } + + // for existing connections, Endpoint can be empty, therefore don't fail marshalling here and + // return JSON null value instead. + return []byte("null"), nil +} + +// UnmarshalJSON unmarshals data into Endpoint based on its format. +func (s *Endpoint) UnmarshalJSON(data []byte) error { + s.rawDIDCommV2 = []DIDCommV2Endpoint{} + if err := json.Unmarshal(data, &s.rawDIDCommV2); err == nil { + s.rawDIDCommV1 = "" + s.rawObj = nil + + return nil + } + + if ok := isURL(string(data)); ok { + s.rawDIDCommV1 = stripQuotes(string(data)) + s.rawDIDCommV2 = nil + s.rawObj = nil + + return nil + } + + if err := json.Unmarshal(data, &s.rawObj); err == nil { + s.rawDIDCommV1 = "" + s.rawDIDCommV2 = nil + + return nil + } + + return fmt.Errorf("endpoint data is not supported") +} + +func isURL(str string) bool { + str = stripQuotes(str) + + u, err := url.Parse(str) + + return err == nil && u.Scheme != "" && u.Host != "" +} + +func stripQuotes(str string) string { + if len(str) > 0 { + if str[0] == '"' { + str = str[1:] + } + + if str[len(str)-1] == '"' { + str = str[:len(str)-1] + } + } + + return str +} diff --git a/pkg/common/model/endpoint_test.go b/pkg/common/model/endpoint_test.go new file mode 100644 index 0000000000..f489241798 --- /dev/null +++ b/pkg/common/model/endpoint_test.go @@ -0,0 +1,133 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package model + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewEndpoint(t *testing.T) { + uri := "uri" + accept := []string{"accept"} + routingkeys := []string{"key1"} + + didCommV2Endpoint := Endpoint{ + rawDIDCommV2: []DIDCommV2Endpoint{{ + URI: uri, + Accept: accept, + RoutingKeys: routingkeys, + }}, + } + + ep := NewDIDCommV2Endpoint([]DIDCommV2Endpoint{{uri, accept, routingkeys}}) + require.EqualValues(t, didCommV2Endpoint, ep) + + didCommV1Endpoint := Endpoint{ + rawDIDCommV1: uri, + } + + ep = NewDIDCommV1Endpoint(uri) + require.EqualValues(t, didCommV1Endpoint, ep) + + didCoreEndpoint := Endpoint{ + rawObj: []string{uri, "uri2"}, + } + + ep = NewDIDCoreEndpoint([]string{uri, "uri2"}) + require.EqualValues(t, didCoreEndpoint, ep) + + ep = NewDIDCommV1Endpoint("") + require.EqualValues(t, Endpoint{}, ep) + + m, err := ep.MarshalJSON() + require.NoError(t, err) + require.Equal(t, m, []byte("null")) + + err = ep.UnmarshalJSON([]byte("")) + require.EqualError(t, err, "endpoint data is not supported") +} + +func TestEndpoint_MarshalUnmarshalJSON(t *testing.T) { + testCases := []struct { + name string + endpoint interface{} + expectedValue interface{} + err error + }{ + { + name: "marshal Endpoint for DIDComm V2", + endpoint: Endpoint{ + rawDIDCommV2: []DIDCommV2Endpoint{{ + URI: "https://agent.example.com/", + Accept: []string{"didcomm/v2"}, + }}, + }, + expectedValue: []byte(`[{"uri":"https://agent.example.com/","accept":["didcomm/v2"]}]`), + }, + { + name: "marshal Endpoint for DIDcomm V1", + endpoint: Endpoint{ + rawDIDCommV1: "https://agent.example.com/", + }, + expectedValue: []byte(fmt.Sprintf("%q", "https://agent.example.com/")), + err: nil, + }, + { + name: "marshal random DIDCore Endpoint (neither DIDcomm V1 nor V2) as interface{}", + endpoint: Endpoint{ + rawObj: []interface{}{"some random endpoint", "some other endpoint"}, + }, + expectedValue: []byte(`["some random endpoint","some other endpoint"]`), + err: nil, + }, + } + + for _, tc := range testCases { + ep, ok := tc.endpoint.(Endpoint) + if !ok { + continue + } + + mep, err := ep.MarshalJSON() + require.NoError(t, err) + require.EqualValues(t, tc.expectedValue, mep) + + newEP := Endpoint{} + err = newEP.UnmarshalJSON(mep) + require.NoError(t, err) + require.EqualValues(t, ep, newEP) + + uri, err := newEP.URI() + require.NoError(t, err) + + switch tc.name { + case "marshal Endpoint for DIDComm V2": + require.Equal(t, ep.rawDIDCommV2[0].URI, uri) + + accept, e := newEP.Accept() + require.NoError(t, e) + require.Equal(t, ep.rawDIDCommV2[0].Accept, accept) + + routingKeys, e := newEP.RoutingKeys() + require.NoError(t, e) + require.Equal(t, ep.rawDIDCommV2[0].RoutingKeys, routingKeys) + case "marshal Endpoint for DIDcomm V1": + require.Equal(t, ep.rawDIDCommV1, uri) + + _, err = newEP.Accept() + require.EqualError(t, err, "endpoint Accept not found") + + _, err = newEP.RoutingKeys() + require.EqualError(t, err, "endpoint RoutingKeys not found") + case "marshal random DIDCore Endpoint (neither DIDcomm V1 nor V2) as interface{}": + require.Equal(t, ep.rawObj.([]interface{})[0], uri) + } + } +} diff --git a/pkg/controller/command/didexchange/command_test.go b/pkg/controller/command/didexchange/command_test.go index c8e60b305b..f37dcab3c4 100644 --- a/pkg/controller/command/didexchange/command_test.go +++ b/pkg/controller/command/didexchange/command_test.go @@ -989,10 +989,8 @@ func newPeerDID(t *testing.T) *did.Doc { d, err := ctx.VDRegistry().Create( peer.DIDMethod, &did.Doc{Service: []did.Service{{ - Type: vdr.DIDCommServiceType, - ServiceEndpoint: model.Endpoint{ - URI: "http://agent.example.com/didcomm", - }, + Type: vdr.DIDCommServiceType, + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://agent.example.com/didcomm"), }}, VerificationMethod: []did.VerificationMethod{getSigningKey()}}, ) require.NoError(t, err) diff --git a/pkg/controller/command/messaging/command.go b/pkg/controller/command/messaging/command.go index 1e5a7779ad..c364c82c1c 100644 --- a/pkg/controller/command/messaging/command.go +++ b/pkg/controller/command/messaging/command.go @@ -186,13 +186,23 @@ func (o *Command) Send(rw io.Writer, req io.Reader) command.Error { } var destination *service.Destination + if request.ServiceEndpointDestination != nil { - destination = &service.Destination{ - ServiceEndpoint: model.Endpoint{ - URI: request.ServiceEndpointDestination.ServiceEndpoint, - RoutingKeys: request.ServiceEndpointDestination.RoutingKeys, - }, - RecipientKeys: request.ServiceEndpointDestination.RecipientKeys, + routingKeys := request.ServiceEndpointDestination.RoutingKeys + if len(routingKeys) > 0 { + destination = &service.Destination{ + ServiceEndpoint: model.NewDIDCommV1Endpoint(request.ServiceEndpointDestination.ServiceEndpoint), + RoutingKeys: routingKeys, + RecipientKeys: request.ServiceEndpointDestination.RecipientKeys, + } + } else { + destination = &service.Destination{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ + URI: request.ServiceEndpointDestination.ServiceEndpoint, + RoutingKeys: routingKeys, + }}), + RecipientKeys: request.ServiceEndpointDestination.RecipientKeys, + } } } diff --git a/pkg/controller/command/messaging/command_test.go b/pkg/controller/command/messaging/command_test.go index 8fd1826b88..0b1aa51ffb 100644 --- a/pkg/controller/command/messaging/command_test.go +++ b/pkg/controller/command/messaging/command_test.go @@ -561,7 +561,7 @@ func TestCommand_Send(t *testing.T) { requestJSON: `{"message_body": "sample-input", "their_did": "theirDID-001"}`, vdr: &mockvdr.MockVDRegistry{ ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (doc *did.DocResolution, e error) { - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, errorCode: SendMsgError, diff --git a/pkg/controller/rest/didexchange/operation_test.go b/pkg/controller/rest/didexchange/operation_test.go index b5c45feb8a..e53ebfc3e4 100644 --- a/pkg/controller/rest/didexchange/operation_test.go +++ b/pkg/controller/rest/didexchange/operation_test.go @@ -581,10 +581,8 @@ func newPeerDID(t *testing.T) *did.Doc { d, err := ctx.VDRegistry().Create( peer.DIDMethod, &did.Doc{Service: []did.Service{{ - Type: vdr.DIDCommServiceType, - ServiceEndpoint: model.Endpoint{ - URI: "http://agent.example.com/didcomm", - }, + Type: vdr.DIDCommServiceType, + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://agent.example.com/didcomm"), }}, VerificationMethod: []did.VerificationMethod{getSigningKey()}}, ) require.NoError(t, err) diff --git a/pkg/controller/rest/messaging/operation_test.go b/pkg/controller/rest/messaging/operation_test.go index 12c7d2f878..ef865aa4f9 100644 --- a/pkg/controller/rest/messaging/operation_test.go +++ b/pkg/controller/rest/messaging/operation_test.go @@ -583,7 +583,7 @@ func TestOperation_Send(t *testing.T) { requestJSON: `{"message_body": "sample-input", "their_did": "theirDID-001"}`, vdr: &mockvdr.MockVDRegistry{ ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, httpErrCode: http.StatusInternalServerError, diff --git a/pkg/didcomm/common/middleware/middleware.go b/pkg/didcomm/common/middleware/middleware.go index 09d3e188e8..7029e8702d 100644 --- a/pkg/didcomm/common/middleware/middleware.go +++ b/pkg/didcomm/common/middleware/middleware.go @@ -217,16 +217,14 @@ func (h *DIDCommMessageMiddleware) handleInboundInvitationAcceptance(senderDID, // if we created an invitation with this DID, and have no connection, we create a connection. rec = &connection.Record{ - ConnectionID: uuid.New().String(), - MyDID: recipientDID, - TheirDID: senderDID, - InvitationID: inv.ID, - State: connection.StateNameCompleted, - Namespace: connection.MyNSPrefix, - ServiceEndPoint: model.Endpoint{ - Accept: h.mediaTypeProfiles, - }, - DIDCommVersion: didcomm.V2, + ConnectionID: uuid.New().String(), + MyDID: recipientDID, + TheirDID: senderDID, + InvitationID: inv.ID, + State: connection.StateNameCompleted, + Namespace: connection.MyNSPrefix, + ServiceEndPoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{Accept: h.mediaTypeProfiles}}), + DIDCommVersion: didcomm.V2, } err = h.connStore.SaveConnectionRecord(rec) diff --git a/pkg/didcomm/common/service/destination.go b/pkg/didcomm/common/service/destination.go index 41d1a17259..68b1f8288e 100644 --- a/pkg/didcomm/common/service/destination.go +++ b/pkg/didcomm/common/service/destination.go @@ -18,7 +18,9 @@ import ( type Destination struct { RecipientKeys []string ServiceEndpoint model.Endpoint + RoutingKeys []string TransportReturnRoute string + MediaTypeProfiles []string DIDDoc *did.Doc } diff --git a/pkg/didcomm/common/service/destination_default.go b/pkg/didcomm/common/service/destination_default.go index 026b701f09..0e2260078d 100644 --- a/pkg/didcomm/common/service/destination_default.go +++ b/pkg/didcomm/common/service/destination_default.go @@ -1,3 +1,4 @@ +//go:build !ACAPyInterop // +build !ACAPyInterop /* @@ -18,8 +19,15 @@ import ( // CreateDestination makes a DIDComm Destination object from a DID Doc as per the DIDComm service conventions: // https://github.com/hyperledger/aries-rfcs/blob/master/features/0067-didcomm-diddoc-conventions/README.md. -//nolint:gocyclo,funlen +//nolint:gocyclo,funlen,gocognit func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) { + var ( + sp model.Endpoint + accept, routingKeys []string + uri string + err error + ) + // try DIDComm V2 and use it if found, else use default DIDComm v1 bloc. didCommService, ok := diddoc.LookupService(didDoc, didCommV2ServiceType) if ok { //nolint:nestif @@ -43,17 +51,32 @@ func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) { didCommService.RecipientKeys = recKeys // if Accept is missing, ensure DIDCommV2 is at least added for packer selection based on MediaTypeProfile. - if len(didCommService.ServiceEndpoint.Accept) == 0 { - didCommService.ServiceEndpoint.Accept = []string{defaultDIDCommV2Profile} + if accept, err = didCommService.ServiceEndpoint.Accept(); len(accept) == 0 || err != nil { + accept = []string{defaultDIDCommV2Profile} + } + + uri, err = didCommService.ServiceEndpoint.URI() + if err != nil { // uri is required. + return nil, fmt.Errorf("create destination: service endpoint URI for didcomm v2 service block "+ + "error: %+v, %w", didDoc, err) } - } else { + + routingKeys, err = didCommService.ServiceEndpoint.RoutingKeys() + if err != nil { // routingKeys can be optional. + routingKeys = nil + } + + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: uri, Accept: accept, RoutingKeys: routingKeys}}) + } else { // didcomm v1 service didCommService, ok = diddoc.LookupService(didDoc, didCommServiceType) if !ok { return nil, fmt.Errorf("create destination: missing DID doc service") } - if didCommService.ServiceEndpoint.URI == "" { - return nil, fmt.Errorf("create destination: no service endpoint URI on didcomm service block in diddoc: %+v", didDoc) + uri, err = didCommService.ServiceEndpoint.URI() + if err != nil { // uri is required. + return nil, fmt.Errorf("create destination: service endpoint URI on didcomm v1 service block "+ + "in diddoc error: %+v, %w", didDoc, err) } if len(didCommService.RecipientKeys) == 0 { @@ -68,18 +91,18 @@ func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) { } // if Accept is missing, ensure DIDCommV1 is at least added for packer selection based on MediaTypeProfile. - if len(didCommService.ServiceEndpoint.Accept) == 0 { - didCommService.ServiceEndpoint.Accept = []string{defaultDIDCommProfile} + if len(didCommService.Accept) == 0 { + didCommService.Accept = []string{defaultDIDCommProfile} } + + sp = model.NewDIDCommV1Endpoint(uri) } return &Destination{ - RecipientKeys: didCommService.RecipientKeys, - ServiceEndpoint: model.Endpoint{ - URI: didCommService.ServiceEndpoint.URI, - RoutingKeys: didCommService.ServiceEndpoint.RoutingKeys, - Accept: didCommService.ServiceEndpoint.Accept, - }, - DIDDoc: didDoc, + RecipientKeys: didCommService.RecipientKeys, + ServiceEndpoint: sp, + RoutingKeys: didCommService.RoutingKeys, + MediaTypeProfiles: didCommService.Accept, + DIDDoc: didDoc, }, nil } diff --git a/pkg/didcomm/common/service/destination_interop.go b/pkg/didcomm/common/service/destination_interop.go index 8cebfbfd26..0039ce3ab2 100644 --- a/pkg/didcomm/common/service/destination_interop.go +++ b/pkg/didcomm/common/service/destination_interop.go @@ -1,3 +1,4 @@ +//go:build ACAPyInterop // +build ACAPyInterop /* @@ -35,7 +36,7 @@ func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) { } } - if didCommService.ServiceEndpoint == "" { + if uri, err := didCommService.ServiceEndpoint.URI(); uri == "" || err != nil { return nil, fmt.Errorf("create destination: no service endpoint on didcomm service block in diddoc: %+v", didDoc) } diff --git a/pkg/didcomm/common/service/destination_test.go b/pkg/didcomm/common/service/destination_test.go index bd34db3916..64220688d0 100644 --- a/pkg/didcomm/common/service/destination_test.go +++ b/pkg/didcomm/common/service/destination_test.go @@ -86,7 +86,7 @@ func TestGetDestinationFromDID(t *testing.T) { t.Run("fails if the service endpoint is missing", func(t *testing.T) { diddoc := createDIDDoc() for i := range diddoc.Service { - diddoc.Service[i].ServiceEndpoint.URI = "" + diddoc.Service[i].ServiceEndpoint = model.NewDIDCommV1Endpoint("") } vdr := &mockvdr.MockVDRegistry{ResolveValue: diddoc} _, err := GetDestination(diddoc.ID, vdr) @@ -114,16 +114,18 @@ func TestGetDestinationFromDID(t *testing.T) { func TestPrepareDestination(t *testing.T) { t.Run("successfully prepared destination", func(t *testing.T) { - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) dest, err := CreateDestination(doc) require.NoError(t, err) require.NotNil(t, dest) - require.Equal(t, dest.ServiceEndpoint.URI, "https://localhost:8090") - require.EqualValues(t, doc.Service[0].ServiceEndpoint.RoutingKeys, dest.ServiceEndpoint.RoutingKeys) + uri, err := dest.ServiceEndpoint.URI() + require.NoError(t, err) + require.Equal(t, uri, "https://localhost:8090") + require.EqualValues(t, doc.Service[0].RoutingKeys, dest.RoutingKeys) }) t.Run("error with destination having recipientKeys not did:keys", func(t *testing.T) { - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) doc.Service[0].RecipientKeys = []string{"badKey"} dest, err := CreateDestination(doc) @@ -132,7 +134,7 @@ func TestPrepareDestination(t *testing.T) { }) t.Run("error while getting service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service = nil dest, err := CreateDestination(didDoc) @@ -142,7 +144,7 @@ func TestPrepareDestination(t *testing.T) { }) t.Run("error while getting recipient keys from did doc", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service[0].RecipientKeys = []string{} recipientKeys, ok := did.LookupDIDCommRecipientKeys(didDoc) @@ -183,15 +185,11 @@ func createDIDDocWithKey(pub string) *did.Doc { } services := []did.Service{ { - ID: fmt.Sprintf(didServiceID, id, 1), - Type: "did-communication", - ServiceEndpoint: model.Endpoint{ - URI: "http://localhost:58416", - Accept: nil, - RoutingKeys: nil, - }, - Priority: 0, - RecipientKeys: []string{pubKeyID}, + ID: fmt.Sprintf(didServiceID, id, 1), + Type: "did-communication", + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://localhost:58416"), + Priority: 0, + RecipientKeys: []string{pubKeyID}, }, } createdTime := time.Now() diff --git a/pkg/didcomm/dispatcher/outbound/outbound.go b/pkg/didcomm/dispatcher/outbound/outbound.go index 06051a9236..4540a56bb3 100644 --- a/pkg/didcomm/dispatcher/outbound/outbound.go +++ b/pkg/didcomm/dispatcher/outbound/outbound.go @@ -107,9 +107,11 @@ func (o *Dispatcher) SendToDID(msg interface{}, myDID, theirDID string) error { didcommMsg, isMsgMap := msg.(service.DIDCommMsgMap) + var isV2 bool + if isMsgMap { - isV2, e := service.IsDIDCommV2(&didcommMsg) - if e == nil && isV2 { + isV2, err = service.IsDIDCommV2(&didcommMsg) + if err == nil && isV2 { connectionVersion = service.V2 } else { connectionVersion = service.V1 @@ -158,16 +160,42 @@ func (o *Dispatcher) SendToDID(msg interface{}, myDID, theirDID string) error { "outboundDispatcher.SendToDID failed to get didcomm destination for theirDID [%s]: %w", theirDID, err) } - if len(connRec.ServiceEndPoint.Accept) > 0 { - dest.ServiceEndpoint.Accept = make([]string, len(connRec.ServiceEndPoint.Accept)) - copy(dest.ServiceEndpoint.Accept, connRec.ServiceEndPoint.Accept) - } - mtp := o.mediaTypeProfile(dest) switch mtp { case transport.MediaTypeV1PlaintextPayload, transport.MediaTypeV1EncryptedEnvelope, transport.MediaTypeRFC0019EncryptedEnvelope, transport.MediaTypeAIP2RFC0019Profile: sendWithAnoncrypt = false + + if len(connRec.MediaTypeProfiles) > 0 { + dest.MediaTypeProfiles = make([]string, len(connRec.MediaTypeProfiles)) + copy(dest.MediaTypeProfiles, connRec.MediaTypeProfiles) + } + case transport.MediaTypeAIP2RFC0587Profile, transport.MediaTypeDIDCommV2Profile: + var ( + url string + accept, routingKeys []string + ) + + url, err = connRec.ServiceEndPoint.URI() + if err != nil && !strings.EqualFold(err.Error(), "endpoint URI not found") { + return err + } + + accept, err = connRec.ServiceEndPoint.Accept() + if err != nil && !strings.EqualFold(err.Error(), "endpoint Accept not found") { + return err + } + + routingKeys, err = connRec.ServiceEndPoint.RoutingKeys() + if err != nil && !strings.EqualFold(err.Error(), "endpoint RoutingKeys not found") { + return err + } + + dest.ServiceEndpoint = commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{{ + URI: url, + Accept: accept, + RoutingKeys: routingKeys, + }}) } if sendWithAnoncrypt { @@ -207,13 +235,19 @@ func (o *Dispatcher) getOrCreateConnection(myDID, theirDID string, connectionVer logger.Debugf("no connection record found for myDID=%s theirDID=%s, will create", myDID, theirDID) newRecord := connection.Record{ - ConnectionID: uuid.New().String(), - MyDID: myDID, - TheirDID: theirDID, - State: connection.StateNameCompleted, - Namespace: connection.MyNSPrefix, - ServiceEndPoint: commonmodel.Endpoint{Accept: o.defaultMediaTypeProfiles()}, - DIDCommVersion: connectionVersion, + ConnectionID: uuid.New().String(), + MyDID: myDID, + TheirDID: theirDID, + State: connection.StateNameCompleted, + Namespace: connection.MyNSPrefix, + DIDCommVersion: connectionVersion, + } + + if connectionVersion == service.V2 { + newRecord.ServiceEndPoint = commonmodel.NewDIDCommV2Endpoint( + []commonmodel.DIDCommV2Endpoint{{Accept: o.defaultMediaTypeProfiles()}}) + } else { + newRecord.MediaTypeProfiles = o.defaultMediaTypeProfiles() } err = o.connections.SaveConnectionRecord(&newRecord) @@ -228,14 +262,21 @@ func (o *Dispatcher) getOrCreateConnection(myDID, theirDID string, connectionVer func (o *Dispatcher) Send(msg interface{}, senderKey string, des *service.Destination) error { // nolint:funlen,gocyclo // check if outbound accepts routing keys, else use recipient keys keys := des.RecipientKeys - if len(des.ServiceEndpoint.RoutingKeys) != 0 { - keys = des.ServiceEndpoint.RoutingKeys + if routingKeys, err := des.ServiceEndpoint.RoutingKeys(); err == nil && len(routingKeys) > 0 { // DIDComm V2 + keys = routingKeys + } else if len(des.RoutingKeys) > 0 { // DIDComm V1 + keys = routingKeys } var outboundTransport transport.OutboundTransport for _, v := range o.outboundTransports { - if v.AcceptRecipient(keys) || v.Accept(des.ServiceEndpoint.URI) { + uri, err := des.ServiceEndpoint.URI() + if err != nil { + logger.Debugf("destination ServiceEndpoint empty: %w, it will not be checked", err) + } + + if v.AcceptRecipient(keys) || v.Accept(uri) { outboundTransport = v break } @@ -292,9 +333,19 @@ func (o *Dispatcher) Send(msg interface{}, senderKey string, des *service.Destin // Forward forwards the message without packing to the destination. func (o *Dispatcher) Forward(msg interface{}, des *service.Destination) error { + var ( + uri string + err error + ) + + uri, err = des.ServiceEndpoint.URI() + if err != nil { + logger.Debugf("destination serviceEndpoint forward URI is not set: %w, will skip value", err) + } + for _, v := range o.outboundTransports { if !v.AcceptRecipient(des.RecipientKeys) { - if !v.Accept(des.ServiceEndpoint.URI) { + if !v.Accept(uri) { continue } } @@ -312,9 +363,10 @@ func (o *Dispatcher) Forward(msg interface{}, des *service.Destination) error { return nil } - return fmt.Errorf("outboundDispatcher.Forward: no transport found for serviceEndpoint: %s", des.ServiceEndpoint.URI) + return fmt.Errorf("outboundDispatcher.Forward: no transport found for serviceEndpoint: %s", uri) } +//nolint:funlen func (o *Dispatcher) createForwardMessage(msg []byte, des *service.Destination) ([]byte, error) { forwardMsgType := service.ForwardMsgType @@ -342,8 +394,18 @@ func (o *Dispatcher) createForwardMessage(msg []byte, des *service.Destination) senderKey = []byte(senderDIDKey) } - if len(des.ServiceEndpoint.RoutingKeys) == 0 { - return msg, nil + routingKeys, err := des.ServiceEndpoint.RoutingKeys() + if err != nil { + logger.Debugf("des.ServiceEndpoint.RoutingKeys() (didcomm v2) returned an error %w, "+ + "will check routinKeys (didcomm v1) array", err) + } + + if len(routingKeys) == 0 { + if len(des.RoutingKeys) == 0 { + return msg, nil + } + + routingKeys = des.RoutingKeys } // create forward message @@ -364,7 +426,7 @@ func (o *Dispatcher) createForwardMessage(msg []byte, des *service.Destination) MediaTypeProfile: mtProfile, Message: req, FromKey: senderKey, - ToKeys: des.ServiceEndpoint.RoutingKeys, + ToKeys: routingKeys, }) if err != nil { return nil, fmt.Errorf("failed to pack forward msg: %w", err) @@ -374,8 +436,8 @@ func (o *Dispatcher) createForwardMessage(msg []byte, des *service.Destination) } func (o *Dispatcher) addTransportRouteOptions(req []byte, des *service.Destination) ([]byte, error) { - // dont add transport route options for forward messages - if len(des.ServiceEndpoint.RoutingKeys) != 0 { + // don't add transport route options for forward messages + if routingKeys, err := des.ServiceEndpoint.RoutingKeys(); err == nil && len(routingKeys) > 0 { return req, nil } @@ -401,10 +463,18 @@ func (o *Dispatcher) addTransportRouteOptions(req []byte, des *service.Destinati } func (o *Dispatcher) mediaTypeProfile(des *service.Destination) string { - mt := "" + var ( + mt string + accept []string + err error + ) + + if accept, err = des.ServiceEndpoint.Accept(); err != nil || len(accept) == 0 { // didcomm v2 + accept = des.MediaTypeProfiles // didcomm v1 + } - if len(des.ServiceEndpoint.Accept) > 0 { - for _, mtp := range des.ServiceEndpoint.Accept { + if len(accept) > 0 { + for _, mtp := range accept { switch mtp { case transport.MediaTypeV1PlaintextPayload, transport.MediaTypeRFC0019EncryptedEnvelope, transport.MediaTypeAIP2RFC0019Profile, transport.MediaTypeProfileDIDCommAIP1: diff --git a/pkg/didcomm/dispatcher/outbound/outbound_test.go b/pkg/didcomm/dispatcher/outbound/outbound_test.go index d8a45ebc01..545da6d963 100644 --- a/pkg/didcomm/dispatcher/outbound/outbound_test.go +++ b/pkg/didcomm/dispatcher/outbound/outbound_test.go @@ -58,7 +58,7 @@ func TestOutboundDispatcher_Send(t *testing.T) { }) require.NoError(t, err) require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: "url"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("url"), })) }) @@ -77,10 +77,10 @@ func TestOutboundDispatcher_Send(t *testing.T) { require.NoError(t, o.Send("data", fromDIDDoc.KeyAgreement[0].VerificationMethod.ID, &service.Destination{ RecipientKeys: []string{toDIDDoc.KeyAgreement[0].VerificationMethod.ID}, - ServiceEndpoint: model.Endpoint{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ URI: "url", Accept: []string{transport.MediaTypeDIDCommV2Profile}, - }, + }}), })) }) @@ -94,7 +94,7 @@ func TestOutboundDispatcher_Send(t *testing.T) { }) require.NoError(t, err) err = o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: "url"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("url"), }) require.Error(t, err) require.Contains(t, err.Error(), "outboundDispatcher.Send: no transport found for destination") @@ -110,7 +110,7 @@ func TestOutboundDispatcher_Send(t *testing.T) { }) require.NoError(t, err) err = o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: "url"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("url"), }) require.Error(t, err) require.Contains(t, err.Error(), "pack error") @@ -128,7 +128,7 @@ func TestOutboundDispatcher_Send(t *testing.T) { }) require.NoError(t, err) err = o.Send("data", mockdiddoc.MockDIDKey(t), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")}) require.Error(t, err) require.Contains(t, err.Error(), "send error") }) @@ -144,10 +144,9 @@ func TestOutboundDispatcher_Send(t *testing.T) { require.NoError(t, err) require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{ - URI: "url", - RoutingKeys: []string{"xyz"}, - }, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: "url", RoutingKeys: []string{"xyz"}}, + }), RecipientKeys: []string{"abc"}, })) }) @@ -167,10 +166,9 @@ func TestOutboundDispatcher_Send(t *testing.T) { require.NoError(t, err) require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{ - URI: "url", - RoutingKeys: []string{"xyz"}, - }, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: "url", RoutingKeys: []string{"xyz"}}, + }), RecipientKeys: []string{"abc"}, })) }) @@ -189,10 +187,9 @@ func TestOutboundDispatcher_Send(t *testing.T) { require.NoError(t, err) err = o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{ - URI: "url", - RoutingKeys: []string{"xyz"}, - }, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: "url", RoutingKeys: []string{"xyz"}}, + }), RecipientKeys: []string{"abc"}, }) require.EqualError(t, err, "outboundDispatcher.Send: failed to create forward msg: failed Create "+ @@ -210,10 +207,9 @@ func TestOutboundDispatcher_Send(t *testing.T) { require.NoError(t, err) _, err = o.createForwardMessage(createPackedMsgForForward(t), &service.Destination{ - ServiceEndpoint: model.Endpoint{ - URI: "url", - RoutingKeys: []string{"xyz"}, - }, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: "url", RoutingKeys: []string{"xyz"}}, + }), RecipientKeys: []string{"abc"}, }) require.Error(t, err) @@ -228,7 +224,7 @@ type mockMessage struct { } func TestOutboundDispatcher_SendToDID(t *testing.T) { - mockDoc := mockdiddoc.GetMockDIDDoc(t) + mockDoc := mockdiddoc.GetMockDIDDoc(t, false) t.Run("success with existing connection record", func(t *testing.T) { o, err := NewOutbound(&mockProvider{ @@ -555,7 +551,7 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) { require.NoError(t, err) require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}})) + &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})})) }) t.Run("transport route option - value set thread", func(t *testing.T) { @@ -590,7 +586,7 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) { require.NoError(t, err) require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}})) + &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})})) }) t.Run("transport route option - no value set", func(t *testing.T) { @@ -617,7 +613,7 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) { require.NoError(t, err) require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}})) + &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})})) }) t.Run("transport route option - forward message", func(t *testing.T) { @@ -634,7 +630,9 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) { testData := []byte("testData") data, err := o.addTransportRouteOptions(testData, - &service.Destination{ServiceEndpoint: model.Endpoint{RoutingKeys: []string{"abc"}}}) + &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {RoutingKeys: []string{"abc"}}, + })}) require.NoError(t, err) require.Equal(t, testData, data) }) @@ -643,14 +641,18 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) { func TestOutboundDispatcher_Forward(t *testing.T) { t.Run("test forward - success", func(t *testing.T) { o, err := NewOutbound(&mockProvider{ - packagerValue: &mockpackager.Packager{}, - outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}}, - storageProvider: mockstore.NewMockStoreProvider(), - protoStorageProvider: mockstore.NewMockStoreProvider(), - mediaTypeProfiles: []string{transport.MediaTypeDIDCommV2Profile}, + packagerValue: &mockpackager.Packager{}, + outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{ + AcceptValue: true, + }}, + storageProvider: mockstore.NewMockStoreProvider(), + protoStorageProvider: mockstore.NewMockStoreProvider(), + mediaTypeProfiles: []string{transport.MediaTypeDIDCommV2Profile}, }) require.NoError(t, err) - require.NoError(t, o.Forward("data", &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}})) + require.NoError(t, o.Forward("data", &service.Destination{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}}), + })) }) t.Run("test forward - no outbound transport found", func(t *testing.T) { @@ -662,7 +664,9 @@ func TestOutboundDispatcher_Forward(t *testing.T) { mediaTypeProfiles: []string{transport.MediaTypeDIDCommV2Profile}, }) require.NoError(t, err) - err = o.Forward("data", &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + err = o.Forward("data", &service.Destination{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}}), + }) require.Error(t, err) require.Contains(t, err.Error(), "outboundDispatcher.Forward: no transport found for serviceEndpoint: url") }) @@ -678,7 +682,8 @@ func TestOutboundDispatcher_Forward(t *testing.T) { mediaTypeProfiles: []string{transport.MediaTypeDIDCommV2Profile}, }) require.NoError(t, err) - err = o.Forward("data", &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + err = o.Forward("data", &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model. + DIDCommV2Endpoint{{URI: "url"}})}) require.Error(t, err) require.Contains(t, err.Error(), "send error") }) diff --git a/pkg/didcomm/packer/legacy/authcrypt/unpack.go b/pkg/didcomm/packer/legacy/authcrypt/unpack.go index 241982ac9b..d1a17ed8a0 100644 --- a/pkg/didcomm/packer/legacy/authcrypt/unpack.go +++ b/pkg/didcomm/packer/legacy/authcrypt/unpack.go @@ -9,7 +9,6 @@ package authcrypt import ( "encoding/base64" "encoding/json" - "errors" "fmt" "github.com/btcsuite/btcutil/base58" @@ -126,6 +125,8 @@ func getCEK(recipients []recipient, km kms.KeyManager) (*keys, error) { } func findVerKey(km kms.KeyManager, candidateKeys []string) (int, error) { + var errs []error + for i, key := range candidateKeys { recKID, err := localkms.CreateKID(base58.Decode(key), kms.ED25519Type) if err != nil { @@ -136,9 +137,11 @@ func findVerKey(km kms.KeyManager, candidateKeys []string) (int, error) { if err == nil { return i, nil } + + errs = append(errs, err) } - return -1, errors.New("none of the recipient keys were found in kms") + return -1, fmt.Errorf("none of the recipient keys were found in kms: %v", errs) } func decodeSender(b64Sender string, pk []byte, km kms.KeyManager) ([]byte, []byte, error) { diff --git a/pkg/didcomm/protocol/didexchange/service.go b/pkg/didcomm/protocol/didexchange/service.go index d8b924b87d..8ecac28ef1 100644 --- a/pkg/didcomm/protocol/didexchange/service.go +++ b/pkg/didcomm/protocol/didexchange/service.go @@ -697,7 +697,11 @@ func (s *Service) CreateConnection(record *connection.Record, theirDID *did.Doc) return fmt.Errorf("failed to save myDID to the did.ConnectionStore: %w", err) } - record.DIDCommVersion = service.V1 + if isDIDCommV2(record.MediaTypeProfiles) { + record.DIDCommVersion = service.V2 + } else { + record.DIDCommVersion = service.V1 + } return s.connectionRecorder.SaveConnectionRecord(record) } @@ -719,6 +723,7 @@ func (s *Service) connectionRecord(msg service.DIDCommMsg) (*connection.Record, return nil, errors.New("invalid message type") } +//nolint:funlen func (s *Service) oobInvitationMsgRecord(msg service.DIDCommMsg) (*connection.Record, error) { thID, err := msg.ThreadID() if err != nil { @@ -737,20 +742,40 @@ func (s *Service) oobInvitationMsgRecord(msg service.DIDCommMsg) (*connection.Re return nil, fmt.Errorf("failed to get the did service block from oob invitation : %w", err) } - connRecord := &connection.Record{ - ConnectionID: generateRandomID(), - ThreadID: thID, - ParentThreadID: oobInvitation.ThreadID, - State: stateNameNull, - InvitationID: oobInvitation.ID, - ServiceEndPoint: model.Endpoint{ - URI: svc.ServiceEndpoint.URI, // TODO: service endpoint should be 'theirs' not 'mine'. - Accept: svc.ServiceEndpoint.Accept, - }, - RecipientKeys: svc.RecipientKeys, // TODO: recipient keys should be 'theirs' not 'mine'. - TheirLabel: oobInvitation.TheirLabel, - Namespace: findNamespace(msg.Type()), - DIDCommVersion: service.V1, + uri, err := svc.ServiceEndpoint.URI() + if err != nil { + logger.Debugf("service DIDComm V1 without ServiceEndpoint URI: %w, skipping it", err) + } + + var connRecord *connection.Record + + if accept, err := svc.ServiceEndpoint.Accept(); err == nil && isDIDCommV2(accept) { + connRecord = &connection.Record{ + ConnectionID: generateRandomID(), + ThreadID: thID, + ParentThreadID: oobInvitation.ThreadID, + State: stateNameNull, + InvitationID: oobInvitation.ID, + ServiceEndPoint: svc.ServiceEndpoint, + RecipientKeys: svc.RecipientKeys, // TODO: recipient keys should be 'theirs' not 'mine'. + TheirLabel: oobInvitation.TheirLabel, + Namespace: findNamespace(msg.Type()), + DIDCommVersion: service.V2, + } + } else { + connRecord = &connection.Record{ + ConnectionID: generateRandomID(), + ThreadID: thID, + ParentThreadID: oobInvitation.ThreadID, + State: stateNameNull, + InvitationID: oobInvitation.ID, + ServiceEndPoint: model.NewDIDCommV1Endpoint(uri), + RecipientKeys: svc.RecipientKeys, // TODO: recipient keys should be 'theirs' not 'mine'. + TheirLabel: oobInvitation.TheirLabel, + Namespace: findNamespace(msg.Type()), + MediaTypeProfiles: svc.Accept, + DIDCommVersion: service.V1, + } } publicDID, ok := oobInvitation.Target.(string) @@ -784,21 +809,32 @@ func (s *Service) invitationMsgRecord(msg service.DIDCommMsg) (*connection.Recor return nil, err } + var ( + sp model.Endpoint + didCommVersion service.Version + ) + + if isDIDCommV2(s.ctx.mediaTypeProfiles) { + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: invitation.ServiceEndpoint, Accept: s.ctx.mediaTypeProfiles}, + }) + didCommVersion = service.V2 + } else { + sp = model.NewDIDCommV1Endpoint(invitation.ServiceEndpoint) + didCommVersion = service.V1 + } + connRecord := &connection.Record{ - ConnectionID: generateRandomID(), - ThreadID: thID, - State: stateNameNull, - InvitationID: invitation.ID, - InvitationDID: invitation.DID, - ServiceEndPoint: model.Endpoint{ - URI: invitation.ServiceEndpoint, - Accept: nil, - RoutingKeys: nil, - }, - RecipientKeys: []string{recKey}, - TheirLabel: invitation.Label, - Namespace: findNamespace(msg.Type()), - DIDCommVersion: service.V1, + ConnectionID: generateRandomID(), + ThreadID: thID, + State: stateNameNull, + InvitationID: invitation.ID, + InvitationDID: invitation.DID, + ServiceEndPoint: sp, + RecipientKeys: []string{recKey}, + TheirLabel: invitation.Label, + Namespace: findNamespace(msg.Type()), + DIDCommVersion: didCommVersion, } if err := s.connectionRecorder.SaveConnectionRecord(connRecord); err != nil { @@ -918,6 +954,7 @@ type options struct { // CreateImplicitInvitation creates implicit invitation. Inviter DID is required, invitee DID is optional. // If invitee DID is not provided new peer DID will be created for implicit invitation exchange request. +//nolint:funlen func (s *Service) CreateImplicitInvitation(inviterLabel, inviterDID, inviteeLabel, inviteeDID string, routerConnections []string) (string, error) { logger.Debugf("implicit invitation requested inviterDID[%s] inviteeDID[%s]", inviterDID, inviteeDID) @@ -933,16 +970,35 @@ func (s *Service) CreateImplicitInvitation(inviterLabel, inviterDID, } thID := generateRandomID() - connRecord := &connection.Record{ - ConnectionID: generateRandomID(), - ThreadID: thID, - State: stateNameNull, - InvitationDID: inviterDID, - Implicit: true, - ServiceEndPoint: dest.ServiceEndpoint, - RecipientKeys: dest.RecipientKeys, - TheirLabel: inviterLabel, - Namespace: findNamespace(InvitationMsgType), + + var connRecord *connection.Record + + if accept, e := dest.ServiceEndpoint.Accept(); e == nil && isDIDCommV2(accept) { + connRecord = &connection.Record{ + ConnectionID: generateRandomID(), + ThreadID: thID, + State: stateNameNull, + InvitationDID: inviterDID, + Implicit: true, + ServiceEndPoint: dest.ServiceEndpoint, + RecipientKeys: dest.RecipientKeys, + TheirLabel: inviterLabel, + Namespace: findNamespace(InvitationMsgType), + } + } else { + connRecord = &connection.Record{ + ConnectionID: generateRandomID(), + ThreadID: thID, + State: stateNameNull, + InvitationDID: inviterDID, + Implicit: true, + ServiceEndPoint: dest.ServiceEndpoint, + RecipientKeys: dest.RecipientKeys, + RoutingKeys: dest.RoutingKeys, + MediaTypeProfiles: dest.MediaTypeProfiles, + TheirLabel: inviterLabel, + Namespace: findNamespace(InvitationMsgType), + } } if e := s.connectionRecorder.SaveConnectionRecordWithMappings(connRecord); e != nil { diff --git a/pkg/didcomm/protocol/didexchange/service_test.go b/pkg/didcomm/protocol/didexchange/service_test.go index d0ed8ae4cd..6a885ce8bf 100644 --- a/pkg/didcomm/protocol/didexchange/service_test.go +++ b/pkg/didcomm/protocol/didexchange/service_test.go @@ -406,7 +406,9 @@ func TestService_Handle_Invitee(t *testing.T) { require.Equal(t, (&requested{}).Name(), connRecord.State) require.Equal(t, invitation.ID, connRecord.InvitationID) require.Equal(t, invitation.RecipientKeys, connRecord.RecipientKeys) - require.Equal(t, invitation.ServiceEndpoint, connRecord.ServiceEndPoint.URI) + uri, err := connRecord.ServiceEndPoint.URI() + require.NoError(t, err) + require.Equal(t, invitation.ServiceEndpoint, uri) didKey, err := ctx.getVerKey(invitation.ID) require.NoError(t, err) @@ -694,7 +696,7 @@ func TestCreateConnection(t *testing.T) { TheirLabel: uuid.New().String(), TheirDID: theirDID.ID, MyDID: newPeerDID(t, k).ID, - ServiceEndPoint: commonmodel.Endpoint{URI: "http://example.com"}, + ServiceEndPoint: commonmodel.NewDIDCommV1Endpoint("http://example.com"), RecipientKeys: []string{"testkeys"}, InvitationID: uuid.New().String(), Namespace: myNSPrefix, @@ -954,7 +956,7 @@ func TestContinueWithPublicDID(t *testing.T) { keyType: kms.ED25519Type, keyAgreementType: kms.X25519ECDHKWType, } - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) svc, err := New(&protocol.MockProvider{ ServiceMap: map[string]interface{}{ mediator.Coordination: &mockroute.MockMediatorSvc{}, @@ -2015,7 +2017,7 @@ func generateRequestMsgPayload(t *testing.T, prov provider, id, invitationID str ctx := context{ outboundDispatcher: prov.OutboundDispatcher(), - vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t)}, + vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)}, connectionRecorder: connRec, } doc, err := ctx.vdRegistry.Create(testMethod, nil) @@ -2183,7 +2185,7 @@ func TestRespondTo(t *testing.T) { ID: uuid.New().String(), Type: "did-communication", RecipientKeys: []string{"did:key:1234567"}, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://example.com"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://example.com"), }), nil) require.NoError(t, err) require.NotEmpty(t, connID) @@ -2248,7 +2250,7 @@ func TestRespondTo(t *testing.T) { func TestSave(t *testing.T) { t.Run("saves invitation", func(t *testing.T) { - expected := newOOBInvite("did:example:public") + expected := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, "did:example:public") provider := testProvider() provider.StoreProvider = &mockstorage.MockStoreProvider{ Custom: &mockStore{ @@ -2276,7 +2278,7 @@ func TestSave(t *testing.T) { } s, err := New(provider) require.NoError(t, err) - err = s.SaveInvitation(newOOBInvite("did:example:public")) + err = s.SaveInvitation(newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, "did:example:public")) require.Error(t, err) require.True(t, errors.Is(err, expected)) }) @@ -2324,7 +2326,7 @@ func newPeerDID(t *testing.T, k kms.KeyManager) *did.Doc { Type: "did-communication", Priority: 0, RecipientKeys: []string{base58.Encode(pubKey)}, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://example.com"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://example.com"), }}), ) require.NoError(t, err) diff --git a/pkg/didcomm/protocol/didexchange/states.go b/pkg/didcomm/protocol/didexchange/states.go index 9d1920c662..aef28e107f 100644 --- a/pkg/didcomm/protocol/didexchange/states.go +++ b/pkg/didcomm/protocol/didexchange/states.go @@ -324,8 +324,10 @@ func (ctx *context) handleInboundOOBInvitation(oobInv *OOBInvitation, thid strin } dest := &service.Destination{ - RecipientKeys: svc.RecipientKeys, - ServiceEndpoint: svc.ServiceEndpoint, + RecipientKeys: svc.RecipientKeys, + ServiceEndpoint: svc.ServiceEndpoint, + RoutingKeys: svc.RoutingKeys, + MediaTypeProfiles: svc.Accept, } connRec.ThreadID = thid @@ -360,9 +362,14 @@ func (ctx *context) createInvitedRequest(destination *service.Destination, label }, } + accept, err := destination.ServiceEndpoint.Accept() + if err != nil { + accept = []string{} + } + // get did document to use in exchange request myDIDDoc, err := ctx.getMyDIDDoc(getPublicDID(options), getRouterConnections(options), - serviceTypeByMediaProfile(destination.ServiceEndpoint.Accept)) + serviceTypeByMediaProfile(accept)) if err != nil { return nil, nil, err } @@ -448,7 +455,12 @@ func (ctx *context) handleInboundRequest(request *Request, options *options, if len(requestDidDoc.Service) > 0 { serviceType = requestDidDoc.Service[0].Type } else { - serviceType = serviceTypeByMediaProfile(destination.ServiceEndpoint.Accept) + accept, e := destination.ServiceEndpoint.Accept() + if e != nil { + accept = []string{} + } + + serviceType = serviceTypeByMediaProfile(accept) } responseDidDoc, err := ctx.getMyDIDDoc(myDID, getRouterConnections(options), serviceType) @@ -488,8 +500,13 @@ func (ctx *context) handleInboundRequest(request *Request, options *options, connRec.TheirDID = request.DID connRec.TheirLabel = request.Label - if len(destination.ServiceEndpoint.Accept) > 0 { - connRec.ServiceEndPoint.Accept = destination.ServiceEndpoint.Accept + accept, err := destination.ServiceEndpoint.Accept() + if err != nil { + accept = []string{} + } + + if len(accept) > 0 { + connRec.MediaTypeProfiles = accept } // send exchange response @@ -628,14 +645,27 @@ func (ctx *context) getDestination(invitation *Invitation) (*service.Destination return service.GetDestination(invitation.DID, ctx.vdRegistry) } - return &service.Destination{ - RecipientKeys: invitation.RecipientKeys, - ServiceEndpoint: model.Endpoint{ - URI: invitation.ServiceEndpoint, - Accept: ctx.mediaTypeProfiles, - RoutingKeys: invitation.RoutingKeys, - }, - }, nil + accept := ctx.mediaTypeProfiles + + var dest *service.Destination + + if isDIDCommV2(accept) { + dest = &service.Destination{ + RecipientKeys: invitation.RecipientKeys, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: invitation.ServiceEndpoint, Accept: accept, RoutingKeys: invitation.RoutingKeys}, + }), + } + } else { + dest = &service.Destination{ + RecipientKeys: invitation.RecipientKeys, + ServiceEndpoint: model.NewDIDCommV1Endpoint(invitation.ServiceEndpoint), + MediaTypeProfiles: accept, + RoutingKeys: invitation.RoutingKeys, + } + } + + return dest, nil } // nolint:gocyclo,funlen @@ -670,10 +700,23 @@ func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, servi return nil, fmt.Errorf("did doc - fetch router config: %w", err) } - services = append(services, did.Service{ServiceEndpoint: model.Endpoint{ - URI: serviceEndpoint, - RoutingKeys: routingKeys, - }}) + var svc did.Service + + if serviceType == didCommServiceType { + svc = did.Service{ + Type: didCommServiceType, + ServiceEndpoint: model.NewDIDCommV1Endpoint(serviceEndpoint), + RoutingKeys: routingKeys, + } + } else if serviceType == didCommV2ServiceType { + svc = did.Service{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: serviceEndpoint, RoutingKeys: routingKeys}, + }), + } + } + + services = append(services, svc) } if len(services) == 0 { @@ -949,6 +992,8 @@ func (ctx *context) getVerKeyFromOOBInvitation(invitationID string) (string, err return "", errVerKeyNotFound } + unmarshalServiceEndpointInOOBTarget(&invitation) + pubKey, err := ctx.resolveVerKey(&invitation) if err != nil { return "", fmt.Errorf("failed to get my verkey: %w", err) @@ -957,6 +1002,40 @@ func (ctx *context) getVerKeyFromOOBInvitation(invitationID string) (string, err return pubKey, nil } +//nolint:nestif +func unmarshalServiceEndpointInOOBTarget(invitation *OOBInvitation) { + // for DIDCommV1, oobInvitation's target serviceEndpoint is a string, transform it to model.Endpoint map equivalent + // for a successful service decode(). + // for DIDCommV2, transform the target from map[string]interface{} to model.Endpoint + if targetMap, ok := invitation.Target.(map[string]interface{}); ok { + if se, ok := targetMap["serviceEndpoint"]; ok { + seStr, ok := se.(string) + if ok { + targetMap["serviceEndpoint"] = model.NewDIDCommV1Endpoint(seStr) + } else if seMap, ok := se.(map[string]interface{}); ok { + seStr, ok = seMap["uri"].(string) + if !ok { + seStr = "" + } + + accept, ok := seMap["accept"].([]string) + if !ok { + accept = []string{} + } + + routingKeys, ok := seMap["routingKeys"].([]string) + if !ok { + routingKeys = []string{} + } + + targetMap["serviceEndpoint"] = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: seStr, Accept: accept, RoutingKeys: routingKeys}, + }) + } + } + } +} + // nolint:gocyclo,funlen func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) { logger.Debugf("extracting service block from oobinvitation=%+v", i) @@ -1006,7 +1085,13 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) { err = decoder.Decode(svc) if err != nil { - return nil, fmt.Errorf("failed to decode service block : %w", err) + // for DIDCommV2, decoder.Decode(svc) doesn't support serviceEndpoint as []interface{} representing an array + // for model.Endpoint. Manually build the endpoint here in this case. + if strings.Contains(err.Error(), "'serviceEndpoint' expected a map, got 'slice'") { + extractDIDCommV2EndpointIntoService(svc, s) + } else { + return nil, fmt.Errorf("failed to decode service block : %w", err) + } } block = &s @@ -1014,6 +1099,7 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) { return nil, fmt.Errorf("unsupported target type: %+v", svc) } + //nolint:nestif if len(i.MediaTypeProfiles) > 0 { // marshal/unmarshal to "clone" service block blockBytes, err := json.Marshal(block) @@ -1031,7 +1117,23 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) { // updating Accept header requires a cloned service block to avoid Data Race errors. // RFC0587: In case the accept property is set in both the DID service block and the out-of-band message, // the out-of-band property takes precedence. - block.ServiceEndpoint.Accept = i.MediaTypeProfiles + if isDIDCommV2(i.MediaTypeProfiles) { + uri, err := block.ServiceEndpoint.URI() + if err != nil { + logger.Debugf("block ServiceEndpoint URI empty for DIDcomm V2, skipping it.") + } + + routingKeys, err := block.ServiceEndpoint.RoutingKeys() + if err != nil { + logger.Debugf("block ServiceEndpoint RoutingKeys empty for DIDcomm V2, skipping these.") + } + + block.ServiceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: uri, Accept: i.MediaTypeProfiles, RoutingKeys: routingKeys}, + }) + } else { + block.Accept = i.MediaTypeProfiles + } } logger.Debugf("extracted service block=%+v", block) @@ -1039,6 +1141,58 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) { return block, nil } +//nolint:gocognit,gocritic,gocyclo,nestif +func extractDIDCommV2EndpointIntoService(svc map[string]interface{}, s did.Service) { + if svcEndpointModel, ok := svc["serviceEndpoint"]; ok { + if svcEndpointArr, ok := svcEndpointModel.([]interface{}); ok && len(svcEndpointArr) > 0 { + if svcEndpointMap, ok := svcEndpointArr[0].(map[string]interface{}); ok { + var ( + uri string + accept []string + routingKeys []string + ) + + if uriVal, ok := svcEndpointMap["uri"]; ok { + if uri, ok = uriVal.(string); !ok { + uri = "" + } + } + + if acceptVal, ok := svcEndpointMap["accept"]; ok { + if acceptArr, ok := acceptVal.([]interface{}); ok { + for _, a := range acceptArr { + accept = append(accept, a.(string)) + } + } + } + + if routingKeysVal, ok := svcEndpointMap["routingKeys"]; ok { + if routingKeysArr, ok := routingKeysVal.([]interface{}); ok { + for _, r := range routingKeysArr { + routingKeys = append(routingKeys, r.(string)) + } + } + } + + s.ServiceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: uri, Accept: accept, RoutingKeys: routingKeys}, + }) + } + } + } +} + +func isDIDCommV2(mediaTypeProfiles []string) bool { + for _, mtp := range mediaTypeProfiles { + switch mtp { + case transport.MediaTypeDIDCommV2Profile, transport.MediaTypeAIP2RFC0587Profile: + return true + } + } + + return false +} + func interopSovService(doc *did.Doc) (*did.Service, error) { s, found := did.LookupService(doc, "endpoint") if !found { diff --git a/pkg/didcomm/protocol/didexchange/states_test.go b/pkg/didcomm/protocol/didexchange/states_test.go index f70fb697a7..63c407ee70 100644 --- a/pkg/didcomm/protocol/didexchange/states_test.go +++ b/pkg/didcomm/protocol/didexchange/states_test.go @@ -400,7 +400,7 @@ func TestRequestedState_Execute(t *testing.T) { Type: didServiceType, Priority: 0, RecipientKeys: []string{"key"}, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://test.com"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://test.com"), }, }), connRecord: &connection.Record{}, @@ -453,7 +453,7 @@ func TestRequestedState_Execute(t *testing.T) { Type: didServiceType, Priority: 0, RecipientKeys: []string{"key"}, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://test.com"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://test.com"), }, }), connRecord: &connection.Record{}, @@ -509,7 +509,8 @@ func TestRequestedState_Execute(t *testing.T) { _, encKey := newSigningAndEncryptionDIDKeys(t, tc.ctx) - inv := newOOBInvite(newServiceBlock([]string{encKey}, []string{encKey}, didServiceType)) + inv := newOOBInvite(tc.ctx.mediaTypeProfiles, newServiceBlock([]string{encKey}, []string{encKey}, + didServiceType)) inv.MyLabel = expected _, _, action, e := (&requested{}).ExecuteInbound(&stateMachineMsg{ DIDCommMsg: service.NewDIDCommMsgMap(inv), @@ -561,13 +562,22 @@ func TestRequestedState_Execute(t *testing.T) { if didServiceType == vdrapi.DIDCommV2ServiceType { expected = doc.KeyAgreement[0].VerificationMethod.ID + + doc.Service = []diddoc.Service{{ + Type: didServiceType, + ServiceEndpoint: commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{ + {URI: "http://test.com", Accept: []string{"didcomm/v2"}}, + }), + RecipientKeys: []string{expected}, + }} + } else { + doc.Service = []diddoc.Service{{ + Type: didServiceType, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://test.com"), + RecipientKeys: []string{expected}, + }} } - doc.Service = []diddoc.Service{{ - Type: didServiceType, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://test.com"}, - RecipientKeys: []string{expected}, - }} tc.ctx.vdRegistry = &mockvdr.MockVDRegistry{ CreateValue: doc, } @@ -585,6 +595,7 @@ func TestRequestedState_Execute(t *testing.T) { _, _, _, err = (&requested{}).ExecuteInbound(&stateMachineMsg{ options: &options{routerConnections: []string{"xyz"}}, DIDCommMsg: service.NewDIDCommMsgMap(newOOBInvite( + tc.ctx.mediaTypeProfiles, newServiceBlock([]string{encKey}, []string{encKey}, didServiceType))), connRecord: &connection.Record{}, }, "", tc.ctx) @@ -639,14 +650,20 @@ func TestRequestedState_Execute(t *testing.T) { options ...vdrapi.DIDMethodOption) (*diddoc.DocResolution, error) { created = true - require.Equal(t, expected.Keys(), didDoc.Service[0].ServiceEndpoint.RoutingKeys) - require.Equal(t, expected.Endpoint(), didDoc.Service[0].ServiceEndpoint.URI) + routingKeys, e := didDoc.Service[0].ServiceEndpoint.RoutingKeys() + require.NoError(t, e) + require.Equal(t, expected.Keys(), routingKeys) + + uri, e := didDoc.Service[0].ServiceEndpoint.URI() + require.NoError(t, e) + require.Equal(t, expected.Endpoint(), uri) return &diddoc.DocResolution{DIDDocument: docResolution}, nil }, ResolveValue: docResolution, } - oobInvite := newOOBInvite(newServiceBlock([]string{encKey}, []string{encKey}, didServiceType)) + oobInvite := newOOBInvite(tc.ctx.mediaTypeProfiles, newServiceBlock( + []string{encKey}, []string{encKey}, didServiceType)) _, _, _, err = (&requested{}).ExecuteInbound(&stateMachineMsg{ options: &options{routerConnections: []string{"xyz"}}, DIDCommMsg: service.NewDIDCommMsgMap(oobInvite), @@ -701,14 +718,11 @@ func TestRequestedState_Execute(t *testing.T) { t.Run(tc.name, func(t *testing.T) { myDoc := createDIDDoc(t, tc.ctx) myDoc.Service = []diddoc.Service{{ - ID: uuid.New().String(), - Type: "invalid", - Priority: 0, - RecipientKeys: nil, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "", - RoutingKeys: nil, - }, + ID: uuid.New().String(), + Type: "invalid", + Priority: 0, + RecipientKeys: nil, + ServiceEndpoint: commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{{}}), }} tc.ctx.vdRegistry = &mockvdr.MockVDRegistry{CreateValue: myDoc} _, _, _, err = (&requested{}).ExecuteInbound(&stateMachineMsg{ @@ -751,14 +765,11 @@ func TestRequestedState_Execute(t *testing.T) { t.Run(tc.name, func(t *testing.T) { myDoc := createDIDDoc(t, tc.ctx) myDoc.Service = []diddoc.Service{{ - ID: uuid.New().String(), - Type: "invalid", - Priority: 0, - RecipientKeys: nil, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "", - RoutingKeys: nil, - }, + ID: uuid.New().String(), + Type: "invalid", + Priority: 0, + RecipientKeys: nil, + ServiceEndpoint: commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{{}}), }} tc.ctx.vdRegistry = &mockvdr.MockVDRegistry{CreateValue: myDoc} _, _, _, err = (&requested{}).ExecuteInbound(&stateMachineMsg{ @@ -772,7 +783,7 @@ func TestRequestedState_Execute(t *testing.T) { Type: didServiceType, Priority: 0, RecipientKeys: []string{"key"}, - ServiceEndpoint: commonmodel.Endpoint{URI: "http://test.com"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("http://test.com"), }, }), connRecord: &connection.Record{}, @@ -827,23 +838,23 @@ func TestRespondedState_Execute(t *testing.T) { ctx *context }{ { - name: "using context with ED25519 main VM and X25519 keyAgreement", + name: fmt.Sprintf("using context with ED25519 main VM and X25519 keyAgreement with profile %s", mtp), ctx: getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp), }, { - name: "using context with P-256 main VM and P-256 keyAgreement", + name: fmt.Sprintf("using context with P-256 main VM and P-256 keyAgreement with profile %s", mtp), ctx: getContext(t, &prov, kms.ECDSAP256TypeIEEEP1363, kms.NISTP256ECDHKWType, mtp), }, { - name: "using context with P-384 main VM and P-384 keyAgreement", + name: fmt.Sprintf("using context with P-384 main VM and P-384 keyAgreement with profile %s", mtp), ctx: getContext(t, &prov, kms.ECDSAP384TypeIEEEP1363, kms.NISTP384ECDHKWType, mtp), }, { - name: "using context with P-521 main VM and P-521 keyAgreement", + name: fmt.Sprintf("using context with P-521 main VM and P-521 keyAgreement with profile %s", mtp), ctx: getContext(t, &prov, kms.ECDSAP521TypeIEEEP1363, kms.NISTP521ECDHKWType, mtp), }, { - name: "using context with ED25519 main VM and P-384 keyAgreement", + name: fmt.Sprintf("using context with ED25519 main VM and P-384 keyAgreement with profile %s", mtp), ctx: getContext(t, &prov, kms.ED25519Type, kms.NISTP384ECDHKWType, mtp), }, } @@ -914,14 +925,11 @@ func TestRespondedState_Execute(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp) myDoc := createDIDDoc(t, ctx) myDoc.Service = []diddoc.Service{{ - ID: uuid.New().String(), - Type: "invalid", - Priority: 0, - RecipientKeys: nil, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "", - RoutingKeys: nil, - }, + ID: uuid.New().String(), + Type: "invalid", + Priority: 0, + RecipientKeys: nil, + ServiceEndpoint: commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{{}}), }} ctx.vdRegistry = &mockvdr.MockVDRegistry{CreateValue: myDoc} _, _, _, err := (&responded{}).ExecuteInbound(&stateMachineMsg{ @@ -999,7 +1007,7 @@ func TestCompletedState_Execute(t *testing.T) { } err = ctx.connectionRecorder.SaveConnectionRecordWithMappings(connRec) require.NoError(t, err) - ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveValue: mockdiddoc.GetMockDIDDoc(t)} + ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveValue: mockdiddoc.GetMockDIDDoc(t, false)} require.NoError(t, err) _, followup, _, e := (&completed{}).ExecuteInbound(&stateMachineMsg{ DIDCommMsg: bytesToDIDCommMsg(t, responsePayloadBytes), @@ -1267,11 +1275,11 @@ func TestNewResponseFromRequest(t *testing.T) { }) t.Run("unsuccessful new response from request due to create did error", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) ctx := &context{ vdRegistry: &mockvdr.MockVDRegistry{ CreateErr: fmt.Errorf("create DID error"), - ResolveValue: mockdiddoc.GetMockDIDDoc(t), + ResolveValue: mockdiddoc.GetMockDIDDoc(t, false), }, routeSvc: &mockroute.MockMediatorSvc{}, } @@ -1307,7 +1315,7 @@ func TestNewResponseFromRequest(t *testing.T) { require.NotNil(t, didConnStore) ctx := &context{ - vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t)}, + vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)}, crypto: &mockcrypto.Crypto{SignErr: errors.New("sign error")}, connectionRecorder: connRec, connectionStore: didConnStore, @@ -1362,7 +1370,7 @@ func TestPrepareResponse(t *testing.T) { request, err := createRequest(t, ctx, false, transport.MediaTypeRFC0019EncryptedEnvelope) require.NoError(t, err) - _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t)) + _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t, false)) require.NoError(t, err) }) @@ -1373,7 +1381,7 @@ func TestPrepareResponse(t *testing.T) { request, err := createRequest(t, ctx, false, transport.MediaTypeRFC0019EncryptedEnvelope) require.NoError(t, err) - _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t)) + _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t, false)) require.NoError(t, err) }) @@ -1395,7 +1403,7 @@ func TestPrepareResponse(t *testing.T) { request, err := createRequest(t, ctx, false, transport.MediaTypeRFC0019EncryptedEnvelope) require.NoError(t, err) - _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t)) + _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t, false)) require.Error(t, err) require.True(t, errors.Is(err, expected)) }) @@ -1410,7 +1418,7 @@ func TestPrepareResponse(t *testing.T) { ctx.kms = &mockkms.KeyManager{GetKeyErr: expected} - _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t)) + _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t, false)) require.Error(t, err) require.True(t, errors.Is(err, expected)) }) @@ -1428,7 +1436,7 @@ func TestPrepareResponse(t *testing.T) { ctx.kms = &mockkms.KeyManager{GetKeyValue: mockKey} - _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t)) + _, err = ctx.prepareResponse(request, mockdiddoc.GetMockDIDDoc(t, false)) require.Error(t, err) }) } @@ -1439,7 +1447,7 @@ func TestContext_DIDDocAttachment(t *testing.T) { t.Run("successful new did doc attachment without signing", func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) att, err := ctx.didDocAttachment(doc, "") require.NoError(t, err) @@ -1458,7 +1466,7 @@ func TestContext_DIDDocAttachment(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) ctx.doACAPyInterop = true - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) _, pub, err := ctx.kms.CreateAndExportPubKeyBytes(kms.ED25519Type) require.NoError(t, err) @@ -1482,7 +1490,7 @@ func TestContext_DIDDocAttachment(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) ctx.doACAPyInterop = true - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) _, err := ctx.didDocAttachment(doc, "did:key:not a did key") require.Error(t, err) @@ -1493,7 +1501,7 @@ func TestContext_DIDDocAttachment(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) ctx.doACAPyInterop = true - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) didKey, _ := fingerprint.CreateDIDKey([]byte("abcdefghabcdefghabcdefghabcdefgh~!@#")) @@ -1519,7 +1527,7 @@ func TestResolvePublicKey(t *testing.T) { t.Run("resolve key reference from doc in vdr", func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveValue: doc} vm := doc.VerificationMethod[0] @@ -1547,7 +1555,7 @@ func TestResolvePublicKey(t *testing.T) { t.Run("fail to resolve doc for key reference", func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) vm := doc.VerificationMethod[0] @@ -1558,7 +1566,7 @@ func TestResolvePublicKey(t *testing.T) { t.Run("fail to find key in resolved doc", func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, transport.MediaTypeRFC0019EncryptedEnvelope) - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveValue: doc} kid := doc.VerificationMethod[0].ID @@ -1578,7 +1586,7 @@ func TestResolveDIDDocFromMessage(t *testing.T) { for _, mtp := range mtps { t.Run(fmt.Sprintf("success with media type profile: %s", mtp), func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp) - docIn := mockdiddoc.GetMockDIDDoc(t) + docIn := mockdiddoc.GetMockDIDDoc(t, false) att, err := ctx.didDocAttachment(docIn, "") require.NoError(t, err) @@ -1591,7 +1599,7 @@ func TestResolveDIDDocFromMessage(t *testing.T) { t.Run(fmt.Sprintf("success - public resolution with media type profile: %s", mtp), func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp) - docIn := mockdiddoc.GetMockDIDDoc(t) + docIn := mockdiddoc.GetMockDIDDoc(t, false) docIn.ID = "did:remote:abc" ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveValue: docIn} @@ -1605,7 +1613,7 @@ func TestResolveDIDDocFromMessage(t *testing.T) { t.Run(fmt.Sprintf("failure - can't do public resolution with media type profile: %s", mtp), func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp) - docIn := mockdiddoc.GetMockDIDDoc(t) + docIn := mockdiddoc.GetMockDIDDoc(t, false) docIn.ID = "did:remote:abc" ctx.vdRegistry = &mockvdr.MockVDRegistry{ResolveErr: fmt.Errorf("resolve error")} @@ -1659,7 +1667,7 @@ func TestResolveDIDDocFromMessage(t *testing.T) { t.Run(fmt.Sprintf("success - interop mode with media type profile: %s", mtp), func(t *testing.T) { ctx := getContext(t, &prov, kms.ED25519Type, kms.X25519ECDHKWType, mtp) - docIn := mockdiddoc.GetMockDIDDoc(t) + docIn := mockdiddoc.GetMockDIDDoc(t, false) docIn.ID = "did:sov:abcdefg" att, err := ctx.didDocAttachment(docIn, "") @@ -1679,7 +1687,7 @@ func TestResolveDIDDocFromMessage(t *testing.T) { ctx.vdRegistry = &mockvdr.MockVDRegistry{CreateErr: fmt.Errorf("create error")} - docIn := mockdiddoc.GetMockDIDDoc(t) + docIn := mockdiddoc.GetMockDIDDoc(t, false) att, err := ctx.didDocAttachment(docIn, "") require.NoError(t, err) @@ -1732,7 +1740,7 @@ func TestGetInvitationRecipientKey(t *testing.T) { require.Equal(t, invitation.RecipientKeys[0], recKey) }) t.Run("failed to get invitation recipient key", func(t *testing.T) { - doc := mockdiddoc.GetMockDIDDoc(t) + doc := mockdiddoc.GetMockDIDDoc(t, false) _, ok := diddoc.LookupService(doc, "did-communication") require.True(t, ok) ctx := context{vdRegistry: &mockvdr.MockVDRegistry{ResolveValue: doc}} @@ -1835,7 +1843,7 @@ func TestGetDIDDocAndConnection(t *testing.T) { customKMS := newKMS(t, mockstorage.NewMockStoreProvider()) ctx := context{ kms: customKMS, - vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t)}, + vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)}, connectionRecorder: connRec, connectionStore: didConnStore, routeSvc: &mockroute.MockMediatorSvc{}, @@ -1871,7 +1879,7 @@ func TestGetDIDDocAndConnection(t *testing.T) { customKMS := newKMS(t, mockstorage.NewMockStoreProvider()) ctx := context{ kms: customKMS, - vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t)}, + vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)}, connectionRecorder: connRec, routeSvc: &mockroute.MockMediatorSvc{ Connections: []string{"xyz"}, @@ -1890,7 +1898,7 @@ func TestGetDIDDocAndConnection(t *testing.T) { customKMS := newKMS(t, mockstorage.NewMockStoreProvider()) ctx := context{ kms: customKMS, - vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t)}, + vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)}, connectionRecorder: connRec, routeSvc: &mockroute.MockMediatorSvc{ Connections: []string{"xyz"}, @@ -1944,7 +1952,7 @@ func TestGetServiceBlock(t *testing.T) { vdRegistry: v, } - inv := newOOBInvite(doc.ID) + inv := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, doc.ID) svc, err := ctx.getServiceBlock(inv) require.NoError(t, err) @@ -1956,7 +1964,7 @@ func TestGetServiceBlock(t *testing.T) { vdRegistry: v, } - inv := newOOBInvite(doc.ID) + inv := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, doc.ID) svc, err := ctx.getServiceBlock(inv) require.Error(t, err) @@ -1975,7 +1983,7 @@ func TestGetServiceBlock(t *testing.T) { doACAPyInterop: true, } - inv := newOOBInvite(doc.ID) + inv := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, doc.ID) svc, err := ctx.getServiceBlock(inv) require.Error(t, err) @@ -1997,7 +2005,7 @@ func TestGetVerKey(t *testing.T) { t.Run("returns verkey from explicit oob invitation", func(t *testing.T) { expected := newServiceBlock([]string{encKey}, []string{encKey}, didCommServiceType) - invitation := newOOBInvite(expected) + invitation := newOOBInvite(expected.Accept, expected) ctx.connectionRecorder = connRecorder(t, testProvider()) err := ctx.connectionRecorder.SaveInvitation(invitation.ThreadID, invitation) @@ -2008,7 +2016,9 @@ func TestGetVerKey(t *testing.T) { require.Equal(t, expected.RecipientKeys[0], result) expected = newServiceBlock([]string{encKey}, []string{encKey}, didCommV2ServiceType) - invitation = newOOBInvite(expected) + accept, err := expected.ServiceEndpoint.Accept() + require.NoError(t, err) + invitation = newOOBInvite(accept, expected) ctx.connectionRecorder = connRecorder(t, testProvider()) err = ctx.connectionRecorder.SaveInvitation(invitation.ThreadID, invitation) @@ -2020,7 +2030,7 @@ func TestGetVerKey(t *testing.T) { }) t.Run("returns verkey from implicit oob invitation", func(t *testing.T) { publicDID := createDIDDoc(t, ctx) - invitation := newOOBInvite(publicDID.ID) + invitation := newOOBInvite([]string{ctx.mediaTypeProfiles[0]}, publicDID.ID) ctx.connectionRecorder = connRecorder(t, testProvider()) ctx.vdRegistry = &mockvdr.MockVDRegistry{ ResolveValue: publicDID, @@ -2037,7 +2047,7 @@ func TestGetVerKey(t *testing.T) { t.Run("returns verkey from implicit (interop) oob invitation", func(t *testing.T) { publicDID, err := diddoc.ParseDocument([]byte(sovDoc)) require.NoError(t, err) - invitation := newOOBInvite(publicDID.ID) + invitation := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, publicDID.ID) ctx.connectionRecorder = connRecorder(t, testProvider()) ctx.vdRegistry = &mockvdr.MockVDRegistry{ ResolveValue: publicDID, @@ -2056,7 +2066,7 @@ func TestGetVerKey(t *testing.T) { t.Run("returns verkey from explicit didexchange invitation", func(t *testing.T) { expected := newServiceBlock([]string{encKey}, []string{encKey}, didCommServiceType) - invitation := newDidExchangeInvite("", expected) + invitation := newDidExchangeInvite(t, "", expected) ctx.connectionRecorder = connRecorder(t, testProvider()) err := ctx.connectionRecorder.SaveInvitation(invitation.ID, invitation) @@ -2067,7 +2077,7 @@ func TestGetVerKey(t *testing.T) { require.Equal(t, expected.RecipientKeys[0], result) expected = newServiceBlock([]string{encKey}, []string{encKey}, didCommV2ServiceType) - invitation = newDidExchangeInvite("", expected) + invitation = newDidExchangeInvite(t, "", expected) ctx.connectionRecorder = connRecorder(t, testProvider()) err = ctx.connectionRecorder.SaveInvitation(invitation.ID, invitation) @@ -2094,7 +2104,7 @@ func TestGetVerKey(t *testing.T) { }) t.Run("fails for oob invitation with no target", func(t *testing.T) { - invalid := newOOBInvite(nil) + invalid := newOOBInvite(nil, nil) ctx.connectionRecorder = connRecorder(t, testProvider()) err := ctx.connectionRecorder.SaveInvitation(invalid.ThreadID, invalid) @@ -2115,11 +2125,13 @@ func TestGetVerKey(t *testing.T) { } ctx.connectionRecorder = connRecorder(t, pr) - invitation := newOOBInvite(newServiceBlock([]string{encKey}, []string{encKey}, didCommServiceType)) + invitation := newOOBInvite([]string{transport.MediaTypeRFC0019EncryptedEnvelope}, + newServiceBlock([]string{encKey}, []string{encKey}, didCommServiceType)) err := ctx.connectionRecorder.SaveInvitation(invitation.ID, invitation) require.NoError(t, err) - invitation = newOOBInvite(newServiceBlock([]string{encKey}, []string{encKey}, didCommV2ServiceType)) + invitation = newOOBInvite([]string{transport.MediaTypeDIDCommV2Profile}, + newServiceBlock([]string{encKey}, []string{encKey}, didCommV2ServiceType)) err = ctx.connectionRecorder.SaveInvitation(invitation.ID, invitation) require.NoError(t, err) @@ -2179,29 +2191,38 @@ func createDIDDocWithKey(verDIDKey, encDIDKey, mediaTypeProfile string) *diddoc. var ( didCommService string recKey string + sp commonmodel.Endpoint ) switch mediaTypeProfile { case transport.MediaTypeDIDCommV2Profile, transport.MediaTypeAIP2RFC0587Profile: didCommService = vdrapi.DIDCommV2ServiceType recKey = verDIDKey + sp = commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{ + {URI: "http://localhost:58416", Accept: []string{mediaTypeProfile}}, + }) default: didCommService = vdrapi.DIDCommServiceType recKey = encPubKeyID + sp = commonmodel.NewDIDCommV1Endpoint("http://localhost:58416") } services := []diddoc.Service{ { - ID: fmt.Sprintf(didServiceID, id, 1), - Type: didCommService, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "http://localhost:58416", - Accept: []string{mediaTypeProfile}, - }, - Priority: 0, - RecipientKeys: []string{recKey}, + ID: fmt.Sprintf(didServiceID, id, 1), + Type: didCommService, + ServiceEndpoint: sp, + Priority: 0, + RecipientKeys: []string{recKey}, }, } + + switch mediaTypeProfile { + case transport.MediaTypeDIDCommV2Profile, transport.MediaTypeAIP2RFC0587Profile: + default: // set DIDComm V1 Accept field. + services[0].Accept = []string{mediaTypeProfile} + } + createdTime := time.Now() didDoc := &diddoc.Doc{ Context: []string{diddoc.ContextV1}, @@ -2360,7 +2381,9 @@ func toBytes(t *testing.T, data interface{}) []byte { return src } -func newDidExchangeInvite(publicDID string, svc *diddoc.Service) *Invitation { +func newDidExchangeInvite(t *testing.T, publicDID string, svc *diddoc.Service) *Invitation { + t.Helper() + i := &Invitation{ ID: uuid.New().String(), Type: InvitationMsgType, @@ -2368,35 +2391,70 @@ func newDidExchangeInvite(publicDID string, svc *diddoc.Service) *Invitation { } if svc != nil { - i.RecipientKeys = svc.RecipientKeys - i.ServiceEndpoint = svc.ServiceEndpoint.URI - i.RoutingKeys = svc.ServiceEndpoint.RoutingKeys + if svc.Type == didCommV2ServiceType { + i.RecipientKeys = svc.RecipientKeys + uri, err := svc.ServiceEndpoint.URI() + require.NoError(t, err) + + i.ServiceEndpoint = uri + + routingKeys, err := svc.ServiceEndpoint.RoutingKeys() + require.NoError(t, err) + + i.RoutingKeys = routingKeys + } else { + var err error + + i.RecipientKeys = svc.RecipientKeys + i.ServiceEndpoint, err = svc.ServiceEndpoint.URI() + require.NoError(t, err) + i.RoutingKeys = svc.RoutingKeys + } } return i } -func newOOBInvite(target interface{}) *OOBInvitation { +func newOOBInvite(accept []string, target interface{}) *OOBInvitation { return &OOBInvitation{ ID: uuid.New().String(), Type: oobMsgType, ThreadID: uuid.New().String(), TheirLabel: "test", Target: target, - MediaTypeProfiles: []string{"didcomm/v2"}, + MediaTypeProfiles: accept, } } func newServiceBlock(recKeys, routingKeys []string, didCommServiceVType string) *diddoc.Service { - return &diddoc.Service{ - ID: uuid.New().String(), - Type: didCommServiceVType, - RecipientKeys: recKeys, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "http://test.com", - RoutingKeys: routingKeys, - }, + var ( + sp commonmodel.Endpoint + didCommV1RoutingKeys []string + ) + + switch didCommServiceVType { + case didCommV2ServiceType: + sp = commonmodel.NewDIDCommV2Endpoint([]commonmodel.DIDCommV2Endpoint{ + {URI: "http://test.com", Accept: []string{transport.MediaTypeDIDCommV2Profile}, RoutingKeys: routingKeys}, + }) + default: + sp = commonmodel.NewDIDCommV1Endpoint("http://test.com") + didCommV1RoutingKeys = routingKeys + } + + svc := &diddoc.Service{ + ID: uuid.New().String(), + Type: didCommServiceVType, + RecipientKeys: recKeys, + ServiceEndpoint: sp, + } + + if didCommServiceVType == didCommServiceType { + svc.Accept = []string{transport.MediaTypeRFC0019EncryptedEnvelope} + svc.RoutingKeys = didCommV1RoutingKeys } + + return svc } func connRecorder(t *testing.T, p provider) *connection.Recorder { diff --git a/pkg/didcomm/protocol/mediator/service.go b/pkg/didcomm/protocol/mediator/service.go index 3c0c965e9b..a570dd4261 100644 --- a/pkg/didcomm/protocol/mediator/service.go +++ b/pkg/didcomm/protocol/mediator/service.go @@ -710,6 +710,7 @@ func (s *Service) GetConnections() ([]string, error) { // TODO https://github.com/hyperledger/aries-framework-go/issues/1105 Support to Add multiple // recKeys to the Router func (s *Service) AddKey(connID, recKey string) error { + logger.Warnf("~~~~~~~~ about to add Router Key: %v", recKey) // check if router is already registered err := s.ensureConnectionExists(connID) if err != nil { diff --git a/pkg/didcomm/protocol/mediator/service_test.go b/pkg/didcomm/protocol/mediator/service_test.go index 6e65edf06c..965f008ea7 100644 --- a/pkg/didcomm/protocol/mediator/service_test.go +++ b/pkg/didcomm/protocol/mediator/service_test.go @@ -835,7 +835,7 @@ func TestServiceForwardMsg(t *testing.T) { if didID == invalidDID { return nil, errors.New("invalid") } - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, }) @@ -888,7 +888,7 @@ func TestMessagePickup(t *testing.T) { }, VDRegistryValue: &mockvdr.MockVDRegistry{ ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, }) @@ -931,7 +931,7 @@ func TestMessagePickup(t *testing.T) { }, VDRegistryValue: &mockvdr.MockVDRegistry{ ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (doc *did.DocResolution, e error) { - return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t)}, nil + return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil }, }, }) diff --git a/pkg/didcomm/protocol/outofband/service.go b/pkg/didcomm/protocol/outofband/service.go index 0a0675ae8e..26d93db72b 100644 --- a/pkg/didcomm/protocol/outofband/service.go +++ b/pkg/didcomm/protocol/outofband/service.go @@ -10,11 +10,13 @@ import ( "encoding/json" "errors" "fmt" + "strings" "github.com/google/uuid" "github.com/mitchellh/mapstructure" "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange" @@ -941,8 +943,24 @@ func chooseTarget(svcs []interface{}) (interface{}, error) { } err = decoder.Decode(svc) + //nolint:nestif if err != nil { - return nil, fmt.Errorf("failed to decode service block : %w", err) + var targetErr *mapstructure.Error + + if errors.As(err, &targetErr) { + for _, er := range targetErr.Errors { + // if serviceEndpoint is a string, explicitly convert it using model.DIDCommV1Endpoint(). + if strings.EqualFold(er, "'serviceEndpoint' expected a map, got 'string'") { + uri, ok := svc["serviceEndpoint"].(string) + if ok { + s.ServiceEndpoint = model.NewDIDCommV1Endpoint(uri) + return &s, nil + } + } + } + } + + return nil, fmt.Errorf("failed to decode service block : %w, svc: %#v", err, svc) } return &s, nil diff --git a/pkg/didcomm/protocol/outofband/service_test.go b/pkg/didcomm/protocol/outofband/service_test.go index 3e2de7dddd..2ecb88f748 100644 --- a/pkg/didcomm/protocol/outofband/service_test.go +++ b/pkg/didcomm/protocol/outofband/service_test.go @@ -142,6 +142,20 @@ func TestHandleInbound(t *testing.T) { _, err := s.HandleInbound(service.NewDIDCommMsgMap(newInvitation()), service.NewDIDCommContext(myDID, theirDID, nil)) require.NoError(t, err) }) + + t.Run("accepts out-of-band invitation messages with service as map[string]interface{} and serviceEndpoint as "+ + "string (DIDCommV1)", func(t *testing.T) { + s := newAutoService(t, testProvider()) + customServiceMap := map[string]interface{}{ + "recipientKeys": []string{"did:key:123"}, + "serviceEndpoint": "http://user.agent.aries.js.example.com:10081", + "type": "did-communication", + } + _, err := s.HandleInbound(service.NewDIDCommMsgMap(newInvitationWithService(customServiceMap)), + service.NewDIDCommContext(myDID, theirDID, nil)) + require.NoError(t, err) + }) + t.Run("rejects unsupported message types", func(t *testing.T) { s, err := New(testProvider()) require.NoError(t, err) @@ -978,14 +992,12 @@ func TestChooseTarget(t *testing.T) { }) t.Run("chooses a did service entry", func(t *testing.T) { expected := &did.Service{ - ID: uuid.New().String(), - Type: "did-communication", - Priority: 0, - RecipientKeys: []string{"my ver key"}, - ServiceEndpoint: commonmodel.Endpoint{ - URI: "my service endpoint", - RoutingKeys: []string{"my routing key"}, - }, + ID: uuid.New().String(), + Type: "did-communication", + Priority: 0, + RecipientKeys: []string{"my ver key"}, + ServiceEndpoint: commonmodel.NewDIDCommV1Endpoint("my service endpoint"), + RoutingKeys: []string{"my routing key"}, } result, err := chooseTarget([]interface{}{expected}) require.NoError(t, err) @@ -993,14 +1005,12 @@ func TestChooseTarget(t *testing.T) { }) t.Run("chooses a map-type service", func(t *testing.T) { expected := map[string]interface{}{ - "id": uuid.New().String(), - "type": "did-communication", - "priority": uint(0), - "recipientKeys": []string{"my ver key"}, - "serviceEndpoint": commonmodel.Endpoint{ - URI: "my service endpoint", - RoutingKeys: []string{"my routing key"}, - }, + "id": uuid.New().String(), + "type": "did-communication", + "priority": uint(0), + "recipientKeys": []string{"my ver key"}, + "serviceEndpoint": commonmodel.NewDIDCommV1Endpoint("my service endpoint"), + "RoutingKeys": []string{"my routing key"}, } svc, err := chooseTarget([]interface{}{expected}) require.NoError(t, err) @@ -1029,13 +1039,17 @@ func testProvider() *protocol.MockProvider { } func newInvitation() *Invitation { + return newInvitationWithService("did:example:1235") +} + +func newInvitationWithService(svc interface{}) *Invitation { return &Invitation{ ID: uuid.New().String(), Type: InvitationMsgType, Label: "test", Goal: "test", GoalCode: "test", - Services: []interface{}{"did:example:1235"}, + Services: []interface{}{svc}, Protocols: []string{didexchange.PIURI}, Requests: []*decorator.Attachment{ { diff --git a/pkg/didcomm/protocol/outofbandv2/service.go b/pkg/didcomm/protocol/outofbandv2/service.go index a8feb21f2b..47d4649715 100644 --- a/pkg/didcomm/protocol/outofbandv2/service.go +++ b/pkg/didcomm/protocol/outofbandv2/service.go @@ -268,11 +268,9 @@ func (s *Service) AcceptInvitation(i *Invitation, opts ...AcceptOption) (string, } services = append(services, did.Service{ - ServiceEndpoint: model.Endpoint{ - URI: serviceEndpoint, - Accept: s.myMediaTypeProfiles, - RoutingKeys: routingKeys, - }, + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: serviceEndpoint, Accept: s.myMediaTypeProfiles, RoutingKeys: routingKeys}, + }), RecipientKeys: []string{recKey}, Type: vdrapi.DIDCommV2ServiceType, }) diff --git a/pkg/didcomm/protocol/outofbandv2/service_test.go b/pkg/didcomm/protocol/outofbandv2/service_test.go index e74118948b..a619600347 100644 --- a/pkg/didcomm/protocol/outofbandv2/service_test.go +++ b/pkg/didcomm/protocol/outofbandv2/service_test.go @@ -226,7 +226,7 @@ func TestAcceptInvitation(t *testing.T) { s.vdrRegistry = &mockvdr.MockVDRegistry{ ResolveFunc: func(id string, _ ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { return &did.DocResolution{ - DIDDocument: mockdiddoc.GetMockDIDDoc(t), + DIDDocument: mockdiddoc.GetMockDIDDoc(t, true), }, nil }, } @@ -267,7 +267,7 @@ func TestAcceptInvitation(t *testing.T) { }, ResolveFunc: func(id string, _ ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { return &did.DocResolution{ - DIDDocument: mockdiddoc.GetMockDIDDoc(t), + DIDDocument: mockdiddoc.GetMockDIDDoc(t, true), }, nil }, } @@ -280,8 +280,11 @@ func TestAcceptInvitation(t *testing.T) { docSvc, ok := did.LookupService(createdDoc, vdrapi.DIDCommV2ServiceType) require.True(t, ok) - require.Len(t, docSvc.ServiceEndpoint.RoutingKeys, 1) - require.Equal(t, testKey, docSvc.ServiceEndpoint.RoutingKeys[0]) + routingKeys, err := docSvc.ServiceEndpoint.RoutingKeys() + require.NoError(t, err) + + require.Len(t, routingKeys, 1) + require.Equal(t, testKey, routingKeys[0]) }) t.Run("error fetching mediator config", func(t *testing.T) { @@ -338,7 +341,7 @@ func TestAcceptInvitation(t *testing.T) { }, ResolveFunc: func(id string, _ ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { return &did.DocResolution{ - DIDDocument: mockdiddoc.GetMockDIDDoc(t), + DIDDocument: mockdiddoc.GetMockDIDDoc(t, true), }, nil }, } @@ -387,7 +390,7 @@ func TestAcceptInvitation(t *testing.T) { s.vdrRegistry = &mockvdr.MockVDRegistry{ ResolveFunc: func(id string, _ ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { return &did.DocResolution{ - DIDDocument: mockdiddoc.GetMockDIDDoc(t), + DIDDocument: mockdiddoc.GetMockDIDDoc(t, true), }, nil }, } diff --git a/pkg/didcomm/transport/http/outbound.go b/pkg/didcomm/transport/http/outbound.go index ffd48cd1e0..5bea1ab2b6 100644 --- a/pkg/didcomm/transport/http/outbound.go +++ b/pkg/didcomm/transport/http/outbound.go @@ -92,7 +92,12 @@ func (cs *OutboundHTTPClient) Start(prov transport.Provider) error { // Send sends a2a exchange data via HTTP (client side). func (cs *OutboundHTTPClient) Send(data []byte, destination *service.Destination) (string, error) { - resp, err := cs.client.Post(destination.ServiceEndpoint.URI, commContentType, bytes.NewBuffer(data)) + uri, err := destination.ServiceEndpoint.URI() + if err != nil { + return "", fmt.Errorf("error getting ServiceEndpoint URI: %w", err) + } + + resp, err := cs.client.Post(uri, commContentType, bytes.NewBuffer(data)) if err != nil { logger.Errorf("posting DID envelope to agent failed [%s, %v]", destination.ServiceEndpoint, err) return "", err diff --git a/pkg/didcomm/transport/http/outbound_test.go b/pkg/didcomm/transport/http/outbound_test.go index b211dbee05..bf6394caf3 100644 --- a/pkg/didcomm/transport/http/outbound_test.go +++ b/pkg/didcomm/transport/http/outbound_test.go @@ -102,6 +102,6 @@ func TestOutboundHTTPTransport(t *testing.T) { func prepareDestination(endPoint string) *service.Destination { return &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: endPoint}, + ServiceEndpoint: model.NewDIDCommV1Endpoint(endPoint), } } diff --git a/pkg/didcomm/transport/ws/outbound.go b/pkg/didcomm/transport/ws/outbound.go index c0a5b8b804..926401f220 100644 --- a/pkg/didcomm/transport/ws/outbound.go +++ b/pkg/didcomm/transport/ws/outbound.go @@ -86,14 +86,16 @@ func (cs *OutboundClient) AcceptRecipient(keys []string) bool { return acceptRecipient(cs.pool, keys) } -//nolint:gocyclo +//nolint:gocyclo,funlen func (cs *OutboundClient) getConnection(destination *service.Destination) (*websocket.Conn, func(), error) { var conn *websocket.Conn // get the connection for the routing or recipient keys keys := destination.RecipientKeys - if len(destination.ServiceEndpoint.RoutingKeys) != 0 { - keys = destination.ServiceEndpoint.RoutingKeys + if routingKeys, err := destination.ServiceEndpoint.RoutingKeys(); err == nil && len(routingKeys) != 0 { + keys = routingKeys + } else if len(destination.RoutingKeys) != 0 { + keys = destination.RoutingKeys } for _, v := range keys { @@ -110,9 +112,17 @@ func (cs *OutboundClient) getConnection(destination *service.Destination) (*webs return conn, cleanup, nil } - var err error + var ( + err error + uri string + ) - conn, _, err = websocket.Dial(context.Background(), destination.ServiceEndpoint.URI, nil) + uri, err = destination.ServiceEndpoint.URI() + if err != nil { + return nil, cleanup, fmt.Errorf("unable to send ws outbound request: %w", err) + } + + conn, _, err = websocket.Dial(context.Background(), uri, nil) if err != nil { return nil, cleanup, fmt.Errorf("websocket client : %w", err) } diff --git a/pkg/didcomm/transport/ws/outbound_test.go b/pkg/didcomm/transport/ws/outbound_test.go index 499c6ddd50..96e358977b 100644 --- a/pkg/didcomm/transport/ws/outbound_test.go +++ b/pkg/didcomm/transport/ws/outbound_test.go @@ -93,7 +93,7 @@ func TestClient(t *testing.T) { addr := startWebSocketServer(t, echo) resp, err := outbound.Send(createTransportDecRequest(t, decorator.TransportReturnRouteAll), - prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, recKey)) + prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, recKey, nil)) require.NoError(t, err) require.Equal(t, "", resp) @@ -125,8 +125,7 @@ func TestClient(t *testing.T) { addr := startWebSocketServer(t, echo) - des := prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, recKey) - des.ServiceEndpoint.RoutingKeys = routingKeys + des := prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, recKey, routingKeys) data := "didcomm-message" resp, err := outbound.Send([]byte(data), des) @@ -146,7 +145,7 @@ func TestClient(t *testing.T) { addr := startWebSocketServer(t, echo) resp, err := outbound.Send(createTransportDecRequest(t, decorator.TransportReturnRouteAll), - prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, nil)) + prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, nil, nil)) require.NoError(t, err) require.Equal(t, "", resp) }) @@ -163,7 +162,7 @@ func TestClient(t *testing.T) { addr := startWebSocketServer(t, echo) resp, err := outbound.Send(createTransportDecRequest(t, decorator.TransportReturnRouteNone), - prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteNone, nil)) + prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteNone, nil, nil)) require.NoError(t, err) require.Equal(t, "", resp) }) @@ -213,7 +212,7 @@ func TestClient(t *testing.T) { require.NoError(t, err) resp, err := outboundClient.Send([]byte("data"), - prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, nil)) + prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, nil, nil)) require.NoError(t, err) require.Equal(t, "", resp) diff --git a/pkg/didcomm/transport/ws/pool_test.go b/pkg/didcomm/transport/ws/pool_test.go index 2a51718b01..a7cea62c7f 100644 --- a/pkg/didcomm/transport/ws/pool_test.go +++ b/pkg/didcomm/transport/ws/pool_test.go @@ -59,7 +59,7 @@ func TestConnectionStore(t *testing.T) { frameworkID: uuid.New().String(), executeInbound: func(envelope *transport.Envelope) error { resp, outboundErr := outbound.Send([]byte(response), - prepareDestinationWithTransport("ws://doesnt-matter", "", []string{verKey})) + prepareDestinationWithTransport("ws://doesnt-matter", "", []string{verKey}, nil)) require.NoError(t, outboundErr) require.Equal(t, "", resp) return nil @@ -125,7 +125,7 @@ func TestConnectionStore(t *testing.T) { // send the outbound message resp, err := outbound.Send(request, - prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, []string{verKey})) + prepareDestinationWithTransport("ws://"+addr, decorator.TransportReturnRouteAll, []string{verKey}, nil)) require.NoError(t, err) require.Equal(t, "", resp) diff --git a/pkg/didcomm/transport/ws/support_test.go b/pkg/didcomm/transport/ws/support_test.go index d6a44036e5..d646ca68ac 100644 --- a/pkg/didcomm/transport/ws/support_test.go +++ b/pkg/didcomm/transport/ws/support_test.go @@ -67,13 +67,15 @@ func websocketClient(t *testing.T, port string) (*websocket.Conn, func()) { func prepareDestination(endPoint string) *service.Destination { return &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: endPoint}, + ServiceEndpoint: model.NewDIDCommV1Endpoint(endPoint), } } -func prepareDestinationWithTransport(endPoint, returnRoute string, recipientKeys []string) *service.Destination { +func prepareDestinationWithTransport(endPoint, returnRoute string, + recipientKeys, routingKeys []string) *service.Destination { return &service.Destination{ - ServiceEndpoint: model.Endpoint{URI: endPoint}, + ServiceEndpoint: model.NewDIDCommV1Endpoint(endPoint), + RoutingKeys: routingKeys, RecipientKeys: recipientKeys, TransportReturnRoute: returnRoute, } diff --git a/pkg/doc/did/doc.go b/pkg/doc/did/doc.go index 5406db69b8..69ecd68619 100644 --- a/pkg/doc/did/doc.go +++ b/pkg/doc/did/doc.go @@ -21,6 +21,7 @@ import ( "github.com/multiformats/go-multibase" "github.com/xeipuuv/gojsonschema" + "github.com/hyperledger/aries-framework-go/pkg/common/log" "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk" "github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld" @@ -64,6 +65,7 @@ var ( schemaLoaderV1 = gojsonschema.NewStringLoader(schemaV1) //nolint:gochecknoglobals schemaLoaderV011 = gojsonschema.NewStringLoader(schemaV011) //nolint:gochecknoglobals schemaLoaderV12019 = gojsonschema.NewStringLoader(schemaV12019) //nolint:gochecknoglobals + logger = log.New("aries-framework/doc/did") //nolint:gochecknoglobals ) // ErrDIDDocumentNotExist error did doc not exist. @@ -365,7 +367,9 @@ type Service struct { Type string `json:"type"` Priority uint `json:"priority,omitempty"` RecipientKeys []string `json:"recipientKeys,omitempty"` + RoutingKeys []string `json:"routingKeys,omitempty"` ServiceEndpoint model.Endpoint `json:"serviceEndpoint"` + Accept []string `json:"accept,omitempty"` Properties map[string]interface{} `json:"properties,omitempty"` recipientKeysRelativeURL map[string]bool routingKeysRelativeURL map[string]bool @@ -633,14 +637,14 @@ func populateProofs(context, didID, baseURI string, rawProofs []interface{}) ([] return proofs, nil } -//nolint:funlen +//nolint:funlen,gocyclo func populateServices(didID, baseURI string, rawServices []map[string]interface{}) []Service { services := make([]Service, 0, len(rawServices)) for _, rawService := range rawServices { id := stringEntry(rawService[jsonldID]) recipientKeys := stringArray(rawService[jsonldRecipientKeys]) - routingKeys := stringArray(rawService[jsonldRoutingKeys]) + routingKeys := stringArray(rawService[jsonldRoutingKeys]) // routingkeys here for DIDComm V1 only. var recipientKeysRelativeURL map[string]bool @@ -661,28 +665,26 @@ func populateServices(didID, baseURI string, rawServices []map[string]interface{ routingKeys, routingKeysRelativeURL = populateKeys(routingKeys, didID, baseURI) } - sp := model.Endpoint{ - RoutingKeys: routingKeys, - } + var sp model.Endpoint //nolint:nestif if epEntry, ok := rawService[jsonldServicePoint]; ok { - var ( - uriStr string - accept []string - ) - - if uriStr, ok = epEntry.(string); ok { - sp.URI = uriStr - } else { - epMapEntry := mapEntry(epEntry) - - if uriStr, ok = epMapEntry["uri"].(string); ok { - sp.URI = uriStr - } - - if accept, ok = epMapEntry["accept"].([]string); ok { - sp.Accept = accept + uriStr, ok := epEntry.(string) + // for now handling DIDComm V1 or V2 only. + if ok { // DIDComm V1 format. + sp = model.NewDIDCommV1Endpoint(uriStr) + } else if epEntry != nil { // DIDComm V2 format (first valid entry for now). + entries, ok := epEntry.([]interface{}) + if ok && len(entries) > 0 { + firstEntry, ok := entries[0].(map[string]interface{}) + if ok { + epURI := stringEntry(firstEntry["uri"]) + epAccept := stringArray(firstEntry["accept"]) + epRoutingKeys := stringArray(firstEntry["routingKeys"]) + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: epURI, Accept: epAccept, RoutingKeys: epRoutingKeys}, + }) + } } } } @@ -692,6 +694,7 @@ func populateServices(didID, baseURI string, rawServices []map[string]interface{ ServiceEndpoint: sp, RecipientKeys: recipientKeys, Priority: uintEntry(rawService[jsonldPriority]), + RoutingKeys: routingKeys, recipientKeysRelativeURL: recipientKeysRelativeURL, routingKeysRelativeURL: routingKeysRelativeURL, } @@ -1025,7 +1028,11 @@ func stringEntry(entry interface{}) string { return "" } - return entry.(string) + if e, ok := entry.(string); ok { + return e + } + + return "" } // uintEntry. @@ -1284,6 +1291,7 @@ func (r *didKeyResolver) Resolve(id string) (*verifier.PublicKey, error) { // ErrKeyNotFound is returned when key is not found. var ErrKeyNotFound = errors.New("key not found") +// nolint:funlen,gocognit,gocyclo func populateRawServices(services []Service, didID, baseURI string) []map[string]interface{} { var rawServices []map[string]interface{} @@ -1296,7 +1304,7 @@ func populateRawServices(services []Service, didID, baseURI string) []map[string routingKeys := make([]string, 0) - for _, v := range services[i].ServiceEndpoint.RoutingKeys { + for _, v := range services[i].RoutingKeys { if services[i].routingKeysRelativeURL[v] { routingKeys = append(routingKeys, makeRelativeDIDURL(v, baseURI, didID)) continue @@ -1305,6 +1313,38 @@ func populateRawServices(services []Service, didID, baseURI string) []map[string routingKeys = append(routingKeys, v) } + sepRoutingKeys, err := services[i].ServiceEndpoint.RoutingKeys() + if err == nil && len(sepRoutingKeys) > 0 { + var tmpRoutingKeys []string + + for _, v := range sepRoutingKeys { + if services[i].routingKeysRelativeURL[v] { + tmpRoutingKeys = append(tmpRoutingKeys, makeRelativeDIDURL(v, baseURI, didID)) + continue + } + + tmpRoutingKeys = append(tmpRoutingKeys, v) + } + + sepRoutingKeys = tmpRoutingKeys + } + + sepAccept, err := services[i].ServiceEndpoint.Accept() + if err != nil { + logger.Debugf("accept field of DIDComm V2 endpoint missing or invalid, it will be ignored: %w", err) + } + + sepURI, err := services[i].ServiceEndpoint.URI() + if err != nil { + logger.Debugf("URI field of DIDComm V2 endpoint missing or invalid, it will be ignored: %w", err) + } + + if len(sepAccept) > 0 || len(sepRoutingKeys) > 0 { + services[i].ServiceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: sepURI, Accept: sepAccept, RoutingKeys: sepRoutingKeys}, + }) + } + recipientKeys := make([]string, 0) for _, v := range services[i].RecipientKeys { @@ -1322,7 +1362,23 @@ func populateRawServices(services []Service, didID, baseURI string) []map[string } rawService[jsonldType] = services[i].Type - rawService[jsonldServicePoint] = services[i].ServiceEndpoint + + if len(sepAccept) > 0 || len(sepRoutingKeys) > 0 { // DIDComm V2 + if len(sepAccept) == 0 { // ensure array is non nil, to avoid schema validation errors. + sepAccept = []string{} + } + + if len(sepRoutingKeys) == 0 { // ensure array is non nil, to avoid schema validation errors. + sepRoutingKeys = []string{} + } + + rawService[jsonldServicePoint] = []map[string]interface{}{ + {"uri": sepURI, "accept": sepAccept, "routingKeys": sepRoutingKeys}, + } + } else { // DIDComm V1 + rawService[jsonldServicePoint] = sepURI + } + rawService[jsonldPriority] = services[i].Priority if len(recipientKeys) > 0 { diff --git a/pkg/doc/did/doc_test.go b/pkg/doc/did/doc_test.go index 775cac4b6e..77aade795e 100644 --- a/pkg/doc/did/doc_test.go +++ b/pkg/doc/did/doc_test.go @@ -144,19 +144,17 @@ func TestValidWithDocBase(t *testing.T) { ID: "did:example:123456789abcdefghi#inbox", Type: "SocialWebInboxService", relativeURL: true, - ServiceEndpoint: model.Endpoint{URI: "https://social.example.com/83hfh37dj"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://social.example.com/83hfh37dj"), Properties: map[string]interface{}{"spamCost": map[string]interface{}{"amount": "0.50", "currency": "USD"}}, }, { - ID: "did:example:123456789abcdefghi#did-communication", - Type: "did-communication", - Priority: 0, - relativeURL: true, - RecipientKeys: []string{"did:example:123456789abcdefghi#key2"}, - ServiceEndpoint: model.Endpoint{ - URI: "https://agent.example.com/", - RoutingKeys: []string{"did:example:123456789abcdefghi#key2"}, - }, + ID: "did:example:123456789abcdefghi#did-communication", + Type: "did-communication", + Priority: 0, + relativeURL: true, + RecipientKeys: []string{"did:example:123456789abcdefghi#key2"}, + RoutingKeys: []string{"did:example:123456789abcdefghi#key2"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://agent.example.com/"), Properties: map[string]interface{}{}, recipientKeysRelativeURL: map[string]bool{"did:example:123456789abcdefghi#key2": true}, routingKeysRelativeURL: map[string]bool{"did:example:123456789abcdefghi#key2": true}, @@ -253,18 +251,16 @@ func TestValid(t *testing.T) { { ID: "did:example:123456789abcdefghi#inbox", Type: "SocialWebInboxService", - ServiceEndpoint: model.Endpoint{URI: "https://social.example.com/83hfh37dj"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://social.example.com/83hfh37dj"), Properties: map[string]interface{}{"spamCost": map[string]interface{}{"amount": "0.50", "currency": "USD"}}, }, { - ID: "did:example:123456789abcdefghi#did-communication", - Type: "did-communication", - Priority: 0, - RecipientKeys: []string{"did:example:123456789abcdefghi#key2"}, - ServiceEndpoint: model.Endpoint{ - URI: "https://agent.example.com/", - RoutingKeys: []string{"did:example:123456789abcdefghi#key2"}, - }, + ID: "did:example:123456789abcdefghi#did-communication", + Type: "did-communication", + Priority: 0, + RecipientKeys: []string{"did:example:123456789abcdefghi#key2"}, + RoutingKeys: []string{"did:example:123456789abcdefghi#key2"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://agent.example.com/"), Properties: map[string]interface{}{}, recipientKeysRelativeURL: map[string]bool{"did:example:123456789abcdefghi#key2": false}, routingKeysRelativeURL: map[string]bool{"did:example:123456789abcdefghi#key2": false}, diff --git a/pkg/doc/did/helpers_test.go b/pkg/doc/did/helpers_test.go index 30a37bb64e..3657888405 100644 --- a/pkg/doc/did/helpers_test.go +++ b/pkg/doc/did/helpers_test.go @@ -16,7 +16,7 @@ import ( func TestGetRecipientKeys(t *testing.T) { t.Run("successfully getting recipient keys", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) recipientKeys, ok := LookupDIDCommRecipientKeys(didDoc) require.True(t, ok) @@ -24,7 +24,7 @@ func TestGetRecipientKeys(t *testing.T) { }) t.Run("error due to missing did-communication service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service = nil recipientKeys, ok := LookupDIDCommRecipientKeys(didDoc) @@ -33,7 +33,7 @@ func TestGetRecipientKeys(t *testing.T) { }) t.Run("error due to missing recipient keys in did-communication service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service[0].RecipientKeys = []string{} recipientKeys, ok := LookupDIDCommRecipientKeys(didDoc) @@ -46,7 +46,7 @@ func TestGetDidCommService(t *testing.T) { didCommServiceType := "did-communication" t.Run("successfully getting did-communication service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) s, ok := LookupService(didDoc, didCommServiceType) require.True(t, ok) @@ -55,7 +55,7 @@ func TestGetDidCommService(t *testing.T) { }) t.Run("error due to missing service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service = nil s, ok := LookupService(didDoc, didCommServiceType) @@ -64,7 +64,7 @@ func TestGetDidCommService(t *testing.T) { }) t.Run("error due to missing did-communication service", func(t *testing.T) { - didDoc := mockdiddoc.GetMockDIDDoc(t) + didDoc := mockdiddoc.GetMockDIDDoc(t, false) didDoc.Service[0].Type = "some-type" s, ok := LookupService(didDoc, didCommServiceType) diff --git a/pkg/doc/did/schema.go b/pkg/doc/did/schema.go index 7b9cf36a67..10a6c298cf 100644 --- a/pkg/doc/did/schema.go +++ b/pkg/doc/did/schema.go @@ -135,6 +135,32 @@ const ( } } }, + "serviceEndpoint": { + "type": "object", + "minProperties": 1, + "properties": { + "uri": { + "type": "string", + "format": "uri" + }, + "accept": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + }, + "routingKeys": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + } + } + }, "service": { "required": [ "id", @@ -152,22 +178,13 @@ const ( "serviceEndpoint": { "oneOf": [ { - "type": "object", - "minProperties": 1, - "properties": { - "uri": { - "type": "string", - "format": "uri" - }, - "accept": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - } - } + "type": "array", + "items": { + "$ref": "#/definitions/serviceEndpoint" + } + }, + { + "type": "object" }, { "type": "string", @@ -306,6 +323,32 @@ const ( } } }, + "serviceEndpoint": { + "type": "object", + "minProperties": 1, + "properties": { + "uri": { + "type": "string", + "format": "uri" + }, + "accept": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + }, + "routingKeys": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + } + } + }, "service": { "required": [ "id", @@ -323,22 +366,13 @@ const ( "serviceEndpoint": { "oneOf": [ { - "type": "object", - "minProperties": 1, - "properties": { - "uri": { - "type": "string", - "format": "uri" - }, - "accept": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - } - } + "type": "array", + "items": { + "$ref": "#/definitions/serviceEndpoint" + } + }, + { + "type": "object" }, { "type": "string", @@ -448,6 +482,32 @@ const ( } } }, + "serviceEndpoint": { + "type": "object", + "minProperties": 1, + "properties": { + "uri": { + "type": "string", + "format": "uri" + }, + "accept": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + }, + "routingKeys": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + } + } + }, "service": { "required": [ "type", @@ -464,22 +524,13 @@ const ( "serviceEndpoint": { "oneOf": [ { - "type": "object", - "minProperties": 1, - "properties": { - "uri": { - "type": "string", - "format": "uri" - }, - "accept": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - } - } + "type": "array", + "items": { + "$ref": "#/definitions/serviceEndpoint" + } + }, + { + "type": "object" }, { "type": "string", diff --git a/pkg/doc/verifiable/credential_jwt_proof_test.go b/pkg/doc/verifiable/credential_jwt_proof_test.go index 59264b7fa3..4eaf62fd7c 100644 --- a/pkg/doc/verifiable/credential_jwt_proof_test.go +++ b/pkg/doc/verifiable/credential_jwt_proof_test.go @@ -240,7 +240,7 @@ func createDIDKeyFetcher(t *testing.T, pub ed25519.PublicKey, didID string) Publ { ID: fmt.Sprintf(didServiceID, id, 1), Type: "did-communication", - ServiceEndpoint: model.Endpoint{URI: "http://localhost:47582"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://localhost:47582"), Priority: 0, RecipientKeys: []string{pubKeyID}, }, diff --git a/pkg/framework/aries/framework_test.go b/pkg/framework/aries/framework_test.go index f8699aa978..3e3ef179fa 100644 --- a/pkg/framework/aries/framework_test.go +++ b/pkg/framework/aries/framework_test.go @@ -126,7 +126,7 @@ func TestFramework(t *testing.T) { e := ctx.OutboundDispatcher().Send( []byte("Hello World"), mockdiddoc.MockDIDKey(t), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: serverURL}}, + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint(serverURL)}, ) require.NoError(t, e) }) @@ -584,11 +584,11 @@ func TestFramework(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(aries.outboundTransports)) r, err := aries.outboundTransports[0].Send([]byte("data"), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")}) require.NoError(t, err) require.Equal(t, "data", r) r, err = aries.outboundTransports[1].Send([]byte("data1"), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")}) require.NoError(t, err) require.Equal(t, "data1", r) require.NoError(t, aries.Close()) diff --git a/pkg/framework/context/context_test.go b/pkg/framework/context/context_test.go index 329de25f32..3dea55aba5 100644 --- a/pkg/framework/context/context_test.go +++ b/pkg/framework/context/context_test.go @@ -770,12 +770,12 @@ func TestNewProvider(t *testing.T) { require.NoError(t, err) require.Len(t, prov.OutboundTransports(), 2) r, err := prov.outboundTransports[0].Send([]byte("data"), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}, + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")}, ) require.NoError(t, err) require.Equal(t, "data", r) r, err = prov.outboundTransports[1].Send([]byte("data1"), - &service.Destination{ServiceEndpoint: model.Endpoint{URI: "url"}}) + &service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")}) require.NoError(t, err) require.Equal(t, "data1", r) }) diff --git a/pkg/mock/diddoc/mock_diddoc.go b/pkg/mock/diddoc/mock_diddoc.go index 7a2245015c..d05f5d8090 100644 --- a/pkg/mock/diddoc/mock_diddoc.go +++ b/pkg/mock/diddoc/mock_diddoc.go @@ -15,27 +15,64 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/doc/did" + "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk" "github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint" ) // GetMockDIDDoc creates a mock DID Doc for testing. -func GetMockDIDDoc(t *testing.T) *did.Doc { +//nolint:funlen +func GetMockDIDDoc(t *testing.T, isDIDCommV2 bool) *did.Doc { t.Helper() + var keyAgreements []did.Verification + + services := []did.Service{ + { + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://localhost:8090"), + RoutingKeys: []string{MockDIDKey(t)}, + Type: "did-communication", + Priority: 0, + RecipientKeys: []string{MockDIDKey(t)}, + }, + } + + if isDIDCommV2 { + services[0].ServiceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + { + URI: "https://localhost:8090", + Accept: []string{"didcomm/v2"}, + RoutingKeys: []string{MockDIDKey(t)}, + }, + }) + services[0].Type = "DIDCommMessaging" + services[0].RoutingKeys = nil + services[0].RecipientKeys = []string{"#key-2"} + x25519 := jwk.JWK{} + + err := x25519.UnmarshalJSON([]byte(`{ + "kty": "OKP", + "crv": "X25519", + "x": "EXXinkMxdA4zGmwpOOpbCXt6Ts6CwyXyEKI3jfHkS3k" + }`)) + if err == nil { + keyBytes, err := x25519.MarshalJSON() + if err == nil { + keyAgreements = append(keyAgreements, did.Verification{ + VerificationMethod: did.VerificationMethod{ + ID: "did:example:123456789abcdefghi#key-2", + Type: "JSONWebKey2020", + Controller: "did:example:123456789abcdefghi", + Value: keyBytes, + }, + }) + } + } + } + return &did.Doc{ Context: []string{"https://w3id.org/did/v1"}, ID: "did:peer:123456789abcdefghi", - Service: []did.Service{ - { - ServiceEndpoint: model.Endpoint{ - URI: "https://localhost:8090", - RoutingKeys: []string{MockDIDKey(t)}, - }, - Type: "did-communication", - Priority: 0, - RecipientKeys: []string{MockDIDKey(t)}, - }, - }, + Service: services, VerificationMethod: []did.VerificationMethod{ { ID: "did:example:123456789abcdefghi#keys-1", @@ -56,6 +93,7 @@ func GetMockDIDDoc(t *testing.T) *did.Doc { Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), }, }, + KeyAgreement: keyAgreements, } } @@ -72,20 +110,16 @@ func GetLegacyInteropMockDIDDoc(t *testing.T, id string, ed25519PubKey []byte) * ID: peerDID, Service: []did.Service{ { - ServiceEndpoint: model.Endpoint{ - URI: "https://localhost:8090", - }, - Type: "did-communication", - Priority: 0, - RecipientKeys: []string{pubKeyBase58}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://localhost:8090"), + Type: "did-communication", + Priority: 0, + RecipientKeys: []string{pubKeyBase58}, }, { - ServiceEndpoint: model.Endpoint{ - URI: "https://localhost:8090", - }, - Type: "IndyAgent", - Priority: 0, - RecipientKeys: []string{pubKeyBase58}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://localhost:8090"), + Type: "IndyAgent", + Priority: 0, + RecipientKeys: []string{pubKeyBase58}, }, }, VerificationMethod: []did.VerificationMethod{ @@ -101,7 +135,7 @@ func GetLegacyInteropMockDIDDoc(t *testing.T, id string, ed25519PubKey []byte) * // GetMockDIDDocWithKeyAgreements creates mock DID doc with KeyAgreements. func GetMockDIDDocWithKeyAgreements(t *testing.T) *did.Doc { - didDoc := GetMockDIDDoc(t) + didDoc := GetMockDIDDoc(t, false) didDoc.KeyAgreement = []did.Verification{ { @@ -136,10 +170,11 @@ func GetMockDIDDocWithDIDCommV2Bloc(t *testing.T, id string) *did.Doc { ID: peerDID, Service: []did.Service{ { - ServiceEndpoint: model.Endpoint{ + ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{ URI: "https://localhost:8090", + Accept: []string{"didcomm/v2"}, RoutingKeys: []string{MockDIDKey(t)}, - }, + }}), Type: "DIDCommMessaging", Priority: 0, RecipientKeys: []string{MockDIDKey(t)}, @@ -197,13 +232,11 @@ func GetMockIndyDoc(t *testing.T) *did.Doc { }, Service: []did.Service{ { - ID: "did:sov:AyRHrP7u6rF1dKViGf5shA;indy", - Type: "IndyAgent", - Priority: 0, - RecipientKeys: []string{"6SFxbqdqGKtVVmLvXDnq9JP4ziZCG2fJzETpMYHt1VNx"}, - ServiceEndpoint: model.Endpoint{ - URI: "https://localhost:8090", - }, + ID: "did:sov:AyRHrP7u6rF1dKViGf5shA;indy", + Type: "IndyAgent", + Priority: 0, + RecipientKeys: []string{"6SFxbqdqGKtVVmLvXDnq9JP4ziZCG2fJzETpMYHt1VNx"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://localhost:8090"), }, }, Authentication: []did.Verification{ diff --git a/pkg/mock/vdr/mock_registry.go b/pkg/mock/vdr/mock_registry.go index 7b00cbc523..b54a700952 100644 --- a/pkg/mock/vdr/mock_registry.go +++ b/pkg/mock/vdr/mock_registry.go @@ -102,13 +102,11 @@ func createDefaultDID() *did.Doc { } service := did.Service{ - ID: "did:example:123456789abcdefghi#did-communication", - Type: "did-communication", - ServiceEndpoint: model.Endpoint{ - URI: "https://agent.example.com/", - }, - RecipientKeys: []string{creator}, - Priority: 0, + ID: "did:example:123456789abcdefghi#did-communication", + Type: "did-communication", + ServiceEndpoint: model.NewDIDCommV1Endpoint("https://agent.example.com/"), + RecipientKeys: []string{creator}, + Priority: 0, } signingKey := did.VerificationMethod{ diff --git a/pkg/store/connection/connection_lookup.go b/pkg/store/connection/connection_lookup.go index 9496b502da..be5d07fb85 100644 --- a/pkg/store/connection/connection_lookup.go +++ b/pkg/store/connection/connection_lookup.go @@ -64,10 +64,12 @@ type Record struct { MyDID string ServiceEndPoint model.Endpoint // ServiceEndPoint is 'their' DIDComm service endpoint. RecipientKeys []string // RecipientKeys holds 'their' DIDComm recipient keys. + RoutingKeys []string // RoutingKeys holds 'their' DIDComm routing keys. InvitationID string InvitationDID string Implicit bool Namespace string + MediaTypeProfiles []string DIDCommVersion didcomm.Version PeerDIDInitialState string MyDIDRotation *DIDRotationRecord `json:"myDIDRotation,omitempty"` diff --git a/pkg/store/did/didconnection_test.go b/pkg/store/did/didconnection_test.go index 764686e647..0b16efefd8 100644 --- a/pkg/store/did/didconnection_test.go +++ b/pkg/store/did/didconnection_test.go @@ -36,8 +36,8 @@ func TestBaseConnectionStore(t *testing.T) { prov := ctx{ store: mockstorage.NewMockStoreProvider(), vdr: &mockvdr.MockVDRegistry{ - CreateValue: mockdiddoc.GetMockDIDDoc(t), - ResolveValue: mockdiddoc.GetMockDIDDoc(t), + CreateValue: mockdiddoc.GetMockDIDDoc(t, false), + ResolveValue: mockdiddoc.GetMockDIDDoc(t, false), }, } @@ -63,8 +63,8 @@ func TestBaseConnectionStore(t *testing.T) { }, }, vdr: &mockvdr.MockVDRegistry{ - CreateValue: mockdiddoc.GetMockDIDDoc(t), - ResolveValue: mockdiddoc.GetMockDIDDoc(t), + CreateValue: mockdiddoc.GetMockDIDDoc(t, false), + ResolveValue: mockdiddoc.GetMockDIDDoc(t, false), }, }) require.NoError(t, err) @@ -123,7 +123,7 @@ func TestBaseConnectionStore(t *testing.T) { cs, err := NewConnectionStore(&prov) require.NoError(t, err) - err = cs.SaveDIDByResolving(mockdiddoc.GetMockDIDDoc(t).ID) + err = cs.SaveDIDByResolving(mockdiddoc.GetMockDIDDoc(t, false).ID) require.NoError(t, err) }) diff --git a/pkg/store/did/store_test.go b/pkg/store/did/store_test.go index a17f98fdb1..c92dad1b36 100644 --- a/pkg/store/did/store_test.go +++ b/pkg/store/did/store_test.go @@ -258,7 +258,7 @@ func createDIDDocWithKey(pub string) *did.Doc { { ID: fmt.Sprintf(didServiceID, id, 1), Type: "did-communication", - ServiceEndpoint: model.Endpoint{URI: "http://localhost:58416"}, + ServiceEndpoint: model.NewDIDCommV1Endpoint("http://localhost:58416"), Priority: 0, RecipientKeys: []string{pubKeyID}, }, diff --git a/pkg/vdr/peer/creator.go b/pkg/vdr/peer/creator.go index 037f331b07..09c3d53073 100644 --- a/pkg/vdr/peer/creator.go +++ b/pkg/vdr/peer/creator.go @@ -8,10 +8,14 @@ package peer import ( "fmt" + "strings" "time" "github.com/google/uuid" + "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/pkg/common/model" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" "github.com/hyperledger/aries-framework-go/pkg/doc/did" vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" "github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint" @@ -24,6 +28,8 @@ const ( x25519KeyAgreementKey2019 = "X25519KeyAgreementKey2019" ) +var logger = log.New("aries-framework/pkg/vdr/peer") + // Create create new DID Document. // TODO https://github.com/hyperledger/aries-framework-go/issues/2466 func (v *VDR) Create(didDoc *did.Doc, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { @@ -61,7 +67,7 @@ func (v *VDR) Create(didDoc *did.Doc, opts ...vdrapi.DIDMethodOption) (*did.DocR return &did.DocResolution{Context: []string{schemaResV1}, DIDDocument: didDoc}, nil } -//nolint: funlen,gocyclo +//nolint: funlen,gocyclo,gocognit func build(didDoc *did.Doc, docOpts *vdrapi.DIDMethodOpts) (*did.DocResolution, error) { if len(didDoc.VerificationMethod) == 0 && len(didDoc.KeyAgreement) == 0 { return nil, fmt.Errorf("verification method and key agreement are empty, at least one should be set") @@ -89,13 +95,48 @@ func build(didDoc *did.Doc, docOpts *vdrapi.DIDMethodOpts) (*did.DocResolution, didDoc.Service[i].Type = v } - if didDoc.Service[i].ServiceEndpoint.URI == "" && docOpts.Values[DefaultServiceEndpoint] != nil { - v, ok := docOpts.Values[DefaultServiceEndpoint].(string) - if !ok { - return nil, fmt.Errorf("defaultServiceEndpoint not string") - } + uri, e := didDoc.Service[i].ServiceEndpoint.URI() + if e != nil { + logger.Debugf("service endpoint URI returned error %w, ignoring..", e) + } - didDoc.Service[i].ServiceEndpoint.URI = v + // nolint:nestif + if uri == "" && docOpts.Values[DefaultServiceEndpoint] != nil { + switch didDoc.Service[i].Type { + case vdrapi.DIDCommServiceType: + v, ok := docOpts.Values[DefaultServiceEndpoint].(string) + if !ok { + return nil, fmt.Errorf("defaultServiceEndpoint not string") + } + + didDoc.Service[i].ServiceEndpoint = model.NewDIDCommV1Endpoint(v) + case vdrapi.DIDCommV2ServiceType: + epArrayEntry := stringArray(docOpts.Values[DefaultServiceEndpoint]) + + sp := model.Endpoint{} + + if len(epArrayEntry) == 0 { + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{}}) + } else { + for _, ep := range epArrayEntry { + err = sp.UnmarshalJSON([]byte(ep)) + if err != nil { + if strings.EqualFold(err.Error(), "endpoint data is not supported") { + // if unmarshall failed, then use as string. + sp = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: ep, Accept: []string{transport.MediaTypeDIDCommV2Profile}}, + }) + } + + continue + } + + break + } + } + + didDoc.Service[i].ServiceEndpoint = sp + } } applyDIDCommKeys(i, didDoc) @@ -148,6 +189,41 @@ func build(didDoc *did.Doc, docOpts *vdrapi.DIDMethodOpts) (*did.DocResolution, return &did.DocResolution{DIDDocument: didDoc}, nil } +// stringEntry. +func stringEntry(entry interface{}) string { + if entry == nil { + return "" + } + + return entry.(string) +} + +// stringArray. +func stringArray(entry interface{}) []string { + if entry == nil { + return nil + } + + entries, ok := entry.([]interface{}) + if !ok { + if entryStr, ok := entry.(string); ok { + return []string{entryStr} + } + + return nil + } + + var result []string + + for _, e := range entries { + if e != nil { + result = append(result, stringEntry(e)) + } + } + + return result +} + func applyDIDCommKeys(i int, didDoc *did.Doc) { if didDoc.Service[i].Type == vdrapi.DIDCommServiceType { didKey, _ := fingerprint.CreateDIDKey(didDoc.VerificationMethod[0].Value) diff --git a/pkg/vdr/peer/creator_test.go b/pkg/vdr/peer/creator_test.go index e7bbc6edd6..7649a9ce03 100644 --- a/pkg/vdr/peer/creator_test.go +++ b/pkg/vdr/peer/creator_test.go @@ -75,11 +75,9 @@ func TestDIDCreator(t *testing.T) { routingKeys := []string{"abc", "xyz"} docResolution, err := c.Create( &did.Doc{VerificationMethod: []did.VerificationMethod{getSigningKey()}, Service: []did.Service{{ - ServiceEndpoint: model.Endpoint{ - URI: "request-endpoint", - RoutingKeys: routingKeys, - }, - Type: "request-type", + ServiceEndpoint: model.NewDIDCommV1Endpoint("request-endpoint"), + RoutingKeys: routingKeys, + Type: "request-type", }}}) require.NoError(t, err) @@ -88,8 +86,10 @@ func TestDIDCreator(t *testing.T) { // verify service not empty, type and endpoint from request options require.NotEmpty(t, docResolution.DIDDocument.Service) require.Equal(t, "request-type", docResolution.DIDDocument.Service[0].Type) - require.Equal(t, "request-endpoint", docResolution.DIDDocument.Service[0].ServiceEndpoint.URI) - require.Equal(t, routingKeys, docResolution.DIDDocument.Service[0].ServiceEndpoint.RoutingKeys) + uri, err := docResolution.DIDDocument.Service[0].ServiceEndpoint.URI() + require.NoError(t, err) + require.Equal(t, "request-endpoint", uri) + require.Equal(t, routingKeys, docResolution.DIDDocument.Service[0].RoutingKeys) }) t.Run("test request overrides with keyAgreement", func(t *testing.T) { @@ -103,11 +103,9 @@ func TestDIDCreator(t *testing.T) { docResolution, err := c.Create( &did.Doc{ VerificationMethod: []did.VerificationMethod{sVM}, Service: []did.Service{{ - ServiceEndpoint: model.Endpoint{ - URI: "request-endpoint", - RoutingKeys: routingKeys, - }, - Type: "request-type", + ServiceEndpoint: model.NewDIDCommV1Endpoint("request-endpoint"), + RoutingKeys: routingKeys, + Type: "request-type", }}, KeyAgreement: []did.Verification{eVM}, }) @@ -118,8 +116,9 @@ func TestDIDCreator(t *testing.T) { // verify service not empty, type and endpoint from request options require.NotEmpty(t, docResolution.DIDDocument.Service) require.Equal(t, "request-type", docResolution.DIDDocument.Service[0].Type) - require.Equal(t, "request-endpoint", docResolution.DIDDocument.Service[0].ServiceEndpoint.URI) - require.Equal(t, routingKeys, docResolution.DIDDocument.Service[0].ServiceEndpoint.RoutingKeys) + uri, err := docResolution.DIDDocument.Service[0].ServiceEndpoint.URI() + require.NoError(t, err) + require.Equal(t, "request-endpoint", uri) // verify KeyAgreement require.Len(t, docResolution.DIDDocument.KeyAgreement, 1) diff --git a/pkg/vdr/verifiable_compat_test.go b/pkg/vdr/verifiable_compat_test.go index 32a1f6a909..a1fc60610e 100644 --- a/pkg/vdr/verifiable_compat_test.go +++ b/pkg/vdr/verifiable_compat_test.go @@ -153,7 +153,7 @@ func createPeerDIDLikeDIDExchangeService(t *testing.T, a *context.Provider) *did docResolution, err := a.VDRegistry().Create( peer.DIDMethod, &did.Doc{ Service: []did.Service{ - {ServiceEndpoint: model.Endpoint{URI: "http://example.com/didcomm"}}, + {ServiceEndpoint: model.NewDIDCommV1Endpoint("http://example.com/didcomm")}, }, VerificationMethod: []did.VerificationMethod{ authVM, diff --git a/test/bdd/agent/agent_sdk_steps.go b/test/bdd/agent/agent_sdk_steps.go index 7e5996e748..40f631eb0a 100644 --- a/test/bdd/agent/agent_sdk_steps.go +++ b/test/bdd/agent/agent_sdk_steps.go @@ -470,6 +470,7 @@ func withDynamicEnvelopeParams() createAgentOption { func (a *SDKSteps) getStoreProvider(agentID string) storage.Provider { storeProv := leveldb.NewProvider(dbPath + "/" + agentID + uuid.New().String()) + return storeProv } diff --git a/test/bdd/cmd/sidetree/main.go b/test/bdd/cmd/sidetree/main.go index 616f330056..515edb9cda 100644 --- a/test/bdd/cmd/sidetree/main.go +++ b/test/bdd/cmd/sidetree/main.go @@ -12,6 +12,7 @@ import ( "fmt" "os" + "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" "github.com/hyperledger/aries-framework-go/test/bdd/pkg/sidetree" ) @@ -54,7 +55,7 @@ func main() { JWK: j, RecoveryJWK: recoveryJWK, UpdateJWK: updateJWK, - ServiceEndpoint: os.Args[4], + ServiceEndpoint: model.NewDIDCommV1Endpoint(os.Args[4]), }) if err != nil { fmt.Println(err.Error()) diff --git a/test/bdd/features/aries_didcommv2_mediator_e2e_sdk.feature b/test/bdd/features/aries_didcommv2_mediator_e2e_sdk.feature index 43182f8183..59a643f077 100644 --- a/test/bdd/features/aries_didcommv2_mediator_e2e_sdk.feature +++ b/test/bdd/features/aries_didcommv2_mediator_e2e_sdk.feature @@ -9,6 +9,7 @@ Feature: DIDComm v2 Transport between two Agents through DIDComm v2 Routers [SDK] # https://identity.foundation/didcomm-messaging/spec/#routing + @aries_didcommv2_router_sdk_allmediatypes_key_agreement Scenario Outline: Decentralized Identifier(DID) Exchange between two Edge Agents(without Inbound, DIDComm v2 is one way only) through Routers # DID Exchange between Alice and her Router Given options "" "" "" diff --git a/test/bdd/fixtures/sidetree-mock/.env b/test/bdd/fixtures/sidetree-mock/.env index 1b8a4e9aff..9e9bdb14e0 100644 --- a/test/bdd/fixtures/sidetree-mock/.env +++ b/test/bdd/fixtures/sidetree-mock/.env @@ -11,5 +11,5 @@ COMPOSE_DIR=. # sidetree mock -SIDETREE_MOCK_FIXTURE_IMAGE=ghcr.io/trustbloc/sidetree-mock -SIDETREE_MOCK_FIXTURE_IMAGE_TAG=0.6.0 +SIDETREE_MOCK_FIXTURE_IMAGE=ghcr.io/trustbloc-cicd/sidetree-mock +SIDETREE_MOCK_FIXTURE_IMAGE_TAG=0.7.0-snapshot-799d4d5 diff --git a/test/bdd/pkg/didexchange/didexchange_controller_steps.go b/test/bdd/pkg/didexchange/didexchange_controller_steps.go index 7ce347ebed..3b931dbfa0 100644 --- a/test/bdd/pkg/didexchange/didexchange_controller_steps.go +++ b/test/bdd/pkg/didexchange/didexchange_controller_steps.go @@ -20,6 +20,7 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/client/didexchange" "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/pkg/common/model" didexcmd "github.com/hyperledger/aries-framework-go/pkg/controller/command/didexchange" cmdkms "github.com/hyperledger/aries-framework-go/pkg/controller/command/kms" "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" @@ -657,13 +658,16 @@ func (a *ControllerSteps) CreatePublicDIDWithKeyType( // nolint:funlen,gocyclo JWK: j, RecoveryJWK: recoveryJWK, UpdateJWK: updateJWK, - ServiceEndpoint: a.agentServiceEndpoints[destination], + ServiceEndpoint: model.NewDIDCommV1Endpoint(a.agentServiceEndpoints[destination]), } if isDIDCommV2 { params.ServiceType = vdr.DIDCommV2ServiceType params.EncryptionKey = encKey params.EncKeyType = kms.KeyType(encKeyType) + params.ServiceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: a.agentServiceEndpoints[destination], Accept: []string{"didcomm/v2"}}, + }) } doc, err := sidetree.CreateDID(¶ms) diff --git a/test/bdd/pkg/didresolver/didresolver_steps.go b/test/bdd/pkg/didresolver/didresolver_steps.go index d6dec11224..37fd36d5c2 100644 --- a/test/bdd/pkg/didresolver/didresolver_steps.go +++ b/test/bdd/pkg/didresolver/didresolver_steps.go @@ -16,6 +16,7 @@ import ( "github.com/cucumber/godog" "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/pkg/common/model" "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did" "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" @@ -113,6 +114,7 @@ func createDIDDocument(ctx *bddctx.BDDContext, agents, keyType string) error { } serviceType := vdrapi.DIDCommServiceType + serviceEndpoint := model.NewDIDCommV1Endpoint(ctx.AgentCtx[agentID].ServiceEndpoint()) mtps := ctx.AgentCtx[agentID].MediaTypeProfiles() for _, mtp := range mtps { @@ -122,6 +124,9 @@ func createDIDDocument(ctx *bddctx.BDDContext, agents, keyType string) error { case transport.MediaTypeDIDCommV2Profile, transport.MediaTypeAIP2RFC0587Profile: found = true serviceType = vdrapi.DIDCommV2ServiceType + serviceEndpoint = model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ + {URI: ctx.AgentCtx[agentID].ServiceEndpoint()}, + }) } if found { @@ -139,7 +144,7 @@ func createDIDDocument(ctx *bddctx.BDDContext, agents, keyType string) error { EncryptionKey: encKey, KeyType: keyType, EncKeyType: encKT, - ServiceEndpoint: ctx.AgentCtx[agentID].ServiceEndpoint(), + ServiceEndpoint: serviceEndpoint, ServiceType: serviceType, }) if err != nil { diff --git a/test/bdd/pkg/sidetree/sidetree.go b/test/bdd/pkg/sidetree/sidetree.go index 4215f245df..9696c2d899 100644 --- a/test/bdd/pkg/sidetree/sidetree.go +++ b/test/bdd/pkg/sidetree/sidetree.go @@ -16,6 +16,7 @@ import ( "github.com/trustbloc/sidetree-core-go/pkg/util/pubkey" "github.com/trustbloc/sidetree-core-go/pkg/versions/1_0/client" + "github.com/hyperledger/aries-framework-go/pkg/common/model" diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did" "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk" "github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid" @@ -39,7 +40,7 @@ const docTemplate = `{ { "id": "hub", "type": "%s", - "serviceEndpoint": "%s", + "serviceEndpoint": %s, "recipientKeys" : [ "%s" ] } ] @@ -48,7 +49,7 @@ const docTemplate = `{ const ( sha2_256 = 18 // multihash - defaultKeyType = "JwsVerificationKey2020" + defaultKeyType = "JsonWebKey2020" ) type didResolution struct { @@ -68,7 +69,7 @@ type CreateDIDParams struct { EncryptionKey []byte KeyType string EncKeyType kms.KeyType - ServiceEndpoint string + ServiceEndpoint model.Endpoint ServiceType string } @@ -162,7 +163,12 @@ func getOpaqueDocument(params *CreateDIDParams) ([]byte, error) { ka = fmt.Sprintf(ka, "key-2", kaType, encJWKMarshalled) } - data := fmt.Sprintf(docTemplate, params.KeyID, keyType, opsPubKey, ka, serviceType, params.ServiceEndpoint, didKey) + mEndPoint, err := params.ServiceEndpoint.MarshalJSON() + if err != nil { + return nil, err + } + + data := fmt.Sprintf(docTemplate, params.KeyID, keyType, opsPubKey, ka, serviceType, mEndPoint, didKey) doc, err := document.FromBytes([]byte(data)) if err != nil {