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

Commit

Permalink
feat: Connection protocol (RFC-0160) DIDCommV1 implementation (partia…
Browse files Browse the repository at this point in the history
…lly)

Add implementation only for protocol's package

Signed-off-by: Abdulbois <abdulbois.tursunov@avast.com>
  • Loading branch information
Abdulbois committed Jul 25, 2022
1 parent 9bdda1a commit 3d01a16
Show file tree
Hide file tree
Showing 9 changed files with 5,768 additions and 0 deletions.
55 changes: 55 additions & 0 deletions pkg/didcomm/protocol/didconnection/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright Avast Software. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

// connectionEvent implements connection.Event interface.
type connectionEvent struct {
connectionID string
invitationID string
}

// ConnectionID returns Connection connectionID.
func (ex *connectionEvent) ConnectionID() string {
return ex.connectionID
}

// InvitationID returns Connection invitationID.
func (ex *connectionEvent) InvitationID() string {
return ex.invitationID
}

// connectionEventError for sending events with processing error.
type connectionEventError struct {
connectionEvent
err error
}

// Error implements error interface.
func (ex *connectionEventError) Error() string {
if ex.err != nil {
return ex.err.Error()
}

return ""
}

// All implements EventProperties interface.
func (ex *connectionEvent) All() map[string]interface{} {
return map[string]interface{}{
"connectionID": ex.ConnectionID(),
"invitationID": ex.InvitationID(),
}
}

// All implements EventProperties interface.
func (ex *connectionEventError) All() map[string]interface{} {
return map[string]interface{}{
"connectionID": ex.ConnectionID(),
"invitationID": ex.InvitationID(),
"error": ex.Error(),
}
}
30 changes: 30 additions & 0 deletions pkg/didcomm/protocol/didconnection/event_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Copyright Avast Software. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"errors"
"testing"

"github.com/stretchr/testify/require"
)

func TestConnectionEvent(t *testing.T) {
ev := connectionEvent{connectionID: "abc", invitationID: "xyz"}
require.Equal(t, ev.ConnectionID(), "abc")
require.Equal(t, ev.InvitationID(), "xyz")
require.Equal(t, ev.All()["connectionID"], ev.ConnectionID())
require.Equal(t, ev.All()["invitationID"], ev.InvitationID())

err := errors.New("processing error")
evErr := connectionEventError{err: err}
require.Equal(t, err.Error(), evErr.Error())
require.Equal(t, evErr.All()["error"], evErr.Error())

evErr = connectionEventError{}
require.Equal(t, "", evErr.Error())
}
91 changes: 91 additions & 0 deletions pkg/didcomm/protocol/didconnection/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Copyright Avast Software. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"encoding/json"
"fmt"

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

func (ctx *context) createNewKeyAndVM(didDoc *did.Doc) error {
vm, err := ctx.createSigningVM()
if err != nil {
return err
}

kaVM, err := ctx.createEncryptionVM()
if err != nil {
return err
}

didDoc.VerificationMethod = append(didDoc.VerificationMethod, *vm)
// TODO is Authentication needed?
didDoc.Authentication = append(didDoc.Authentication, *did.NewReferencedVerification(vm, did.Authentication))
// TODO is KeyAgreement needed?
didDoc.KeyAgreement = append(didDoc.KeyAgreement, *did.NewReferencedVerification(kaVM, did.KeyAgreement))

return nil
}

func (ctx *context) createSigningVM() (*did.VerificationMethod, error) {
vmType := getVerMethodType(ctx.keyType)

_, pubKeyBytes, err := ctx.kms.CreateAndExportPubKeyBytes(ctx.keyType)
if err != nil {
return nil, fmt.Errorf("createSigningVM: %w", err)
}

vmID := "#key-1"

switch vmType {
case ed25519VerificationKey2018:
return did.NewVerificationMethodFromBytes(vmID, vmType, "", pubKeyBytes), nil
default:
return nil, fmt.Errorf("createSigningVM: unsupported verification method: '%s'", vmType)
}
}

func (ctx *context) createEncryptionVM() (*did.VerificationMethod, error) {
encKeyType := ctx.keyAgreementType

vmType := getVerMethodType(encKeyType)

_, kaPubKeyBytes, err := ctx.kms.CreateAndExportPubKeyBytes(encKeyType)
if err != nil {
return nil, fmt.Errorf("createEncryptionVM: %w", err)
}

vmID := "#key-2"

switch vmType {
case x25519KeyAgreementKey2019:
key := &crypto.PublicKey{}

err = json.Unmarshal(kaPubKeyBytes, key)
if err != nil {
return nil, fmt.Errorf("createEncryptionVM: unable to unmarshal X25519 key: %w", err)
}

return did.NewVerificationMethodFromBytes(vmID, vmType, "", key.X), nil
default:
return nil, fmt.Errorf("unsupported verification method for KeyAgreement: '%s'", vmType)
}
}

// nolint:gochecknoglobals
var vmType = map[kms.KeyType]string{
kms.ED25519Type: ed25519VerificationKey2018,
kms.X25519ECDHKWType: x25519KeyAgreementKey2019,
}

func getVerMethodType(kt kms.KeyType) string {
return vmType[kt]
}
132 changes: 132 additions & 0 deletions pkg/didcomm/protocol/didconnection/keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
Copyright Avast Software. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/kms"
"github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol"
mockroute "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator"
mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
)

func TestCreateNewKeyAndVM(t *testing.T) {
k := newKMS(t, mockstorage.NewMockStoreProvider())

p, err := New(&protocol.MockProvider{
ServiceMap: map[string]interface{}{
mediator.Coordination: &mockroute.MockMediatorSvc{},
},
CustomKMS: k,
})
require.NoError(t, err)

t.Run("createNewKeyAndVM success", func(t *testing.T) {
didDoc := &did.Doc{}

p.ctx.keyType = kms.ED25519
p.ctx.keyAgreementType = kms.X25519ECDHKWType

err = p.ctx.createNewKeyAndVM(didDoc)
require.NoError(t, err)
require.Equal(t, ed25519VerificationKey2018, didDoc.VerificationMethod[0].Type)
require.Equal(t, x25519KeyAgreementKey2019, didDoc.KeyAgreement[0].VerificationMethod.Type)
})

t.Run("createNewKeyAndVM invalid keyType export signing key", func(t *testing.T) {
didDoc := &did.Doc{}

p.ctx.keyType = kms.HMACSHA256Tag256Type // invalid signing key
p.ctx.keyAgreementType = kms.X25519ECDHKWType

err = p.ctx.createNewKeyAndVM(didDoc)
require.EqualError(t, err, "createSigningVM: createAndExportPubKeyBytes: failed to export new public key bytes: "+
"exportPubKeyBytes: failed to export marshalled key: exportPubKeyBytes: failed to get public keyset "+
"handle: keyset.Handle: keyset.Handle: keyset contains a non-private key")
require.Empty(t, didDoc.VerificationMethod)
require.Empty(t, didDoc.KeyAgreement)
})
}

func TestCreateSigningVM(t *testing.T) {
k := newKMS(t, mockstorage.NewMockStoreProvider())

p, err := New(&protocol.MockProvider{
ServiceMap: map[string]interface{}{
mediator.Coordination: &mockroute.MockMediatorSvc{},
},
CustomKMS: k,
})
require.NoError(t, err)

t.Run("createSigningVM success", func(t *testing.T) {
p.ctx.keyType = kms.ED25519

svm, err := p.ctx.createSigningVM()
require.NoError(t, err)
require.NotEmpty(t, svm)
})

t.Run("createSigningVM with empty vmType", func(t *testing.T) {
p.ctx.keyType = ""

svm, err := p.ctx.createSigningVM()
require.EqualError(t, err, "createSigningVM: createAndExportPubKeyBytes: failed to create new key: "+
"failed to create new key, missing key type")
require.Empty(t, svm)
})

t.Run("createSigningVM with unsupported keyType", func(t *testing.T) {
p.ctx.keyType = kms.X25519ECDHKW

svm, err := p.ctx.createSigningVM()
require.EqualError(t, err, "createSigningVM: unsupported verification method: 'X25519KeyAgreementKey2019'")
require.Empty(t, svm)
})
}

func TestCreateEncryptionVM(t *testing.T) {
k := newKMS(t, mockstorage.NewMockStoreProvider())

p, err := New(&protocol.MockProvider{
ServiceMap: map[string]interface{}{
mediator.Coordination: &mockroute.MockMediatorSvc{},
},
CustomKMS: k,
})
require.NoError(t, err)

t.Run("createEncryptionVM success", func(t *testing.T) {
p.ctx.keyAgreementType = kms.X25519ECDHKW

evm, err := p.ctx.createEncryptionVM()
require.NoError(t, err)
require.NotEmpty(t, evm)
})

t.Run("createEncryptionVM with empty keyAgreementType", func(t *testing.T) {
p.ctx.keyAgreementType = ""

evm, err := p.ctx.createEncryptionVM()
require.EqualError(t, err, "createEncryptionVM: createAndExportPubKeyBytes: failed to create new key: "+
"failed to create new key, missing key type")
require.Empty(t, evm)
})

t.Run("createEncryptionVM with unsupported keyType", func(t *testing.T) {
p.ctx.keyAgreementType = kms.ED25519Type

evm, err := p.ctx.createEncryptionVM()
require.EqualError(t, err, "unsupported verification method for KeyAgreement: 'Ed25519VerificationKey2018'")
require.Empty(t, evm)
})
}
78 changes: 78 additions & 0 deletions pkg/didcomm/protocol/didconnection/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright Avast Software. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package didconnection

import (
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
)

// Invitation model
//
// Invitation defines Connection protocol invitation message
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0160-connection-protocol#0-invitation-to-connect
type Invitation struct {
// the Type of the connection invitation
Type string `json:"@type,omitempty"`

// the ID of the connection invitation
ID string `json:"@id,omitempty"`

// the Label of the connection invitation
Label string `json:"label,omitempty"`

// the RecipientKeys for the connection invitation
RecipientKeys []string `json:"recipientKeys,omitempty"`

// the Service endpoint of the connection invitation
ServiceEndpoint string `json:"serviceEndpoint,omitempty"`

// the RoutingKeys of the connection invitation
RoutingKeys []string `json:"routingKeys,omitempty"`

// the DID of the connection invitation
DID string `json:"did,omitempty"`
}

// Request defines a2a Connection request
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0160-connection-protocol#1-connection-request
type Request struct {
Type string `json:"@type,omitempty"`
ID string `json:"@id,omitempty"`
Label string `json:"label"`
Thread *decorator.Thread `json:"~thread,omitempty"`
Connection *Connection `json:"connection,omitempty"`
}

// Response defines a2a Connection response
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0160-connection-protocol#2-connection-response
type Response struct {
Type string `json:"@type,omitempty"`
ID string `json:"@id,omitempty"`
ConnectionSignature *ConnectionSignature `json:"connection~sig,omitempty"`
Thread *decorator.Thread `json:"~thread,omitempty"`
PleaseAck *PleaseAck `json:"~please_ack,omitempty"`
}

// ConnectionSignature connection signature.
type ConnectionSignature struct {
Type string `json:"@type,omitempty"`
Signature string `json:"signature,omitempty"`
SignedData string `json:"sig_data,omitempty"`
SignVerKey string `json:"signers,omitempty"`
}

// PleaseAck connection response accepted acknowledgement.
type PleaseAck struct {
On []string `json:"on,omitempty"`
}

// Connection defines connection body of connection request.
type Connection struct {
DID string `json:"DID,omitempty"`
DIDDoc *did.Doc `json:"DIDDoc,omitempty"`
}

0 comments on commit 3d01a16

Please sign in to comment.