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

Commit

Permalink
fix: fixes for didexchange interop with aca-py
Browse files Browse the repository at this point in the history
accept old and new didex. message formats and mime types:
- application/ssi-agent-wire is accepted as an inbound mime type
- did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0 is accepted as
  an inbound PIURI for didexchange messages
- did doc can be parsed from "did_doc~attach" attachment in request
  messages, as per didexchange spec, in addition to being accepted
  inside "connection" field, as per connection-request spec

did doc format:
- Parse legacy peer did docs with publicKey members even if their
  context states they are using a newer format.
- Translate raw ed25519 public key values to did:key when reading
  recipient keys or routing keys

Signed-off-by: Filip Burlacu <[email protected]>
  • Loading branch information
Filip Burlacu committed Mar 22, 2021
1 parent e6ebe2c commit 92a29a8
Show file tree
Hide file tree
Showing 13 changed files with 528 additions and 40 deletions.
4 changes: 2 additions & 2 deletions cmd/aries-agent-rest/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ require (
github.com/gorilla/mux v1.7.3
github.com/hyperledger/aries-framework-go v0.1.6-0.20210304193329-f56b2cebc386
github.com/hyperledger/aries-framework-go/component/storage/leveldb v0.0.0-20210305152013-b276ca413681
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210310001230-bc1bd8ea889c
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310001230-bc1bd8ea889c
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210320144851-40976de98ccf
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210320144851-40976de98ccf
github.com/rs/cors v1.7.0
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 // indirect
Expand Down
6 changes: 6 additions & 0 deletions cmd/aries-agent-rest/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-202102242
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210224230531-58e1368e5661/go.mod h1:XaPVDJcbQT8BKmThfQdWPc+hgicHFAQzSOavHw2gn/4=
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210310001230-bc1bd8ea889c h1:ELa1dI2zdRegFsaPygDgho+1DKIVUAs0KGnkw7ab304=
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210310001230-bc1bd8ea889c/go.mod h1:zOolL2VqWj6+SPe13nrK0oo5QUOfQZQKB0j1iQsje0k=
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210320144851-40976de98ccf h1:rh+NCbTrYfJ9ho4NRVdgLuIVF05vuklmnF2JWP1+aFo=
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210320144851-40976de98ccf/go.mod h1:HVV8sifdHIyLkrlgmK/6+3YWKnOJPUfoNU+4SwQqMSs=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210205153949-f852f978a0d6/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210210184327-0b9d0fd4c34e/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210224230531-58e1368e5661/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
Expand All @@ -221,10 +223,14 @@ github.com/hyperledger/aries-framework-go/spi v0.0.0-20210305152013-b276ca413681
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210305152013-b276ca413681/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310001230-bc1bd8ea889c h1:PzNaY9LS6oP1edqx8CWUf8FifpWb56PGedqf/tAwR3Q=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310001230-bc1bd8ea889c/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310160016-d5eea2ecdd50/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210320144851-40976de98ccf h1:5xKqwVy1gEBENyU7a+KCeDOOtF21wrYtHsEicuXWE/c=
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210320144851-40976de98ccf/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY=
github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210219073333-c46e84ce678f/go.mod h1:/ljIFCu5iDIziwuvObF0vEc3fJ5dgDpT8RYAhQdNeHI=
github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210226235232-298aa129d822/go.mod h1:6Za6hvu+eZDPerePXIlMuBWbQZDKqTgOrKV56WZMtcI=
github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210304193329-f56b2cebc386 h1:LLJg+gSy+yPGtdYQopGMI4/C4LA9ZTFkomA4c83q0Ow=
github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210304193329-f56b2cebc386/go.mod h1:6Za6hvu+eZDPerePXIlMuBWbQZDKqTgOrKV56WZMtcI=
github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210310160016-d5eea2ecdd50/go.mod h1:AybsT4/saiuxdVhK5CgOLIkcNMPZtX3GAUMOjHcLLjk=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
65 changes: 64 additions & 1 deletion pkg/didcomm/common/service/destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ package service
import (
"fmt"

"github.com/btcsuite/btcutil/base58"

diddoc "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"
)

// Destination provides the recipientKeys, routingKeys, and serviceEndpoint for an outbound message.
Expand All @@ -23,6 +26,8 @@ type Destination struct {

const (
didCommServiceType = "did-communication"
// legacyDIDCommServiceType is the non-spec service type used by legacy didcomm agent systems.
legacyDIDCommServiceType = "IndyAgent"
)

// GetDestination constructs a Destination struct based on the given DID and parameters
Expand All @@ -41,7 +46,7 @@ func GetDestination(did string, vdr vdrapi.Registry) (*Destination, error) {
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")
return createDestinationFromIndy(didDoc)
}

if didCommService.ServiceEndpoint == "" {
Expand All @@ -61,3 +66,61 @@ func CreateDestination(didDoc *diddoc.Doc) (*Destination, error) {
RoutingKeys: didCommService.RoutingKeys,
}, nil
}

func createDestinationFromIndy(didDoc *diddoc.Doc) (*Destination, error) {
didCommService, ok := diddoc.LookupService(didDoc, legacyDIDCommServiceType)
if !ok {
return nil, fmt.Errorf("create destination: missing DID doc service")
}

if didCommService.ServiceEndpoint == "" {
return nil, fmt.Errorf("create destination: no service endpoint on didcomm service block in diddoc: %+v", didDoc)
}

if len(didCommService.RecipientKeys) == 0 {
return nil, fmt.Errorf("create destination: no recipient keys on didcomm service block in diddoc: %+v", didDoc)
}

// TODO ensure recipient keys are did:key's
// https://github.com/hyperledger/aries-framework-go/issues/1604

// convert plain base58 keys to did:key
recKeys := lookupIndyRecipientKeys(didDoc, didCommService.RecipientKeys)
routeKeys := lookupIndyRecipientKeys(didDoc, didCommService.RoutingKeys)

return &Destination{
RecipientKeys: recKeys,
ServiceEndpoint: didCommService.ServiceEndpoint,
RoutingKeys: routeKeys,
}, nil
}

func lookupIndyRecipientKeys(didDoc *diddoc.Doc, recipientKeys []string) []string {
b58VMkeys := map[string]int{}

for i, vm := range didDoc.VerificationMethod {
b58Key := base58.Encode(vm.Value)
b58VMkeys[b58Key] = i
}

var didKeys []string

for _, key := range recipientKeys {
vmIdx, ok := b58VMkeys[key]
if !ok {
continue
}

vm := didDoc.VerificationMethod[vmIdx]
if vm.Type != "Ed25519VerificationKey2018" {
// TODO: handle further key types
continue
}

didKey, _ := fingerprint.CreateDIDKey(vm.Value)

didKeys = append(didKeys, didKey)
}

return didKeys
}
86 changes: 86 additions & 0 deletions pkg/didcomm/common/service/destination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/mock/diddoc"
mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
"github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint"
)

func TestGetDestinationFromDID(t *testing.T) {
Expand Down Expand Up @@ -110,6 +111,91 @@ func TestPrepareDestination(t *testing.T) {
})
}

func TestCreateDestinationFromLegacyDoc(t *testing.T) {
t.Run("successfully prepared destination", func(t *testing.T) {
doc := mockdiddoc.GetMockIndyDoc(t)
dest, err := CreateDestination(doc)
require.NoError(t, err)
require.NotNil(t, dest)
require.Equal(t, dest.ServiceEndpoint, "https://localhost:8090")
require.Equal(t, doc.Service[0].RoutingKeys, dest.RoutingKeys)
})

t.Run("error while getting service", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)
didDoc.Service = nil

dest, err := createDestinationFromIndy(didDoc)
require.Error(t, err)
require.Contains(t, err.Error(), "missing DID doc service")
require.Nil(t, dest)
})

t.Run("missing service endpoint", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)
didDoc.Service = []did.Service{{
Type: legacyDIDCommServiceType,
}}

dest, err := createDestinationFromIndy(didDoc)
require.Error(t, err)
require.Contains(t, err.Error(), "no service endpoint")
require.Nil(t, dest)
})

t.Run("missing recipient keys", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)
didDoc.Service = []did.Service{{
Type: legacyDIDCommServiceType,
ServiceEndpoint: "localhost:8080",
}}

dest, err := createDestinationFromIndy(didDoc)
require.Error(t, err)
require.Contains(t, err.Error(), "no recipient keys")
require.Nil(t, dest)
})
}

func TestLookupIndyKeys(t *testing.T) {
t.Run("lookup recipient keys", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)

recipientKeys := lookupIndyRecipientKeys(didDoc, didDoc.Service[0].RecipientKeys)
require.NotNil(t, recipientKeys)
require.Len(t, recipientKeys, 1)

pk, err := fingerprint.PubKeyFromDIDKey(recipientKeys[0])
require.NoError(t, err)
require.ElementsMatch(t, didDoc.VerificationMethod[0].Value, pk)
})

t.Run("no keys to lookup", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)

recipientKeys := lookupIndyRecipientKeys(didDoc, nil)
require.Nil(t, recipientKeys)
})

t.Run("skip key that isn't found in verificationmethod list", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)

didDoc.Service[0].RecipientKeys = []string{"bad key"}

recipientKeys := lookupIndyRecipientKeys(didDoc, didDoc.Service[0].RecipientKeys)
require.Len(t, recipientKeys, 0)
})

t.Run("skip keys that aren't of handled type", func(t *testing.T) {
didDoc := mockdiddoc.GetMockIndyDoc(t)

didDoc.VerificationMethod[0].Type = "bad type"

recipientKeys := lookupIndyRecipientKeys(didDoc, didDoc.Service[0].RecipientKeys)
require.Len(t, recipientKeys, 0)
})
}

func createDIDDoc() *did.Doc {
pubKey, _ := generateKeyPair()
return createDIDDocWithKey(pubKey)
Expand Down
5 changes: 5 additions & 0 deletions pkg/didcomm/protocol/didexchange/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ type Request struct {
Label string `json:"label,omitempty"`
Connection *Connection `json:"connection,omitempty"`
Thread *decorator.Thread `json:"~thread,omitempty"`
// DID the did of the requester. Optional, may be present within `connection` instead.
DID string `json:"did,omitempty"`
// DocAttach an attachment containing the did doc of the requester.
// Optional, may be present within `connection` instead.
DocAttach *decorator.Attachment `json:"did_doc~attach,omitempty"`
}

// Response defines a2a DID exchange response
Expand Down
70 changes: 64 additions & 6 deletions pkg/didcomm/protocol/didexchange/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ SPDX-License-Identifier: Apache-2.0
package didexchange

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

"github.com/google/uuid"

Expand Down Expand Up @@ -44,6 +46,17 @@ const (
ResponseMsgType = PIURI + "/response"
// AckMsgType defines the did-exchange ack message type.
AckMsgType = PIURI + "/ack"
// OldPIURI is the old did-exchange protocol identifier URI.
OldPIURI = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0"
// OldInvitationMsgType defines the old format's did-exchange invite message type.
OldInvitationMsgType = OldPIURI + "/invitation"
// OldRequestMsgType defines the old format's did-exchange request message type.
OldRequestMsgType = OldPIURI + "/request"
// OldResponseMsgType defines the old format's did-exchange response message type.
OldResponseMsgType = OldPIURI + "/response"
// OldAckMsgType defines the old format's did-exchange ack message type.
OldAckMsgType = OldPIURI + "/ack"

// oobMsgType is the internal message type for the oob invitation that the didexchange service receives.
oobMsgType = "oob-invitation"
routerConnsMetadataKey = "routerConnections"
Expand Down Expand Up @@ -246,7 +259,11 @@ func (s *Service) Accept(msgType string) bool {
return msgType == InvitationMsgType ||
msgType == RequestMsgType ||
msgType == ResponseMsgType ||
msgType == AckMsgType
msgType == AckMsgType ||
msgType == OldInvitationMsgType ||
msgType == OldRequestMsgType ||
msgType == OldResponseMsgType ||
msgType == OldAckMsgType
}

// HandleOutbound handles outbound didexchange messages.
Expand Down Expand Up @@ -629,13 +646,13 @@ func (s *Service) connectionRecord(msg service.DIDCommMsg) (*connection.Record,
switch msg.Type() {
case oobMsgType:
return s.oobInvitationMsgRecord(msg)
case InvitationMsgType:
case InvitationMsgType, OldInvitationMsgType:
return s.invitationMsgRecord(msg)
case RequestMsgType:
case RequestMsgType, OldRequestMsgType:
return s.requestMsgRecord(msg)
case ResponseMsgType:
case ResponseMsgType, OldResponseMsgType:
return s.responseMsgRecord(msg)
case AckMsgType:
case AckMsgType, OldAckMsgType:
return s.ackMsgRecord(msg)
}

Expand Down Expand Up @@ -722,6 +739,42 @@ func (s *Service) invitationMsgRecord(msg service.DIDCommMsg) (*connection.Recor
return connRecord, nil
}

// nolint:gomnd
func pad(b64 string) string {
mod := len(b64) % 4
if mod <= 1 {
return b64
}

return b64 + strings.Repeat("=", 4-mod)
}

func getRequestConnection(r *Request) (*Connection, error) {
if r.Connection != nil {
return r.Connection, nil
}

if r.DocAttach == nil {
return nil, fmt.Errorf("missing did_doc~attach from request")
}

docData, err := base64.StdEncoding.DecodeString(pad(r.DocAttach.Data.Base64))
if err != nil {
return nil, fmt.Errorf("failed to parse base64 attachment data: %w", err)
}

doc, err := did.ParseDocument(docData)
if err != nil {
logger.Errorf("doc bytes: '%s'", string(docData))
return nil, fmt.Errorf("failed to parse did document: %w", err)
}

return &Connection{
DID: r.DID,
DIDDoc: doc,
}, nil
}

func (s *Service) requestMsgRecord(msg service.DIDCommMsg) (*connection.Record, error) {
request := Request{}

Expand All @@ -740,11 +793,16 @@ func (s *Service) requestMsgRecord(msg service.DIDCommMsg) (*connection.Record,
ConnectionID: generateRandomID(),
ThreadID: request.ID,
State: stateNameNull,
TheirDID: request.Connection.DID,
InvitationID: invitationID,
Namespace: theirNSPrefix,
}

if request.Connection != nil {
connRecord.TheirDID = request.Connection.DID
} else {
connRecord.TheirDID = request.DID
}

if err := s.connectionRecorder.SaveConnectionRecord(connRecord); err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 92a29a8

Please sign in to comment.