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

Commit

Permalink
Merge pull request #938 from Moopli/725
Browse files Browse the repository at this point in the history
feat: Protocols can use DIDs to identify the agents they communicate with
  • Loading branch information
fqutishat authored Dec 20, 2019
2 parents 1674d60 + 5d321d4 commit 543b0b9
Show file tree
Hide file tree
Showing 42 changed files with 1,107 additions and 337 deletions.
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
ToVerKeys []string
// ToVerKey holds the key that was used to decrypt an inbound message
ToVerKey []byte
FromDID string
ToDID string
}
Loading

0 comments on commit 543b0b9

Please sign in to comment.