From d90a282c2a55d3626da059b30965f819c5acf384 Mon Sep 17 00:00:00 2001 From: Filip Burlacu Date: Tue, 17 Dec 2019 14:44:44 -0500 Subject: [PATCH 1/2] chore: Deleted mocks file that snuck into repo Signed-off-by: Filip Burlacu --- pkg/didcomm/common/service/mocks/mocks.go | 119 ---------------------- 1 file changed, 119 deletions(-) delete mode 100644 pkg/didcomm/common/service/mocks/mocks.go diff --git a/pkg/didcomm/common/service/mocks/mocks.go b/pkg/didcomm/common/service/mocks/mocks.go deleted file mode 100644 index 9cb165e73..000000000 --- a/pkg/didcomm/common/service/mocks/mocks.go +++ /dev/null @@ -1,119 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service (interfaces: DIDComm) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - gomock "github.com/golang/mock/gomock" - service "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" - reflect "reflect" -) - -// MockDIDComm is a mock of DIDComm interface -type MockDIDComm struct { - ctrl *gomock.Controller - recorder *MockDIDCommMockRecorder -} - -// MockDIDCommMockRecorder is the mock recorder for MockDIDComm -type MockDIDCommMockRecorder struct { - mock *MockDIDComm -} - -// NewMockDIDComm creates a new mock instance -func NewMockDIDComm(ctrl *gomock.Controller) *MockDIDComm { - mock := &MockDIDComm{ctrl: ctrl} - mock.recorder = &MockDIDCommMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockDIDComm) EXPECT() *MockDIDCommMockRecorder { - return m.recorder -} - -// HandleInbound mocks base method -func (m *MockDIDComm) HandleInbound(arg0 *service.DIDCommMsg) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HandleInbound", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// HandleInbound indicates an expected call of HandleInbound -func (mr *MockDIDCommMockRecorder) HandleInbound(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleInbound", reflect.TypeOf((*MockDIDComm)(nil).HandleInbound), arg0) -} - -// HandleOutbound mocks base method -func (m *MockDIDComm) HandleOutbound(arg0 *service.DIDCommMsg, arg1 *service.Destination) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HandleOutbound", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// HandleOutbound indicates an expected call of HandleOutbound -func (mr *MockDIDCommMockRecorder) HandleOutbound(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleOutbound", reflect.TypeOf((*MockDIDComm)(nil).HandleOutbound), arg0, arg1) -} - -// RegisterActionEvent mocks base method -func (m *MockDIDComm) RegisterActionEvent(arg0 chan<- service.DIDCommAction) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RegisterActionEvent", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// RegisterActionEvent indicates an expected call of RegisterActionEvent -func (mr *MockDIDCommMockRecorder) RegisterActionEvent(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterActionEvent", reflect.TypeOf((*MockDIDComm)(nil).RegisterActionEvent), arg0) -} - -// RegisterMsgEvent mocks base method -func (m *MockDIDComm) RegisterMsgEvent(arg0 chan<- service.StateMsg) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RegisterMsgEvent", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// RegisterMsgEvent indicates an expected call of RegisterMsgEvent -func (mr *MockDIDCommMockRecorder) RegisterMsgEvent(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterMsgEvent", reflect.TypeOf((*MockDIDComm)(nil).RegisterMsgEvent), arg0) -} - -// UnregisterActionEvent mocks base method -func (m *MockDIDComm) UnregisterActionEvent(arg0 chan<- service.DIDCommAction) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UnregisterActionEvent", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// UnregisterActionEvent indicates an expected call of UnregisterActionEvent -func (mr *MockDIDCommMockRecorder) UnregisterActionEvent(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterActionEvent", reflect.TypeOf((*MockDIDComm)(nil).UnregisterActionEvent), arg0) -} - -// UnregisterMsgEvent mocks base method -func (m *MockDIDComm) UnregisterMsgEvent(arg0 chan<- service.StateMsg) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UnregisterMsgEvent", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// UnregisterMsgEvent indicates an expected call of UnregisterMsgEvent -func (mr *MockDIDCommMockRecorder) UnregisterMsgEvent(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterMsgEvent", reflect.TypeOf((*MockDIDComm)(nil).UnregisterMsgEvent), arg0) -} From a38a303e030797ba568854dd02508156dfaca40f Mon Sep 17 00:00:00 2001 From: Filip Burlacu Date: Thu, 19 Dec 2019 11:54:45 -0500 Subject: [PATCH 2/2] refactor: Moved generic utility functions out of didexchange - DID doc parsing - Destination struct construction Signed-off-by: Filip Burlacu --- pkg/didcomm/common/service/destination.go | 57 ++++++ .../common/service/destination_test.go | 143 +++++++++++++ pkg/didcomm/common/service/service.go | 8 - pkg/didcomm/protocol/didexchange/service.go | 7 +- .../protocol/didexchange/service_test.go | 48 +---- pkg/didcomm/protocol/didexchange/states.go | 119 ++--------- .../protocol/didexchange/states_test.go | 192 ++---------------- pkg/doc/did/helpers.go | 68 +++++++ pkg/doc/did/helpers_test.go | 96 +++++++++ pkg/internal/mock/diddoc/mock_diddoc.go | 54 +++++ 10 files changed, 464 insertions(+), 328 deletions(-) create mode 100644 pkg/didcomm/common/service/destination.go create mode 100644 pkg/didcomm/common/service/destination_test.go create mode 100644 pkg/doc/did/helpers.go create mode 100644 pkg/doc/did/helpers_test.go create mode 100644 pkg/internal/mock/diddoc/mock_diddoc.go diff --git a/pkg/didcomm/common/service/destination.go b/pkg/didcomm/common/service/destination.go new file mode 100644 index 000000000..67bf07701 --- /dev/null +++ b/pkg/didcomm/common/service/destination.go @@ -0,0 +1,57 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package service + +import ( + "fmt" + + diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did" + "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri" +) + +// Destination provides the recipientKeys, routingKeys, and serviceEndpoint for an outbound message. +// Can be populated from an Invitation or DIDDoc. +type Destination struct { + RecipientKeys []string + ServiceEndpoint string + RoutingKeys []string + TransportReturnRoute string +} + +const ( + didCommServiceType = "did-communication" + ed25519KeyType = "Ed25519VerificationKey2018" +) + +// GetDestination constructs a Destination struct based on the given DID and parameters +// It resolves the DID using the given VDR, and collects relevant data from the resolved DIDDoc. +func GetDestination(did string, vdr vdri.Registry) (*Destination, error) { + didDoc, err := vdr.Resolve(did) + if err != nil { + return nil, err + } + + return CreateDestination(didDoc) +} + +// CreateDestination makes a DIDComm Destination object from a DID Doc +func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) { + didCommService, ok := diddoc.LookupService(didDoc, didCommServiceType) + if !ok { + return nil, fmt.Errorf("create destination: missing DID doc service") + } + + recipientKeys, ok := diddoc.LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + if !ok { + return nil, fmt.Errorf("create destination: missing keys") + } + + return &Destination{ + RecipientKeys: recipientKeys, + ServiceEndpoint: didCommService.ServiceEndpoint, + }, nil +} diff --git a/pkg/didcomm/common/service/destination_test.go b/pkg/didcomm/common/service/destination_test.go new file mode 100644 index 000000000..866fe394f --- /dev/null +++ b/pkg/didcomm/common/service/destination_test.go @@ -0,0 +1,143 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package service + +import ( + "crypto/ed25519" + "crypto/rand" + "errors" + "fmt" + "testing" + "time" + + "github.com/btcsuite/btcutil/base58" + "github.com/stretchr/testify/require" + + "github.com/hyperledger/aries-framework-go/pkg/doc/did" + mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/internal/mock/diddoc" + mockvdri "github.com/hyperledger/aries-framework-go/pkg/internal/mock/vdri" +) + +func TestGetDestinationFromDID(t *testing.T) { + doc := createDIDDoc() + + t.Run("successfully getting destination from public DID", func(t *testing.T) { + vdr := mockvdri.MockVDRIRegistry{ResolveValue: doc} + destination, err := GetDestination(doc.ID, &vdr) + require.NoError(t, err) + require.NotNil(t, destination) + }) + + t.Run("test public key not found", func(t *testing.T) { + doc.PublicKey = nil + vdr := mockvdri.MockVDRIRegistry{ResolveValue: doc} + destination, err := GetDestination(doc.ID, &vdr) + require.Error(t, err) + require.Contains(t, err.Error(), "missing keys") + require.Nil(t, destination) + }) + + t.Run("test service not found", func(t *testing.T) { + doc2 := createDIDDoc() + doc2.Service = nil + vdr := mockvdri.MockVDRIRegistry{ResolveValue: doc2} + destination, err := GetDestination(doc2.ID, &vdr) + require.Error(t, err) + require.Contains(t, err.Error(), "missing DID doc service") + require.Nil(t, destination) + }) + + t.Run("test did document not found", func(t *testing.T) { + vdr := mockvdri.MockVDRIRegistry{ResolveErr: errors.New("resolver error")} + destination, err := GetDestination(doc.ID, &vdr) + require.Error(t, err) + require.Contains(t, err.Error(), "resolver error") + require.Nil(t, destination) + }) +} + +func TestPrepareDestination(t *testing.T) { + ed25519KeyType := "Ed25519VerificationKey2018" + didCommServiceType := "did-communication" + + t.Run("successfully prepared destination", func(t *testing.T) { + dest, err := CreateDestination(mockdiddoc.GetMockDIDDoc()) + require.NoError(t, err) + require.NotNil(t, dest) + require.Equal(t, dest.ServiceEndpoint, "https://localhost:8090") + }) + + t.Run("error while getting service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service = nil + + dest, err := CreateDestination(didDoc) + require.Error(t, err) + require.Contains(t, err.Error(), "missing DID doc service") + require.Nil(t, dest) + }) + + t.Run("error while getting recipient keys from did doc", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service[0].RecipientKeys = []string{} + + recipientKeys, ok := did.LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.False(t, ok) + require.Nil(t, recipientKeys) + }) +} + +func createDIDDoc() *did.Doc { + pubKey, _ := generateKeyPair() + return createDIDDocWithKey(pubKey) +} + +func generateKeyPair() (string, []byte) { + pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + panic(err) + } + + return base58.Encode(pubKey[:]), privKey +} + +func createDIDDocWithKey(pub string) *did.Doc { + const ( + didFormat = "did:%s:%s" + didPKID = "%s#keys-%d" + didServiceID = "%s#endpoint-%d" + method = "test" + ) + + id := fmt.Sprintf(didFormat, method, pub[:16]) + pubKeyID := fmt.Sprintf(didPKID, id, 1) + pubKey := did.PublicKey{ + ID: pubKeyID, + Type: "Ed25519VerificationKey2018", + Controller: id, + Value: []byte(pub), + } + services := []did.Service{ + { + ID: fmt.Sprintf(didServiceID, id, 1), + Type: "did-communication", + ServiceEndpoint: "http://localhost:58416", + Priority: 0, + RecipientKeys: []string{pubKeyID}, + }, + } + createdTime := time.Now() + didDoc := &did.Doc{ + Context: []string{did.Context}, + ID: id, + PublicKey: []did.PublicKey{pubKey}, + Service: services, + Created: &createdTime, + Updated: &createdTime, + } + + return didDoc +} diff --git a/pkg/didcomm/common/service/service.go b/pkg/didcomm/common/service/service.go index b23d0a4be..d3ed34a5d 100644 --- a/pkg/didcomm/common/service/service.go +++ b/pkg/didcomm/common/service/service.go @@ -107,11 +107,3 @@ func (m *DIDCommMsg) ThreadID() (string, error) { return "", ErrThreadIDNotFound } - -// Destination provides the recipientKeys, routingKeys, and serviceEndpoint populated from Invitation -type Destination struct { - RecipientKeys []string - ServiceEndpoint string - RoutingKeys []string - TransportReturnRoute string -} diff --git a/pkg/didcomm/protocol/didexchange/service.go b/pkg/didcomm/protocol/didexchange/service.go index 05cd0d418..5545c2aab 100644 --- a/pkg/didcomm/protocol/didexchange/service.go +++ b/pkg/didcomm/protocol/didexchange/service.go @@ -636,7 +636,12 @@ func (s *Service) CreateImplicitInvitation(inviterLabel, inviterDID, inviteeLabe return "", fmt.Errorf("resolve public did[%s]: %w", inviterDID, err) } - dest, err := prepareDestination(didDoc) + // TODO: hardcoded key type + dest, err := service.CreateDestination(didDoc) + if err != nil { + return "", err + } + thID := generateRandomID() connRecord := &ConnectionRecord{ ConnectionID: generateRandomID(), diff --git a/pkg/didcomm/protocol/didexchange/service_test.go b/pkg/didcomm/protocol/didexchange/service_test.go index 0d2f4f0f7..f43d79ed7 100644 --- a/pkg/didcomm/protocol/didexchange/service_test.go +++ b/pkg/didcomm/protocol/didexchange/service_test.go @@ -14,15 +14,14 @@ import ( "testing" "time" - "github.com/btcsuite/btcutil/base58" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/hyperledger/aries-framework-go/pkg/didcomm/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/doc/did" "github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/protocol" + mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/internal/mock/diddoc" mockstorage "github.com/hyperledger/aries-framework-go/pkg/internal/mock/storage" mockvdri "github.com/hyperledger/aries-framework-go/pkg/internal/mock/vdri" "github.com/hyperledger/aries-framework-go/pkg/storage" @@ -542,47 +541,6 @@ func (m *mockStore) Iterator(start, limit string) storage.StoreIterator { return nil } -func getMockDID() *did.Doc { - return &did.Doc{ - Context: []string{"https://w3id.org/did/v1"}, - ID: "did:peer:123456789abcdefghi#inbox", - Service: []did.Service{ - { - ServiceEndpoint: "https://localhost:8090", - Type: "did-communication", - Priority: 0, - RecipientKeys: []string{"did:example:123456789abcdefghi#keys-2"}, - }, - { - ServiceEndpoint: "https://localhost:8090", - Type: "did-communication", - Priority: 1, - RecipientKeys: []string{"did:example:123456789abcdefghi#keys-1"}, - }, - }, - PublicKey: []did.PublicKey{ - { - ID: "did:example:123456789abcdefghi#keys-1", - Controller: "did:example:123456789abcdefghi", - Type: "Secp256k1VerificationKey2018", - Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), - }, - { - ID: "did:example:123456789abcdefghi#keys-2", - Controller: "did:example:123456789abcdefghi", - Type: "Ed25519VerificationKey2018", - Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), - }, - { - ID: "did:example:123456789abcdefghw#key2", - Controller: "did:example:123456789abcdefghw", - Type: "RsaVerificationKey2018", - Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), - }, - }, - } -} - func randomString() string { u := uuid.New() return u.String() @@ -639,7 +597,7 @@ func TestEventsSuccess(t *testing.T) { } func TestContinueWithPublicDID(t *testing.T) { - didDoc := getMockDID() + didDoc := mockdiddoc.GetMockDIDDoc() svc, err := New(&protocol.MockProvider{}) require.NoError(t, err) @@ -1384,7 +1342,7 @@ func TestFetchConnectionRecord(t *testing.T) { func generateRequestMsgPayload(t *testing.T, prov provider, id, invitationID string) *service.DIDCommMsg { store := mockstorage.NewMockStoreProvider() ctx := context{outboundDispatcher: prov.OutboundDispatcher(), - vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: getMockDID()}, + vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: mockdiddoc.GetMockDIDDoc()}, connectionStore: NewConnectionRecorder(nil, store.Store)} newDidDoc, err := ctx.vdriRegistry.Create(testMethod) require.NoError(t, err) diff --git a/pkg/didcomm/protocol/didexchange/states.go b/pkg/didcomm/protocol/didexchange/states.go index a6f43367d..2ef932353 100644 --- a/pkg/didcomm/protocol/didexchange/states.go +++ b/pkg/didcomm/protocol/didexchange/states.go @@ -308,9 +308,9 @@ func (ctx *context) handleInboundInvitation(invitation *Invitation, } connRec.MyDID = request.Connection.DID - senderVerKeys, err := getRecipientKeys(didDoc) - if err != nil { - return nil, nil, fmt.Errorf("getting sender verification keys: %w", err) + senderVerKeys, ok := did.LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + if !ok { + return nil, nil, fmt.Errorf("getting sender verification keys") } return func() error { @@ -351,14 +351,14 @@ func (ctx *context) handleInboundRequest(request *Request, options *options, con connRec.MyDID = connection.DID connRec.TheirLabel = request.Label - destination, err := prepareDestination(requestDidDoc) + destination, err := service.CreateDestination(requestDidDoc) if err != nil { return nil, nil, err } - senderVerKeys, err := getRecipientKeys(responseDidDoc) - if err != nil { - return nil, nil, err + senderVerKeys, ok := did.LookupRecipientKeys(responseDidDoc, didCommServiceType, ed25519KeyType) + if !ok { + return nil, nil, fmt.Errorf("getting sender verification keys") } // send exchange response @@ -385,7 +385,7 @@ func getLabel(options *options) string { func (ctx *context) getDestination(invitation *Invitation) (*service.Destination, error) { if invitation.DID != "" { - return ctx.getDestinationFromDID(invitation.DID) + return service.GetDestination(invitation.DID, ctx.vdriRegistry) } return &service.Destination{ @@ -432,95 +432,6 @@ func (ctx *context) resolveDidDocFromConnection(conn *Connection) (*did.Doc, err return didDoc, nil } -func (ctx *context) getDestinationFromDID(id string) (*service.Destination, error) { - didDoc, err := ctx.vdriRegistry.Resolve(id) - if err != nil { - return nil, err - } - - return prepareDestination(didDoc) -} - -func getRecipientKeys(didDoc *did.Doc) ([]string, error) { - didCommService, err := getDidCommService(didDoc) - if err != nil { - return nil, err - } - - if len(didCommService.RecipientKeys) == 0 { - return nil, fmt.Errorf("missing recipient keys in did-communication service") - } - - var recipientKeys []string - - for _, keyID := range didCommService.RecipientKeys { - key, err := getPublicKey(keyID, didDoc) - if err != nil { - return nil, err - } - - if isSupportedKeyType(key.Type) { - recipientKeys = append(recipientKeys, string(key.Value)) - } - } - - if len(recipientKeys) == 0 { - return nil, fmt.Errorf("recipient keys in did-communication service not supported") - } - - return recipientKeys, nil -} - -func getPublicKey(id string, didDoc *did.Doc) (*did.PublicKey, error) { - for _, key := range didDoc.PublicKey { - if key.ID == id { - return &key, nil - } - } - - return nil, fmt.Errorf("key not found in DID document: %s", id) -} - -func isSupportedKeyType(keyType string) bool { - return keyType == ed25519KeyType -} - -func getDidCommService(didDoc *did.Doc) (*did.Service, error) { - const notFound = -1 - index := notFound - - for i, s := range didDoc.Service { - if s.Type == didCommServiceType { - if index == notFound || didDoc.Service[index].Priority > s.Priority { - index = i - } - } - } - - if index == notFound { - return nil, fmt.Errorf("service not found in DID document: %s", didCommServiceType) - } - - return &didDoc.Service[index], nil -} - -func prepareDestination(didDoc *did.Doc) (*service.Destination, error) { - didCommService, err := getDidCommService(didDoc) - if err != nil { - return nil, err - } - - recipientKeys, err := getRecipientKeys(didDoc) - if err != nil { - return nil, err - } - - return &service.Destination{ - RecipientKeys: recipientKeys, - ServiceEndpoint: didCommService.ServiceEndpoint, - }, nil -} - // Encode the connection and convert to Connection Signature as per the spec: // https://github.com/hyperledger/aries-rfcs/tree/master/features/0023-did-exchange func (ctx *context) prepareConnectionSignature(connection *Connection, @@ -597,7 +508,7 @@ func (ctx *context) handleInboundResponse(response *Response) (stateAction, *Con return nil, nil, fmt.Errorf("resolve did doc from exchange response connection: %w", err) } - destination, err := prepareDestination(responseDidDoc) + destination, err := service.CreateDestination(responseDidDoc) if err != nil { return nil, nil, fmt.Errorf("prepare destination from response did doc: %w", err) } @@ -607,9 +518,9 @@ func (ctx *context) handleInboundResponse(response *Response) (stateAction, *Con return nil, nil, fmt.Errorf("fetching did document: %w", err) } - senderVerKeys, err := getRecipientKeys(myDidDoc) - if err != nil { - return nil, nil, fmt.Errorf("get public keys: %w", err) + senderVerKeys, ok := did.LookupRecipientKeys(myDidDoc, didCommServiceType, ed25519KeyType) + if !ok { + return nil, nil, fmt.Errorf("getting sender verification keys") } return func() error { @@ -672,9 +583,9 @@ func (ctx *context) getInvitationRecipientKey(invitation *Invitation) (string, e return "", fmt.Errorf("get invitation recipient key: %w", err) } - recipientKeys, err := getRecipientKeys(didDoc) - if err != nil { - return "", fmt.Errorf("get recipient keys from did: %w", err) + recipientKeys, ok := did.LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + if !ok { + return "", fmt.Errorf("get recipient keys from did") } return recipientKeys[0], nil diff --git a/pkg/didcomm/protocol/didexchange/states_test.go b/pkg/didcomm/protocol/didexchange/states_test.go index 7adad15ae..118ffeae9 100644 --- a/pkg/didcomm/protocol/didexchange/states_test.go +++ b/pkg/didcomm/protocol/didexchange/states_test.go @@ -26,6 +26,7 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did" "github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm/protocol" + mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/internal/mock/diddoc" mockstorage "github.com/hyperledger/aries-framework-go/pkg/internal/mock/storage" mockvdri "github.com/hyperledger/aries-framework-go/pkg/internal/mock/vdri" "github.com/hyperledger/aries-framework-go/pkg/storage" @@ -303,7 +304,7 @@ func TestRequestedState_Execute(t *testing.T) { ThreadID: "test", ConnectionID: "123", } - didDoc := getMockDID() + didDoc := mockdiddoc.GetMockDIDDoc() didDoc.Service[0].RecipientKeys = []string{"invalid"} ctx2 := &context{outboundDispatcher: prov.OutboundDispatcher(), vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: didDoc}, @@ -314,7 +315,7 @@ func TestRequestedState_Execute(t *testing.T) { connRecord: connRec, }, "", ctx2) require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document") + require.Contains(t, err.Error(), "getting sender verification keys") require.Nil(t, followup) }) } @@ -369,7 +370,7 @@ func TestRespondedState_Execute(t *testing.T) { require.Equal(t, (&completed{}).Name(), followup.Name()) }) t.Run("handle inbound request public key error", func(t *testing.T) { - didDoc := getMockDID() + didDoc := mockdiddoc.GetMockDIDDoc() didDoc.Service[0].RecipientKeys = []string{"invalid"} ctx2 := &context{outboundDispatcher: prov.OutboundDispatcher(), vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: didDoc}, signer: &mockSigner{}, @@ -380,7 +381,7 @@ func TestRespondedState_Execute(t *testing.T) { connRecord: &ConnectionRecord{}, }, "", ctx2) require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document") + require.Contains(t, err.Error(), "getting sender verification keys") require.Nil(t, followup) }) t.Run("handle inbound request unmarshalling error", func(t *testing.T) { @@ -441,7 +442,7 @@ func TestCompletedState_Execute(t *testing.T) { } err = ctx.connectionStore.saveNewConnectionRecord(connRec) require.NoError(t, err) - ctx.vdriRegistry = &mockvdri.MockVDRIRegistry{ResolveValue: getMockDID()} + ctx.vdriRegistry = &mockvdri.MockVDRIRegistry{ResolveValue: mockdiddoc.GetMockDIDDoc()} require.NoError(t, err) _, followup, _, e := (&completed{}).ExecuteInbound(&stateMachineMsg{ header: &service.Header{Type: ResponseMsgType}, @@ -672,7 +673,7 @@ func TestPrepareConnectionSignature(t *testing.T) { } connectionSignature, err := ctx2.prepareConnectionSignature(connection, newDidDoc.ID) require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document") + require.Contains(t, err.Error(), "get recipient keys from did") require.Nil(t, connectionSignature) }) t.Run("prepare connection signature get invitation", func(t *testing.T) { @@ -698,7 +699,7 @@ func TestPrepareConnectionSignature(t *testing.T) { ctx := &context{signer: &mockSigner{err: errors.New("sign error")}, connectionStore: NewConnectionRecorder(nil, store)} connection := &Connection{ - DIDDoc: getMockDID(), + DIDDoc: mockdiddoc.GetMockDIDDoc(), } connectionSignature, err := ctx.prepareConnectionSignature(connection, invitation.ID) require.Error(t, err) @@ -707,35 +708,6 @@ func TestPrepareConnectionSignature(t *testing.T) { }) } -func TestPrepareDestination(t *testing.T) { - t.Run("successfully prepared destination", func(t *testing.T) { - dest, err := prepareDestination(getMockDID()) - require.NoError(t, err) - require.NotNil(t, dest) - require.Equal(t, dest.ServiceEndpoint, "https://localhost:8090") - }) - - t.Run("error while getting service", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service = nil - - dest, err := prepareDestination(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "service not found in DID document: did-communication") - require.Nil(t, dest) - }) - - t.Run("error while getting recipient keys from did doc", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service[0].RecipientKeys = []string{} - - recipientKeys, err := getRecipientKeys(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "missing recipient keys in did-communication service") - require.Nil(t, recipientKeys) - }) -} - func TestNewRequestFromInvitation(t *testing.T) { invitation := &Invitation{ Type: InvitationMsgType, @@ -807,9 +779,12 @@ func TestNewResponseFromRequest(t *testing.T) { require.NotNil(t, connRec.TheirDID) }) t.Run("unsuccessful new response from request due to create did error", func(t *testing.T) { - didDoc := getMockDID() + didDoc := mockdiddoc.GetMockDIDDoc() ctx := &context{ - vdriRegistry: &mockvdri.MockVDRIRegistry{CreateErr: fmt.Errorf("create DID error"), ResolveValue: getMockDID()}} + vdriRegistry: &mockvdri.MockVDRIRegistry{ + CreateErr: fmt.Errorf("create DID error"), + ResolveValue: mockdiddoc.GetMockDIDDoc(), + }} request := &Request{Connection: &Connection{DID: didDoc.ID, DIDDoc: didDoc}} _, connRec, err := ctx.handleInboundRequest(request, &options{}, &ConnectionRecord{}) require.Error(t, err) @@ -817,7 +792,7 @@ func TestNewResponseFromRequest(t *testing.T) { require.Nil(t, connRec) }) t.Run("unsuccessful new response from request due to sign error", func(t *testing.T) { - ctx := &context{vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: getMockDID()}, + ctx := &context{vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: mockdiddoc.GetMockDIDDoc()}, signer: &mockSigner{err: errors.New("sign error")}, connectionStore: NewConnectionRecorder(nil, store)} request, err := createRequest(ctx) @@ -887,7 +862,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 := getMockDID() + doc := mockdiddoc.GetMockDIDDoc() ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveValue: doc}} invitation := &Invitation{ Type: InvitationMsgType, @@ -916,8 +891,8 @@ func TestGetPublicKey(t *testing.T) { ctx := getContext(prov, nil) newDidDoc, err := ctx.vdriRegistry.Create(testMethod) require.NoError(t, err) - pubkey, err := getPublicKey(newDidDoc.PublicKey[0].ID, newDidDoc) - require.NoError(t, err) + pubkey, ok := diddoc.LookupPublicKey(newDidDoc.PublicKey[0].ID, newDidDoc) + require.True(t, ok) require.NotNil(t, pubkey) }) t.Run("failed to get public key", func(t *testing.T) { @@ -925,95 +900,12 @@ func TestGetPublicKey(t *testing.T) { ctx := getContext(prov, nil) newDidDoc, err := ctx.vdriRegistry.Create(testMethod) require.NoError(t, err) - pubkey, err := getPublicKey("invalid-key", newDidDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document: invalid-key") + pubkey, ok := diddoc.LookupPublicKey("invalid-key", newDidDoc) + require.False(t, ok) require.Nil(t, pubkey) }) } -func TestGetDidCommService(t *testing.T) { - t.Run("successfully getting did-communication service", func(t *testing.T) { - didDoc := getMockDID() - - s, err := getDidCommService(didDoc) - require.NoError(t, err) - require.Equal(t, "did-communication", s.Type) - require.Equal(t, uint(0), s.Priority) - }) - - t.Run("error due to missing service", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service = nil - - s, err := getDidCommService(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "service not found in DID document: did-communication") - require.Nil(t, s) - }) - - t.Run("error due to missing did-communication service", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service[0].Type = "some-type" - didDoc.Service[1].Type = "other-type" - - s, err := getDidCommService(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "service not found in DID document: did-communication") - require.Nil(t, s) - }) -} - -func TestGetRecipientKeys(t *testing.T) { - t.Run("successfully getting recipient keys", func(t *testing.T) { - didDoc := getMockDID() - - recipientKeys, err := getRecipientKeys(didDoc) - require.NoError(t, err) - require.Equal(t, 1, len(recipientKeys)) - }) - - t.Run("error due to missing did-communication service", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service = nil - - recipientKeys, err := getRecipientKeys(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "service not found in DID document: did-communication") - require.Nil(t, recipientKeys) - }) - - t.Run("error due to missing recipient keys in did-communication service", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service[0].RecipientKeys = []string{} - - recipientKeys, err := getRecipientKeys(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "missing recipient keys in did-communication service") - require.Nil(t, recipientKeys) - }) - - t.Run("error due to missing public key in did doc", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service[0].RecipientKeys = []string{"invalid"} - - recipientKeys, err := getRecipientKeys(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document: invalid") - require.Nil(t, recipientKeys) - }) - - t.Run("error due to unsupported key types", func(t *testing.T) { - didDoc := getMockDID() - didDoc.Service[0].RecipientKeys = []string{didDoc.PublicKey[0].ID} - - recipientKeys, err := getRecipientKeys(didDoc) - require.Error(t, err) - require.Contains(t, err.Error(), "recipient keys in did-communication service not supported") - require.Nil(t, recipientKeys) - }) -} - func TestGetDIDDocAndConnection(t *testing.T) { t.Run("successfully getting did doc and connection for public did", func(t *testing.T) { doc := createDIDDoc() @@ -1041,7 +933,9 @@ func TestGetDIDDocAndConnection(t *testing.T) { require.Nil(t, conn) }) t.Run("successfully created peer did", func(t *testing.T) { - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: getMockDID()}} + ctx := context{ + vdriRegistry: &mockvdri.MockVDRIRegistry{CreateValue: mockdiddoc.GetMockDIDDoc()}, + } didDoc, conn, err := ctx.getDIDDocAndConnection("") require.NoError(t, err) require.NotNil(t, didDoc) @@ -1050,48 +944,6 @@ func TestGetDIDDocAndConnection(t *testing.T) { }) } -func TestGetDestinationFromDID(t *testing.T) { - doc := createDIDDoc() - - t.Run("successfully getting destination from public DID", func(t *testing.T) { - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveValue: doc}} - destination, err := ctx.getDestinationFromDID(doc.ID) - require.NoError(t, err) - require.NotNil(t, destination) - }) - t.Run("test public key not found", func(t *testing.T) { - doc.PublicKey = nil - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveValue: doc}} - destination, err := ctx.getDestinationFromDID(doc.ID) - require.Error(t, err) - require.Contains(t, err.Error(), "key not found in DID document") - require.Nil(t, destination) - }) - t.Run("test service not found", func(t *testing.T) { - doc2 := createDIDDoc() - doc2.Service = nil - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveValue: doc2}} - destination, err := ctx.getDestinationFromDID(doc2.ID) - require.Error(t, err) - require.Contains(t, err.Error(), "service not found in DID document: did-communication") - require.Nil(t, destination) - }) - t.Run("get destination by invitation", func(t *testing.T) { - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveValue: createDIDDoc()}} - invitation := &Invitation{DID: "test"} - destination, err := ctx.getDestination(invitation) - require.NoError(t, err) - require.NotNil(t, destination) - }) - t.Run("test did document not found", func(t *testing.T) { - ctx := context{vdriRegistry: &mockvdri.MockVDRIRegistry{ResolveErr: errors.New("resolver error")}} - destination, err := ctx.getDestinationFromDID(doc.ID) - require.Error(t, err) - require.Contains(t, err.Error(), "resolver error") - require.Nil(t, destination) - }) -} - type mockSigner struct { privateKey []byte err error diff --git a/pkg/doc/did/helpers.go b/pkg/doc/did/helpers.go new file mode 100644 index 000000000..0f67b49f6 --- /dev/null +++ b/pkg/doc/did/helpers.go @@ -0,0 +1,68 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package did + +// LookupService returns the service from the given DIDDoc matching the given service type. +func LookupService(didDoc *Doc, serviceType string) (*Service, bool) { + const notFound = -1 + index := notFound + + for i, s := range didDoc.Service { + if s.Type == serviceType { + if index == notFound || didDoc.Service[index].Priority > s.Priority { + index = i + } + } + } + + if index == notFound { + return nil, false + } + + return &didDoc.Service[index], true +} + +// LookupRecipientKeys gets the recipient keys from the did doc which match the given parameters. +func LookupRecipientKeys(didDoc *Doc, serviceType, keyType string) ([]string, bool) { + didCommService, ok := LookupService(didDoc, serviceType) + if !ok { + return nil, false + } + + if len(didCommService.RecipientKeys) == 0 { + return nil, false + } + + var recipientKeys []string + + for _, keyID := range didCommService.RecipientKeys { + key, ok := LookupPublicKey(keyID, didDoc) + if !ok { + return nil, false + } + + if key.Type == keyType { + recipientKeys = append(recipientKeys, string(key.Value)) + } + } + + if len(recipientKeys) == 0 { + return nil, false + } + + return recipientKeys, true +} + +// LookupPublicKey returns the public key with the given id from the given DID Doc +func LookupPublicKey(id string, didDoc *Doc) (*PublicKey, bool) { + for _, key := range didDoc.PublicKey { + if key.ID == id { + return &key, true + } + } + + return nil, false +} diff --git a/pkg/doc/did/helpers_test.go b/pkg/doc/did/helpers_test.go new file mode 100644 index 000000000..5209c5bc0 --- /dev/null +++ b/pkg/doc/did/helpers_test.go @@ -0,0 +1,96 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package did_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + . "github.com/hyperledger/aries-framework-go/pkg/doc/did" + mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/internal/mock/diddoc" +) + +func TestGetRecipientKeys(t *testing.T) { + ed25519KeyType := "Ed25519VerificationKey2018" + didCommServiceType := "did-communication" + + t.Run("successfully getting recipient keys", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + + recipientKeys, ok := LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.True(t, ok) + require.Equal(t, 1, len(recipientKeys)) + }) + + t.Run("error due to missing did-communication service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service = nil + + recipientKeys, ok := LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.False(t, ok) + require.Nil(t, recipientKeys) + }) + + t.Run("error due to missing recipient keys in did-communication service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service[0].RecipientKeys = []string{} + + recipientKeys, ok := LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.False(t, ok) + require.Nil(t, recipientKeys) + }) + + t.Run("error due to missing public key in did doc", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service[0].RecipientKeys = []string{"invalid"} + + recipientKeys, ok := LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.False(t, ok) + require.Nil(t, recipientKeys) + }) + + t.Run("error due to unsupported key types", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service[0].RecipientKeys = []string{didDoc.PublicKey[0].ID} + + recipientKeys, ok := LookupRecipientKeys(didDoc, didCommServiceType, ed25519KeyType) + require.False(t, ok) + require.Nil(t, recipientKeys) + }) +} + +func TestGetDidCommService(t *testing.T) { + didCommServiceType := "did-communication" + + t.Run("successfully getting did-communication service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + + s, ok := LookupService(didDoc, didCommServiceType) + require.True(t, ok) + require.Equal(t, "did-communication", s.Type) + require.Equal(t, uint(0), s.Priority) + }) + + t.Run("error due to missing service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service = nil + + s, ok := LookupService(didDoc, didCommServiceType) + require.False(t, ok) + require.Nil(t, s) + }) + + t.Run("error due to missing did-communication service", func(t *testing.T) { + didDoc := mockdiddoc.GetMockDIDDoc() + didDoc.Service[0].Type = "some-type" + didDoc.Service[1].Type = "other-type" + + s, ok := LookupService(didDoc, didCommServiceType) + require.False(t, ok) + require.Nil(t, s) + }) +} diff --git a/pkg/internal/mock/diddoc/mock_diddoc.go b/pkg/internal/mock/diddoc/mock_diddoc.go new file mode 100644 index 000000000..3a82ab807 --- /dev/null +++ b/pkg/internal/mock/diddoc/mock_diddoc.go @@ -0,0 +1,54 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package mockdiddoc + +import ( + "github.com/btcsuite/btcutil/base58" + + "github.com/hyperledger/aries-framework-go/pkg/doc/did" +) + +// GetMockDIDDoc creates a mock DID Doc for testing. +func GetMockDIDDoc() *did.Doc { + return &did.Doc{ + Context: []string{"https://w3id.org/did/v1"}, + ID: "did:peer:123456789abcdefghi#inbox", + Service: []did.Service{ + { + ServiceEndpoint: "https://localhost:8090", + Type: "did-communication", + Priority: 0, + RecipientKeys: []string{"did:example:123456789abcdefghi#keys-2"}, + }, + { + ServiceEndpoint: "https://localhost:8090", + Type: "did-communication", + Priority: 1, + RecipientKeys: []string{"did:example:123456789abcdefghi#keys-1"}, + }, + }, + PublicKey: []did.PublicKey{ + { + ID: "did:example:123456789abcdefghi#keys-1", + Controller: "did:example:123456789abcdefghi", + Type: "Secp256k1VerificationKey2018", + Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), + }, + { + ID: "did:example:123456789abcdefghi#keys-2", + Controller: "did:example:123456789abcdefghi", + Type: "Ed25519VerificationKey2018", + Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), + }, + { + ID: "did:example:123456789abcdefghw#key2", + Controller: "did:example:123456789abcdefghw", + Type: "RsaVerificationKey2018", + Value: base58.Decode("H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"), + }, + }, + } +}