github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/protocol/legacyconnection/states.go (about)

     1  /*
     2  Copyright Avast Software. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package legacyconnection
     8  
     9  import (
    10  	"encoding/base64"
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/btcsuite/btcutil/base58"
    18  	"github.com/google/uuid"
    19  
    20  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid"
    21  
    22  	"github.com/hyperledger/aries-framework-go/pkg/common/model"
    23  	"github.com/hyperledger/aries-framework-go/pkg/crypto"
    24  	model2 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
    25  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    26  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    27  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
    28  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/transport"
    29  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    30  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey"
    31  	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    32  	"github.com/hyperledger/aries-framework-go/pkg/internal/didcommutil"
    33  	"github.com/hyperledger/aries-framework-go/pkg/internal/didkeyutil"
    34  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    35  	connectionstore "github.com/hyperledger/aries-framework-go/pkg/store/connection"
    36  )
    37  
    38  const (
    39  	stateNameNoop = "noop"
    40  	stateNameNull = "null"
    41  	// StateIDInvited marks the invited phase of the connection protocol.
    42  	StateIDInvited = "invited"
    43  	// StateIDRequested marks the requested phase of the connection protocol.
    44  	StateIDRequested = "requested"
    45  	// StateIDResponded marks the responded phase of the connection protocol.
    46  	StateIDResponded = "responded"
    47  	// StateIDCompleted marks the completed phase of the connection protocol.
    48  	StateIDCompleted   = "completed"
    49  	didCommServiceType = "did-communication"
    50  	ackStatusOK        = "ok"
    51  	// legacyDIDCommServiceType for aca-py interop.
    52  	legacyDIDCommServiceType   = "IndyAgent"
    53  	ed25519VerificationKey2018 = "Ed25519VerificationKey2018"
    54  	didMethod                  = "peer"
    55  	x25519KeyAgreementKey2019  = "X25519KeyAgreementKey2019"
    56  	signatureType              = "https://didcomm.org/signature/1.0/ed25519Sha512_single"
    57  	// PlsAckOnReceipt ack type that says, "Please send me an ack as soon as you receive this message.".
    58  	PlsAckOnReceipt = "RECEIPT"
    59  	timestampLength = 8
    60  )
    61  
    62  // state action for network call.
    63  type stateAction func() error
    64  
    65  // The connection protocol's state.
    66  type state interface {
    67  	// Name of this state.
    68  	Name() string
    69  
    70  	// CanTransitionTo Whether this state allows transitioning into the next state.
    71  	CanTransitionTo(next state) bool
    72  
    73  	// ExecuteInbound this state, returning a followup state to be immediately executed as well.
    74  	// The 'noOp' state should be returned if the state has no followup.
    75  	ExecuteInbound(msg *stateMachineMsg, thid string, ctx *context) (connRecord *connectionstore.Record,
    76  		state state, action stateAction, err error)
    77  }
    78  
    79  // Returns the state towards which the protocol will transition to if the msgType is processed.
    80  func stateFromMsgType(msgType string) (state, error) {
    81  	switch msgType {
    82  	case InvitationMsgType:
    83  		return &invited{}, nil
    84  	case RequestMsgType:
    85  		return &requested{}, nil
    86  	case ResponseMsgType:
    87  		return &responded{}, nil
    88  	case AckMsgType:
    89  		return &completed{}, nil
    90  	default:
    91  		return nil, fmt.Errorf("unrecognized msgType: %s", msgType)
    92  	}
    93  }
    94  
    95  // Returns the state representing the name.
    96  func stateFromName(name string) (state, error) {
    97  	switch name {
    98  	case stateNameNoop:
    99  		return &noOp{}, nil
   100  	case stateNameNull:
   101  		return &null{}, nil
   102  	case StateIDInvited:
   103  		return &invited{}, nil
   104  	case StateIDRequested:
   105  		return &requested{}, nil
   106  	case StateIDResponded:
   107  		return &responded{}, nil
   108  	case StateIDCompleted:
   109  		return &completed{}, nil
   110  	default:
   111  		return nil, fmt.Errorf("invalid state name %s", name)
   112  	}
   113  }
   114  
   115  type noOp struct{}
   116  
   117  func (s *noOp) Name() string {
   118  	return stateNameNoop
   119  }
   120  
   121  func (s *noOp) CanTransitionTo(_ state) bool {
   122  	return false
   123  }
   124  
   125  func (s *noOp) ExecuteInbound(_ *stateMachineMsg, _ string, _ *context) (*connectionstore.Record,
   126  	state, stateAction, error) {
   127  	return nil, nil, nil, errors.New("cannot execute no-op")
   128  }
   129  
   130  // null state.
   131  type null struct{}
   132  
   133  func (s *null) Name() string {
   134  	return stateNameNull
   135  }
   136  
   137  func (s *null) CanTransitionTo(next state) bool {
   138  	return StateIDInvited == next.Name() || StateIDRequested == next.Name()
   139  }
   140  
   141  func (s *null) ExecuteInbound(_ *stateMachineMsg, _ string, _ *context) (*connectionstore.Record,
   142  	state, stateAction, error) {
   143  	return &connectionstore.Record{}, &noOp{}, nil, nil
   144  }
   145  
   146  // invited state.
   147  type invited struct{}
   148  
   149  func (s *invited) Name() string {
   150  	return StateIDInvited
   151  }
   152  
   153  func (s *invited) CanTransitionTo(next state) bool {
   154  	return StateIDRequested == next.Name()
   155  }
   156  
   157  func (s *invited) ExecuteInbound(msg *stateMachineMsg, _ string, _ *context) (*connectionstore.Record,
   158  	state, stateAction, error) {
   159  	if msg.Type() != InvitationMsgType {
   160  		return nil, nil, nil, fmt.Errorf("illegal msg type %s for state %s", msg.Type(), s.Name())
   161  	}
   162  
   163  	return msg.connRecord, &requested{}, func() error { return nil }, nil
   164  }
   165  
   166  // requested state.
   167  type requested struct{}
   168  
   169  func (s *requested) Name() string {
   170  	return StateIDRequested
   171  }
   172  
   173  func (s *requested) CanTransitionTo(next state) bool {
   174  	return StateIDResponded == next.Name()
   175  }
   176  
   177  func (s *requested) ExecuteInbound(msg *stateMachineMsg, thid string, ctx *context) (*connectionstore.Record,
   178  	state, stateAction, error) {
   179  	switch msg.Type() {
   180  	case InvitationMsgType:
   181  		invitation := &Invitation{}
   182  
   183  		err := msg.Decode(invitation)
   184  		if err != nil {
   185  			return nil, nil, nil, fmt.Errorf("JSON unmarshalling of invitation: %w", err)
   186  		}
   187  
   188  		action, connRecord, err := ctx.handleInboundInvitation(invitation, thid, msg.options, msg.connRecord)
   189  		if err != nil {
   190  			return nil, nil, nil, fmt.Errorf("handle inbound invitation: %w", err)
   191  		}
   192  
   193  		return connRecord, &noOp{}, action, nil
   194  	case RequestMsgType:
   195  		return msg.connRecord, &responded{}, func() error { return nil }, nil
   196  	default:
   197  		return nil, nil, nil, fmt.Errorf("illegal msg type %s for state %s", msg.Type(), s.Name())
   198  	}
   199  }
   200  
   201  // responded state.
   202  type responded struct{}
   203  
   204  func (s *responded) Name() string {
   205  	return StateIDResponded
   206  }
   207  
   208  func (s *responded) CanTransitionTo(next state) bool {
   209  	return StateIDCompleted == next.Name()
   210  }
   211  
   212  func (s *responded) ExecuteInbound(msg *stateMachineMsg, _ string, ctx *context) (*connectionstore.Record,
   213  	state, stateAction, error) {
   214  	switch msg.Type() {
   215  	case RequestMsgType:
   216  		request := &Request{}
   217  
   218  		err := msg.Decode(request)
   219  		if err != nil {
   220  			return nil, nil, nil, fmt.Errorf("JSON unmarshalling of request: %w", err)
   221  		}
   222  
   223  		action, connRecord, err := ctx.handleInboundRequest(request, msg.options, msg.connRecord)
   224  		if err != nil {
   225  			return nil, nil, nil, fmt.Errorf("handle inbound request: %w", err)
   226  		}
   227  
   228  		return connRecord, &noOp{}, action, nil
   229  	case ResponseMsgType:
   230  		return msg.connRecord, &completed{}, func() error { return nil }, nil
   231  	default:
   232  		return nil, nil, nil, fmt.Errorf("illegal msg type %s for state %s", msg.Type(), s.Name())
   233  	}
   234  }
   235  
   236  // completed state.
   237  type completed struct{}
   238  
   239  func (s *completed) Name() string {
   240  	return StateIDCompleted
   241  }
   242  
   243  func (s *completed) CanTransitionTo(_ state) bool {
   244  	return false
   245  }
   246  
   247  func (s *completed) ExecuteInbound(msg *stateMachineMsg, _ string, ctx *context) (*connectionstore.Record,
   248  	state, stateAction, error) {
   249  	switch msg.Type() {
   250  	case ResponseMsgType:
   251  		response := &Response{}
   252  
   253  		err := msg.Decode(response)
   254  		if err != nil {
   255  			return nil, nil, nil, fmt.Errorf("JSON unmarshalling of response: %w", err)
   256  		}
   257  
   258  		action, connRecord, err := ctx.handleInboundResponse(response)
   259  		if err != nil {
   260  			return nil, nil, nil, fmt.Errorf("handle inbound response: %w", err)
   261  		}
   262  
   263  		return connRecord, &noOp{}, action, nil
   264  	case AckMsgType:
   265  		action := func() error { return nil }
   266  		return msg.connRecord, &noOp{}, action, nil
   267  	default:
   268  		return nil, nil, nil, fmt.Errorf("illegal msg type %s for state %s", msg.Type(), s.Name())
   269  	}
   270  }
   271  
   272  func (ctx *context) handleInboundInvitation(invitation *Invitation, thid string, options *options,
   273  	connRec *connectionstore.Record) (stateAction, *connectionstore.Record, error) {
   274  	// create a destination from invitation
   275  	destination, err := ctx.getDestination(invitation)
   276  	if err != nil {
   277  		return nil, nil, err
   278  	}
   279  
   280  	pid := invitation.ID
   281  	if connRec.Implicit {
   282  		pid = invitation.DID
   283  	}
   284  
   285  	return ctx.createConnectionRequest(destination, getLabel(options), thid, pid, options, connRec)
   286  }
   287  
   288  func (ctx *context) createConnectionRequest(destination *service.Destination, label, thid, pthid string,
   289  	options *options, connRec *connectionstore.Record) (stateAction, *connectionstore.Record, error) {
   290  	request := &Request{
   291  		Type:  RequestMsgType,
   292  		ID:    thid,
   293  		Label: label,
   294  		Thread: &decorator.Thread{
   295  			PID: pthid,
   296  		},
   297  	}
   298  	// get did document to use in connection request
   299  	myDIDDoc, err := ctx.getMyDIDDoc(getPublicDID(options), getRouterConnections(options), legacyDIDCommServiceType)
   300  	if err != nil {
   301  		return nil, nil, err
   302  	}
   303  
   304  	connRec.MyDID = myDIDDoc.ID
   305  
   306  	senderKey, err := recipientKey(myDIDDoc)
   307  	if err != nil {
   308  		return nil, nil, fmt.Errorf("getting recipient key: %w", err)
   309  	}
   310  
   311  	request.Connection = &Connection{
   312  		DID:    myDIDDoc.ID,
   313  		DIDDoc: myDIDDoc,
   314  	}
   315  
   316  	return func() error {
   317  		return ctx.outboundDispatcher.Send(request, senderKey, destination)
   318  	}, connRec, nil
   319  }
   320  
   321  // nolint:gocyclo,funlen
   322  func (ctx *context) handleInboundRequest(request *Request, options *options,
   323  	connRec *connectionstore.Record) (stateAction, *connectionstore.Record, error) {
   324  	logger.Debugf("handling request: %#v", request)
   325  
   326  	requestDidDoc, err := ctx.resolveDidDocFromConnection(request.Connection)
   327  	if err != nil {
   328  		return nil, nil, fmt.Errorf("resolve did doc from connection request: %w", err)
   329  	}
   330  
   331  	// get did document that will be used in connection response
   332  	// (my did doc)
   333  	myDID := getPublicDID(options)
   334  
   335  	destination, err := service.CreateDestination(requestDidDoc)
   336  	if err != nil {
   337  		return nil, nil, err
   338  	}
   339  
   340  	var serviceType string
   341  	if len(requestDidDoc.Service) > 0 {
   342  		serviceType = didcommutil.GetServiceType(requestDidDoc.Service[0].Type)
   343  	} else {
   344  		serviceType = legacyDIDCommServiceType
   345  	}
   346  
   347  	responseDidDoc, err := ctx.getMyDIDDoc(myDID, getRouterConnections(options), serviceType)
   348  	if err != nil {
   349  		return nil, nil, fmt.Errorf("get response did doc and connection: %w", err)
   350  	}
   351  
   352  	var verKey string
   353  	if len(connRec.InvitationRecipientKeys) > 0 {
   354  		verKey = connRec.InvitationRecipientKeys[0]
   355  	} else {
   356  		verKey, err = ctx.getVerKey(request.Thread.PID)
   357  		if err != nil {
   358  			return nil, nil, fmt.Errorf("failed to get verkey : %w", err)
   359  		}
   360  	}
   361  
   362  	// prepare connection signature
   363  	connectionSignature, err := ctx.prepareConnectionSignature(responseDidDoc, verKey)
   364  	if err != nil {
   365  		return nil, nil, err
   366  	}
   367  
   368  	response := ctx.prepareResponse(request, connectionSignature)
   369  
   370  	var senderVerKey string
   371  
   372  	senderVerKey, err = recipientKey(responseDidDoc)
   373  	if err != nil {
   374  		return nil, nil, fmt.Errorf("get recipient key: %w", err)
   375  	}
   376  
   377  	connRec.MyDID = responseDidDoc.ID
   378  	connRec.TheirDID = request.Connection.DID
   379  	connRec.TheirLabel = request.Label
   380  
   381  	if len(responseDidDoc.Service) > 0 {
   382  		connRec.RecipientKeys = responseDidDoc.Service[0].RecipientKeys
   383  	}
   384  
   385  	accept, err := destination.ServiceEndpoint.Accept()
   386  	if err != nil {
   387  		accept = []string{}
   388  	}
   389  
   390  	if len(accept) > 0 {
   391  		connRec.MediaTypeProfiles = accept
   392  	}
   393  	// send connection response
   394  	return func() error {
   395  		return ctx.outboundDispatcher.Send(response, senderVerKey, destination)
   396  	}, connRec, nil
   397  }
   398  
   399  func (ctx *context) prepareConnectionSignature(didDoc *did.Doc, verKey string) (*ConnectionSignature, error) {
   400  	connection := &Connection{
   401  		DID:    didDoc.ID,
   402  		DIDDoc: didDoc,
   403  	}
   404  	logger.Debugf("connection=%+v verKey=%s", connection, verKey)
   405  
   406  	connAttributeBytes, err := connection.toLegacyJSONBytes()
   407  	if err != nil {
   408  		return nil, fmt.Errorf("failed to marshal connection : %w", err)
   409  	}
   410  
   411  	now := time.Now().Unix()
   412  	timestampBuf := make([]byte, timestampLength)
   413  	binary.BigEndian.PutUint64(timestampBuf, uint64(now))
   414  
   415  	concatenateSignData := append(timestampBuf, connAttributeBytes...)
   416  
   417  	var signingKey []byte
   418  
   419  	if strings.HasPrefix(verKey, "did:key:") {
   420  		var pubKey *crypto.PublicKey
   421  
   422  		pubKey, err = kmsdidkey.EncryptionPubKeyFromDIDKey(verKey)
   423  		if err != nil {
   424  			return nil, err
   425  		}
   426  
   427  		signingKey = pubKey.X
   428  	} else {
   429  		signingKey = base58.Decode(verKey)
   430  	}
   431  
   432  	signingKID, err := jwkkid.CreateKID(signingKey, kms.ED25519Type)
   433  	if err != nil {
   434  		return nil, fmt.Errorf("failed to generate KID from public key: %w", err)
   435  	}
   436  
   437  	kh, err := ctx.kms.Get(signingKID)
   438  	if err != nil {
   439  		return nil, fmt.Errorf("failed to get key handle: %w", err)
   440  	}
   441  
   442  	var signature []byte
   443  
   444  	signature, err = ctx.crypto.Sign(concatenateSignData, kh)
   445  	if err != nil {
   446  		return nil, fmt.Errorf("signing data: %w", err)
   447  	}
   448  
   449  	return &ConnectionSignature{
   450  		Type:       signatureType,
   451  		SignedData: base64.URLEncoding.EncodeToString(concatenateSignData),
   452  		SignVerKey: verKey,
   453  		Signature:  base64.URLEncoding.EncodeToString(signature),
   454  	}, nil
   455  }
   456  
   457  func (ctx *context) prepareResponse(request *Request, signature *ConnectionSignature) *Response {
   458  	// prepare the response
   459  	response := &Response{
   460  		Type: ResponseMsgType,
   461  		ID:   uuid.New().String(),
   462  		Thread: &decorator.Thread{
   463  			ID: request.ID,
   464  		},
   465  		ConnectionSignature: signature,
   466  		PleaseAck: &PleaseAck{
   467  			[]string{PlsAckOnReceipt},
   468  		},
   469  	}
   470  
   471  	if request.Thread != nil {
   472  		response.Thread.PID = request.Thread.PID
   473  	}
   474  
   475  	return response
   476  }
   477  
   478  func getPublicDID(options *options) string {
   479  	if options == nil {
   480  		return ""
   481  	}
   482  
   483  	return options.publicDID
   484  }
   485  
   486  func getRouterConnections(options *options) []string {
   487  	if options == nil {
   488  		return nil
   489  	}
   490  
   491  	return options.routerConnections
   492  }
   493  
   494  // returns the label given in the options, otherwise an empty string.
   495  func getLabel(options *options) string {
   496  	if options == nil {
   497  		return ""
   498  	}
   499  
   500  	return options.label
   501  }
   502  
   503  func (ctx *context) getDestination(invitation *Invitation) (*service.Destination, error) {
   504  	if invitation.DID != "" {
   505  		return service.GetDestination(invitation.DID, ctx.vdRegistry)
   506  	}
   507  
   508  	accept := ctx.mediaTypeProfiles
   509  	if isDIDCommV2(accept) {
   510  		return nil, fmt.Errorf("DIDComm V2 profile type(s): %v - are not supported", accept)
   511  	}
   512  
   513  	return &service.Destination{
   514  		RecipientKeys:     invitation.RecipientKeys,
   515  		ServiceEndpoint:   model.NewDIDCommV1Endpoint(invitation.ServiceEndpoint),
   516  		MediaTypeProfiles: accept,
   517  		RoutingKeys:       invitation.RoutingKeys,
   518  	}, nil
   519  }
   520  
   521  // nolint:gocyclo,funlen
   522  func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, serviceType string) (*did.Doc, error) {
   523  	if pubDID != "" {
   524  		logger.Debugf("using public did[%s] for connection", pubDID)
   525  
   526  		docResolution, err := ctx.vdRegistry.Resolve(pubDID)
   527  		if err != nil {
   528  			return nil, fmt.Errorf("resolve public did[%s]: %w", pubDID, err)
   529  		}
   530  
   531  		err = ctx.connectionStore.SaveDIDFromDoc(docResolution.DIDDocument)
   532  		if err != nil {
   533  			return nil, err
   534  		}
   535  
   536  		return docResolution.DIDDocument, nil
   537  	}
   538  
   539  	logger.Debugf("creating new '%s' did for connection", didMethod)
   540  
   541  	var (
   542  		services   []did.Service
   543  		newService bool
   544  	)
   545  
   546  	for _, connID := range routerConnections {
   547  		// get the route configs (pass empty service endpoint, as default service endpoint added in VDR)
   548  		serviceEndpoint, routingKeys, err := mediator.GetRouterConfig(ctx.routeSvc, connID, "")
   549  		if err != nil {
   550  			return nil, fmt.Errorf("did doc - fetch router config: %w", err)
   551  		}
   552  
   553  		var svc did.Service
   554  
   555  		switch serviceType {
   556  		case didCommServiceType, legacyDIDCommServiceType:
   557  			routingKeys = didkeyutil.ConvertDIDKeysToBase58Keys(routingKeys)
   558  			svc = did.Service{
   559  				Type:            serviceType,
   560  				ServiceEndpoint: model.NewDIDCommV1Endpoint(serviceEndpoint),
   561  				RoutingKeys:     routingKeys,
   562  			}
   563  		default:
   564  			return nil, fmt.Errorf("service type %s is not supported", serviceType)
   565  		}
   566  
   567  		services = append(services, svc)
   568  	}
   569  
   570  	if len(services) == 0 {
   571  		newService = true
   572  
   573  		services = append(services, did.Service{Type: serviceType})
   574  	}
   575  
   576  	newDID := &did.Doc{Service: services}
   577  
   578  	err := ctx.createNewKeyAndVM(newDID)
   579  	if err != nil {
   580  		return nil, fmt.Errorf("failed to create and export public key: %w", err)
   581  	}
   582  
   583  	if newService {
   584  		switch didcommutil.GetServiceType(newDID.Service[0].Type) {
   585  		case didCommServiceType, legacyDIDCommServiceType:
   586  			newDID.Service[0].RecipientKeys = []string{base58.Encode(newDID.VerificationMethod[0].Value)}
   587  		default:
   588  			return nil, fmt.Errorf("service type %s is not supported", newDID.Service[0].Type)
   589  		}
   590  	}
   591  	// by default use peer did
   592  	docResolution, err := ctx.vdRegistry.Create(didMethod, newDID)
   593  	if err != nil {
   594  		return nil, fmt.Errorf("create %s did: %w", didMethod, err)
   595  	}
   596  
   597  	if len(routerConnections) != 0 {
   598  		err = ctx.addRouterKeys(docResolution.DIDDocument, routerConnections)
   599  		if err != nil {
   600  			return nil, err
   601  		}
   602  	}
   603  
   604  	err = ctx.connectionStore.SaveDIDFromDoc(docResolution.DIDDocument)
   605  	if err != nil {
   606  		return nil, err
   607  	}
   608  
   609  	return docResolution.DIDDocument, nil
   610  }
   611  
   612  func (ctx *context) addRouterKeys(doc *did.Doc, routerConnections []string) error {
   613  	svc, ok := did.LookupService(doc, legacyDIDCommServiceType)
   614  	if ok {
   615  		for _, recKey := range svc.RecipientKeys {
   616  			for _, connID := range routerConnections {
   617  				// TODO https://github.com/hyperledger/aries-framework-go/issues/1105 Support to Add multiple
   618  				//  recKeys to the Router
   619  				if err := mediator.AddKeyToRouter(ctx.routeSvc, connID, recKey); err != nil {
   620  					return fmt.Errorf("did doc - add key to the router: %w", err)
   621  				}
   622  			}
   623  		}
   624  	}
   625  
   626  	return nil
   627  }
   628  
   629  func (ctx *context) isPrivateDIDMethod(method string) bool {
   630  	// todo: find better solution to forcing test dids to be treated as private dids
   631  	if method == "local" || method == "test" {
   632  		return true
   633  	}
   634  
   635  	return method == "peer" || method == "sov" || method == "key"
   636  }
   637  
   638  func (ctx *context) resolveDidDocFromConnection(con *Connection) (*did.Doc, error) {
   639  	if con.DIDDoc == nil {
   640  		return nil, fmt.Errorf("missing DIDDoc")
   641  	}
   642  	// FIXME Interop: aca-py and vcx issue. Should be removed after theirs fix
   643  	if !strings.HasPrefix(con.DIDDoc.ID, "did") && len(con.DIDDoc.Service) > 0 {
   644  		if len(con.DIDDoc.Service[0].RecipientKeys) > 0 {
   645  			con.DIDDoc.ID = didkeyutil.ConvertBase58KeysToDIDKeys(con.DIDDoc.Service[0].RecipientKeys)[0]
   646  			con.DID = con.DIDDoc.ID
   647  		}
   648  	}
   649  
   650  	parsedDID, err := did.Parse(con.DID)
   651  	if err != nil {
   652  		return nil, fmt.Errorf("failed to parse did: %w", err)
   653  	}
   654  
   655  	if err == nil && !ctx.isPrivateDIDMethod(parsedDID.Method) {
   656  		docResolution, e := ctx.vdRegistry.Resolve(con.DID)
   657  		if e != nil {
   658  			return nil, fmt.Errorf("failed to resolve public did %s: %w", con.DID, e)
   659  		}
   660  
   661  		return docResolution.DIDDocument, nil
   662  	}
   663  	// store provided did document
   664  	_, err = ctx.vdRegistry.Create("peer", con.DIDDoc, vdrapi.WithOption("store", true))
   665  	if err != nil {
   666  		return nil, fmt.Errorf("failed to store provided did document: %w", err)
   667  	}
   668  
   669  	return con.DIDDoc, nil
   670  }
   671  
   672  func (ctx *context) handleInboundResponse(response *Response) (stateAction, *connectionstore.Record, error) {
   673  	ack := model2.Ack{
   674  		Type:   AckMsgType,
   675  		ID:     uuid.New().String(),
   676  		Status: ackStatusOK,
   677  		Thread: &decorator.Thread{
   678  			ID: response.Thread.ID,
   679  		},
   680  	}
   681  
   682  	nsThID, err := connectionstore.CreateNamespaceKey(myNSPrefix, ack.Thread.ID)
   683  	if err != nil {
   684  		return nil, nil, err
   685  	}
   686  
   687  	connRecord, err := ctx.connectionRecorder.GetConnectionRecordByNSThreadID(nsThID)
   688  	if err != nil {
   689  		return nil, nil, fmt.Errorf("get connection record: %w", err)
   690  	}
   691  
   692  	conn, err := ctx.verifySignature(response.ConnectionSignature, connRecord.RecipientKeys[0])
   693  	if err != nil {
   694  		return nil, nil, err
   695  	}
   696  
   697  	connRecord.TheirDID = conn.DID
   698  
   699  	responseDidDoc, err := ctx.resolveDidDocFromConnection(conn)
   700  	if err != nil {
   701  		return nil, nil, fmt.Errorf("resolve response did doc: %w", err)
   702  	}
   703  
   704  	destination, err := service.CreateDestination(responseDidDoc)
   705  	if err != nil {
   706  		return nil, nil, fmt.Errorf("prepare destination from response did doc: %w", err)
   707  	}
   708  
   709  	docResolution, err := ctx.vdRegistry.Resolve(connRecord.MyDID)
   710  	if err != nil {
   711  		return nil, nil, fmt.Errorf("fetching did document: %w", err)
   712  	}
   713  
   714  	recKey, err := recipientKey(docResolution.DIDDocument)
   715  	if err != nil {
   716  		return nil, nil, fmt.Errorf("handle inbound response: %w", err)
   717  	}
   718  
   719  	return func() error {
   720  		return ctx.outboundDispatcher.Send(ack, recKey, destination)
   721  	}, connRecord, nil
   722  }
   723  
   724  // verifySignature verifies connection signature and returns connection.
   725  func (ctx *context) verifySignature(connSignature *ConnectionSignature, recipientKeys string) (*Connection, error) {
   726  	sigData, err := base64.URLEncoding.DecodeString(connSignature.SignedData)
   727  	if err != nil {
   728  		return nil, fmt.Errorf("decode signature data: %w", err)
   729  	}
   730  
   731  	if len(sigData) == 0 {
   732  		return nil, fmt.Errorf("missing or invalid signature data")
   733  	}
   734  
   735  	signature, err := base64.URLEncoding.DecodeString(connSignature.Signature)
   736  	if err != nil {
   737  		return nil, fmt.Errorf("decode signature: %w", err)
   738  	}
   739  
   740  	// The signature data must be used to verify against the invitation's recipientKeys for continuity.
   741  	var verKey []byte
   742  
   743  	if strings.HasPrefix(recipientKeys, "did:key:") {
   744  		var pubKey *crypto.PublicKey
   745  
   746  		pubKey, err = kmsdidkey.EncryptionPubKeyFromDIDKey(recipientKeys)
   747  		if err != nil {
   748  			return nil, err
   749  		}
   750  
   751  		verKey = pubKey.X
   752  	} else {
   753  		verKey = base58.Decode(recipientKeys)
   754  	}
   755  
   756  	kh, err := ctx.kms.PubKeyBytesToHandle(verKey, kms.ED25519Type)
   757  	if err != nil {
   758  		return nil, fmt.Errorf("failed to get key handle: %w", err)
   759  	}
   760  
   761  	err = ctx.crypto.Verify(signature, sigData, kh)
   762  	if err != nil {
   763  		return nil, fmt.Errorf("verify signature: %w", err)
   764  	}
   765  
   766  	// trimming the timestamp and delimiter - only taking out connection attribute bytes
   767  	if len(sigData) <= timestampLength {
   768  		return nil, fmt.Errorf("missing connection attribute bytes")
   769  	}
   770  
   771  	connBytes := sigData[timestampLength:]
   772  
   773  	conn, err := parseLegacyJSONBytes(connBytes)
   774  	if err != nil {
   775  		return nil, fmt.Errorf("JSON unmarshalling of connection: %w", err)
   776  	}
   777  
   778  	return conn, nil
   779  }
   780  
   781  func (ctx *context) getVerKey(invitationID string) (string, error) {
   782  	var invitation Invitation
   783  
   784  	if isDID(invitationID) {
   785  		invitation = Invitation{ID: invitationID, DID: invitationID}
   786  	} else {
   787  		err := ctx.connectionRecorder.GetInvitation(invitationID, &invitation)
   788  		if err != nil {
   789  			return "", fmt.Errorf("get invitation for [invitationID=%s]: %w", invitationID, err)
   790  		}
   791  	}
   792  
   793  	invPubKey, err := ctx.getInvitationRecipientKey(&invitation)
   794  	if err != nil {
   795  		return "", fmt.Errorf("get invitation recipient key: %w", err)
   796  	}
   797  
   798  	return invPubKey, nil
   799  }
   800  
   801  func (ctx *context) getInvitationRecipientKey(invitation *Invitation) (string, error) {
   802  	if invitation.DID != "" {
   803  		docResolution, err := ctx.vdRegistry.Resolve(invitation.DID)
   804  		if err != nil {
   805  			return "", fmt.Errorf("get invitation recipient key: %w", err)
   806  		}
   807  
   808  		recKey, err := recipientKey(docResolution.DIDDocument)
   809  		if err != nil {
   810  			return "", fmt.Errorf("getInvitationRecipientKey: %w", err)
   811  		}
   812  
   813  		return recKey, nil
   814  	}
   815  
   816  	return invitation.RecipientKeys[0], nil
   817  }
   818  
   819  func isDID(str string) bool {
   820  	const didPrefix = "did:"
   821  	return strings.HasPrefix(str, didPrefix)
   822  }
   823  
   824  func isDIDCommV2(mediaTypeProfiles []string) bool {
   825  	for _, mtp := range mediaTypeProfiles {
   826  		switch mtp {
   827  		case transport.MediaTypeDIDCommV2Profile, transport.MediaTypeAIP2RFC0587Profile:
   828  			return true
   829  		}
   830  	}
   831  
   832  	return false
   833  }
   834  
   835  // returns the did:key ID of the first element in the doc's destination RecipientKeys.
   836  func recipientKey(doc *did.Doc) (string, error) {
   837  	serviceType := didcommutil.GetServiceType(doc.Service[0].Type)
   838  
   839  	switch serviceType {
   840  	case vdrapi.DIDCommServiceType, legacyDIDCommServiceType:
   841  		dest, err := service.CreateDestination(doc)
   842  		if err != nil {
   843  			return "", fmt.Errorf("failed to create destination: %w", err)
   844  		}
   845  
   846  		return dest.RecipientKeys[0], nil
   847  	default:
   848  		return "", fmt.Errorf("recipientKeyAsDIDKey: invalid DID Doc service type: '%v'", doc.Service[0].Type)
   849  	}
   850  }