Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

feat: Protocols can use DIDs to identify the agents they communicate with #938

Merged
merged 2 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkg/client/didexchange/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/google/uuid"

"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/didconnection"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
"github.com/hyperledger/aries-framework-go/pkg/kms"
Expand Down Expand Up @@ -40,6 +41,7 @@ type provider interface {
InboundTransportEndpoint() string
StorageProvider() storage.Provider
TransientStorageProvider() storage.Provider
DIDConnectionStore() didconnection.Store
}

// Client enable access to didexchange api
Expand Down Expand Up @@ -94,7 +96,7 @@ func New(ctx provider) (*Client, error) {
didexchangeSvc: didexchangeSvc,
kms: ctx.KMS(),
inboundTransportEndpoint: ctx.InboundTransportEndpoint(),
connectionStore: didexchange.NewConnectionRecorder(transientStore, store),
connectionStore: didexchange.NewConnectionRecorder(transientStore, store, ctx.DIDConnectionStore()),
}, nil
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/client/didexchange/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func TestClient_QueryConnectionByID(t *testing.T) {
require.NoError(t, err)
require.NoError(t, err)
require.NoError(t, transientStore.Put("conn_id1", connBytes))
c := didexchange.NewConnectionRecorder(transientStore, store)
c := didexchange.NewConnectionRecorder(transientStore, store, nil)
result, err := c.GetConnectionRecord(connID)
require.NoError(t, err)
require.Equal(t, "complete", result.State)
Expand All @@ -219,7 +219,7 @@ func TestClient_QueryConnectionByID(t *testing.T) {
connRec := &didexchange.ConnectionRecord{ConnectionID: connID, ThreadID: threadID, State: "complete"}
connBytes, err := json.Marshal(connRec)
require.NoError(t, err)
c := didexchange.NewConnectionRecorder(transientStore, store)
c := didexchange.NewConnectionRecorder(transientStore, store, nil)
require.NoError(t, transientStore.Put("conn_id1", connBytes))
_, err = c.GetConnectionRecord(connID)
require.Error(t, err)
Expand All @@ -234,7 +234,7 @@ func TestClient_QueryConnectionByID(t *testing.T) {
transientStore := mockstore.MockStore{ErrGet: storage.ErrDataNotFound}
store := mockstore.MockStore{}
require.NoError(t, err)
c := didexchange.NewConnectionRecorder(&transientStore, &store)
c := didexchange.NewConnectionRecorder(&transientStore, &store, nil)
_, err = c.GetConnectionRecord(connID)
require.Error(t, err)
require.True(t, errors.Is(err, storage.ErrDataNotFound))
Expand Down
27 changes: 27 additions & 0 deletions pkg/didcomm/common/didconnection/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"errors"

diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did"
)

// Store stores DIDs indexed by public key, so agents can find the DID associated with a given key.
type Store interface {
// SaveDID saves a DID indexed by the given public keys to the Store
SaveDID(did string, keys ...string) error
// GetDID gets the DID stored under the given key
GetDID(key string) (string, error)
// SaveDIDByResolving resolves a DID using the VDR then saves the map from keys -> did
SaveDIDByResolving(did string, keys ...string) error
// SaveDIDFromDoc saves a map from keys -> did for a did doc
SaveDIDFromDoc(doc *diddoc.Doc) error
}

// ErrNotFound signals that the entry for the given DID and key is not present in the store.
var ErrNotFound = errors.New("did not found under given key")
110 changes: 110 additions & 0 deletions pkg/didcomm/common/didconnection/didconnection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"encoding/json"
"errors"
"fmt"

diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri"
"github.com/hyperledger/aries-framework-go/pkg/storage"
)

// ConnectionStore stores DIDs indexed by key
type ConnectionStore struct {
store storage.Store
vdr vdri.Registry
}

type didRecord struct {
DID string `json:"did,omitempty"`
}

type provider interface {
StorageProvider() storage.Provider
VDRIRegistry() vdri.Registry
}

// New returns a new did lookup Store
func New(ctx provider) (*ConnectionStore, error) {
store, err := ctx.StorageProvider().OpenStore("didconnection")
if err != nil {
return nil, err
}

return &ConnectionStore{store: store, vdr: ctx.VDRIRegistry()}, nil
}

// saveDID saves a DID, indexed using the given public key
func (c *ConnectionStore) saveDID(did, key string) error {
data := didRecord{
DID: did,
}

bytes, err := json.Marshal(data)
if err != nil {
return err
}

return c.store.Put(key, bytes)
}

// SaveDID saves a DID, indexed using the given public keys
func (c *ConnectionStore) SaveDID(did string, keys ...string) error {
for _, key := range keys {
err := c.saveDID(did, key)
if err != nil {
return fmt.Errorf("saving DID in did map: %w", err)
}
}

return nil
}

// SaveDIDFromDoc saves a map from a did doc's keys to the did
func (c *ConnectionStore) SaveDIDFromDoc(doc *diddoc.Doc) error {
var keys []string
for i := range doc.PublicKey {
keys = append(keys, string(doc.PublicKey[i].Value))
}

return c.SaveDID(doc.ID, keys...)
}

// SaveDIDByResolving resolves a DID using the VDR then saves the map from keys -> did
// keys: fallback keys in case the DID can't be resolved
func (c *ConnectionStore) SaveDIDByResolving(did string, keys ...string) error {
doc, err := c.vdr.Resolve(did)
if errors.Is(err, vdri.ErrNotFound) {
return c.SaveDID(did, keys...)
} else if err != nil {
return err
}

return c.SaveDIDFromDoc(doc)
}

// GetDID gets the DID stored under the given key
func (c *ConnectionStore) GetDID(key string) (string, error) {
bytes, err := c.store.Get(key)
if errors.Is(err, storage.ErrDataNotFound) {
return "", ErrNotFound
} else if err != nil {
return "", err
}

var record didRecord

err = json.Unmarshal(bytes, &record)
if err != nil {
return "", err
}

return record.DID, nil
}
129 changes: 129 additions & 0 deletions pkg/didcomm/common/didconnection/didconnection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

vdriapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdri"
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"
)

type ctx struct {
store storage.Provider
vdr vdriapi.Registry
}

func (c *ctx) StorageProvider() storage.Provider {
return c.store
}

func (c *ctx) VDRIRegistry() vdriapi.Registry {
return c.vdr
}

func TestBaseConnectionStore(t *testing.T) {
prov := ctx{
store: mockstorage.NewMockStoreProvider(),
vdr: &mockvdri.MockVDRIRegistry{
CreateValue: mockdiddoc.GetMockDIDDoc(),
ResolveValue: mockdiddoc.GetMockDIDDoc(),
},
}

t.Run("New", func(t *testing.T) {
_, err := New(&prov)
require.NoError(t, err)

_, err = New(&ctx{
store: &mockstorage.MockStoreProvider{
ErrOpenStoreHandle: fmt.Errorf("store error"),
},
})
require.Error(t, err)
require.Contains(t, err.Error(), "store error")
})

t.Run("SaveDID error", func(t *testing.T) {
cs, err := New(&ctx{
store: &mockstorage.MockStoreProvider{
Store: &mockstorage.MockStore{
Store: map[string][]byte{},
ErrPut: fmt.Errorf("put error"),
},
},
vdr: &mockvdri.MockVDRIRegistry{
CreateValue: mockdiddoc.GetMockDIDDoc(),
ResolveValue: mockdiddoc.GetMockDIDDoc(),
},
})
require.NoError(t, err)

err = cs.SaveDID("did", "key")
require.Error(t, err)
require.Contains(t, err.Error(), "put error")
})

t.Run("SaveDID + GetDID", func(t *testing.T) {
connStore, err := New(&prov)
require.NoError(t, err)

err = connStore.SaveDID("did:abcde", "abcde")
require.NoError(t, err)

didVal, err := connStore.GetDID("abcde")
require.NoError(t, err)
require.Equal(t, "did:abcde", didVal)

wrong, err := connStore.GetDID("fhtagn")
require.EqualError(t, err, ErrNotFound.Error())
require.Equal(t, "", wrong)

err = connStore.store.Put("bad-data", []byte("aaooga"))
require.NoError(t, err)

_, err = connStore.GetDID("bad-data")
require.Error(t, err)
require.Contains(t, err.Error(), "invalid character")
})

t.Run("SaveDIDFromDoc", func(t *testing.T) {
connStore, err := New(&prov)
require.NoError(t, err)

err = connStore.SaveDIDFromDoc(mockdiddoc.GetMockDIDDoc())
require.NoError(t, err)
})

t.Run("SaveDIDByResolving success", func(t *testing.T) {
cs, err := New(&prov)
require.NoError(t, err)

err = cs.SaveDIDByResolving(mockdiddoc.GetMockDIDDoc().ID)
require.NoError(t, err)
})

t.Run("SaveDIDByResolving error", func(t *testing.T) {
prov := ctx{
store: mockstorage.NewMockStoreProvider(),
vdr: &mockvdri.MockVDRIRegistry{ResolveErr: fmt.Errorf("resolve error")},
}

cs, err := New(&prov)
require.NoError(t, err)

err = cs.SaveDIDByResolving("did")
require.Error(t, err)
require.Contains(t, err.Error(), "resolve error")
})
}
3 changes: 2 additions & 1 deletion pkg/didcomm/common/service/destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ type Destination struct {

const (
didCommServiceType = "did-communication"
ed25519KeyType = "Ed25519VerificationKey2018"
// TODO: hardcoded key type https://github.com/hyperledger/aries-framework-go/issues/1008
ed25519KeyType = "Ed25519VerificationKey2018"
)

// GetDestination constructs a Destination struct based on the given DID and parameters
Expand Down
11 changes: 8 additions & 3 deletions pkg/didcomm/common/transport/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ SPDX-License-Identifier: Apache-2.0

package transport

// Envelope contain msg, FromVerKey and ToVerKeys
// Envelope holds message data and metadata for inbound and outbound messaging
type Envelope struct {
Message []byte
FromVerKey string
ToVerKeys []string
FromVerKey []byte
// ToVerKeys stores string (base58) verification keys for an outbound message
troyronda marked this conversation as resolved.
Show resolved Hide resolved
ToVerKeys []string
// ToVerKey holds the key that was used to decrypt an inbound message
ToVerKey []byte
FromDID string
ToDID string
}
Loading