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 }