This repository has been archived by the owner on Mar 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Connection protocol (RFC-0160) DIDCommV1 implementation (partia…
…lly) Add implementation only for protocol's package Signed-off-by: Abdulbois <abdulbois.tursunov@avast.com>
- Loading branch information
Showing
9 changed files
with
5,768 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"` | ||
} |
Oops, something went wrong.