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

feat: Packager now holds multiple Packers for runtime format selection #717

Merged
merged 2 commits into from
Nov 8, 2019

Conversation

Moopli
Copy link
Contributor

@Moopli Moopli commented Nov 6, 2019

Renames Crypters to Packers, and gives the Packager a list of Packers, for all formats the agent supports (elements are configurable and injectable by client), and a reference to the default Packer (also configurable and injectable) to use for encryption.

By default, the JWE and Legacy Packers are both enabled, available for decrypting messages.

Fixes #160
Fixes #273

@Moopli
Copy link
Contributor Author

Moopli commented Nov 6, 2019

Note: need to reconcile urlencoding vs rawurlencoding, or allow Packager.getEncodingType() to test both encodings, since JWE uses raw and legacy doesn't.

Edit: done

@@ -26,7 +52,7 @@ type Packager interface {
// []byte: The packed message
//
// error: error
PackMessage(envelope *Envelope) ([]byte, error)
PackMessage(envelope *packer.Envelope) ([]byte, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a package called envelope but this signature has an Envelope defined in packer?

What is the intended purpose of the Envelope package?

Copy link
Contributor Author

@Moopli Moopli Nov 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could swap the package names - I needed to pull Envelope out of envelope to avoid an import cycle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed envelope (containing just the packager) to packager and packer (previously crypto) to envelope

@Moopli Moopli force-pushed the 160 branch 2 times, most recently from fbe8ee2 to ad2316c Compare November 7, 2019 15:59
@codecov
Copy link

codecov bot commented Nov 7, 2019

Codecov Report

Merging #717 into master will increase coverage by 0.39%.
The diff coverage is 97.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #717      +/-   ##
==========================================
+ Coverage    90.6%   90.99%   +0.39%     
==========================================
  Files          82       82              
  Lines        4777     4840      +63     
==========================================
+ Hits         4328     4404      +76     
+ Misses        252      240      -12     
+ Partials      197      196       -1
Impacted Files Coverage Δ
pkg/didcomm/transport/http/inbound.go 81.25% <ø> (ø) ⬆️
pkg/kms/kms.go 86.44% <ø> (ø) ⬆️
pkg/didcomm/packer/jwe/authcrypt/common.go 100% <ø> (ø)
pkg/framework/aries/default.go 88.23% <100%> (+1.56%) ⬆️
pkg/didcomm/packer/jwe/authcrypt/encrypt_jwk.go 80.32% <100%> (ø)
pkg/didcomm/packer/jwe/authcrypt/pack.go 85.4% <100%> (ø)
pkg/didcomm/packer/jwe/authcrypt/decrypt_jwk.go 75.75% <100%> (ø)
pkg/didcomm/packer/legacy/authcrypt/unpack.go 92.68% <100%> (ø)
pkg/framework/context/context.go 97.91% <100%> (+9.02%) ⬆️
pkg/didcomm/packer/jwe/authcrypt/authcrypt.go 100% <100%> (ø)
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 12031e6...4ada8ce. Read the comment docs.

@@ -79,7 +111,7 @@ func TestBaseKMSInPackager_UnpackMessage(t *testing.T) {
// It should fail since Recipient keys are not found in the KMS
_, err = packager.UnpackMessage(packMsg)
require.Error(t, err)
require.EqualError(t, err, "failed from decrypt: failed to decrypt message: key not found")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove the specific error check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the message is partially decoded before the Packer unpacks it, the failure case is different, and occurs in getEncodingType()

pkg/didcomm/envelope/package_test.go Outdated Show resolved Hide resolved
require.NoError(t, err)
_, err = packager.UnpackMessage(nil)
require.Error(t, err)
require.EqualError(t, err, "failed from decrypt: failed to decrypt message: unexpected end of JSON input")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove expected detailed error.. this error message should be specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another case where the error now happens inside getEncodingType()

pkg/didcomm/packer/jwe/authcrypt/authcrypt.go Show resolved Hide resolved
pkg/didcomm/packer/jwe/authcrypt/pack.go Show resolved Hide resolved
pkg/didcomm/packer/jwe/authcrypt/pack.go Show resolved Hide resolved
// Package envelope manages the handling of DIDComm raw messages in JWE compliant envelopes.
// The aim of this package is build these envelopes and parse them. They are mainly used as
// wire-level wrappers of 'payloads' used in DID Exchange flows.

// PackagerCreator method to create new outbound dispatcher service
type PackagerCreator func(prov Provider) (Packager, error)
type PackagerCreator func(prov PackerProvider) (Packager, error)

// Packager provide methods to pack and unpack msg
type Packager interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that Crypter is renamed to Packer, this Packager interface becomes an extra layer that we can skim.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now has the purpose of selecting which pack/unpack algorithm to use, allowing the agent to support communicating using multiple message formats for compatibility - so it's not a layer we can pull out.

pkg/framework/context/context.go Show resolved Hide resolved
pkg/framework/context/context.go Outdated Show resolved Hide resolved
@@ -6,12 +6,16 @@ SPDX-License-Identifier: Apache-2.0

package envelope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct to observe that this package is only needed by the framework (supplying the injectable API)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In talking with @Baha-sk we've decided to go the other way - Packager does not need to be injectable because pack/unpack format configuration is done through WithDefaultPacker() and WithPackers(). Where do you think Packager should move? Without Packager injection we only need the interface for mocking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packager will hold the list of Packers including a default one, it will also decide which one to use based on the envelope type. Users don't need to inject it as it's a utility interface that's required by the framework.

Users can inject Packers and a DefaultPacker to support their own implementation.

@Moopli Moopli force-pushed the 160 branch 6 times, most recently from a1fa70c to c29c5d1 Compare November 7, 2019 23:58
fqutishat
fqutishat previously approved these changes Nov 8, 2019
@fqutishat fqutishat dismissed their stale review November 8, 2019 15:32

by mistake

@Moopli Moopli requested a review from fqutishat November 8, 2019 15:58
@@ -66,7 +66,7 @@ func TestNilEncryptSenderJwk(t *testing.T) {
require.Empty(t, l)
require.Empty(t, m)

pld, err := crypter.Encrypt([]byte(""), someKey[:], [][]byte{someKey[:]})
pld, err := crypter.Pack([]byte(""), someKey[:], [][]byte{someKey[:]})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the variable name still crypter ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go over all other uses of 'crypter' too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done now.

"github.com/hyperledger/aries-framework-go/pkg/didcomm/envelope"
)

// Package packager manages the handling of DIDComm raw messages in JWE compliant envelopes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the top level Packager interface is agnostic of JWE?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think I should say instead for "DID Exchange flows"? Since this format is used for all protocol communications.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see "DID Exchange" - I saw "DIDComm".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few lines down.

package packager

import (
"github.com/hyperledger/aries-framework-go/pkg/didcomm/envelope"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for a separate envelope package? Does it now only have a struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Envelope struct, Packer interface, and Packer Creator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's separate mainly to avoid an import cycle - which iirc was a cycle only because of tests, so I'll see if I can fix that.

Copy link
Contributor Author

@Moopli Moopli Nov 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I should keep the packages separate, but I can rename envelope to packer - I'd named it packer earlier. I'll work out how to put the Envelope struct inside pkg/didcomm/packager without an import cycle, which I believe only came about due to tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've resolved this by pulling Envelope out into pkg/didcomm/common/transport

}

// BasePackager is the basic implementation of Packager
type BasePackager struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for the prefix Base ?

Copy link
Contributor Author

@Moopli Moopli Nov 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since it's not injectable we don't expect others.
Will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then again there is the distinction between this and the interface (the interface is Packager), since we also have a mock packager. The interface is what everything consumes.

I could make the struct definition private.

Copy link
Contributor

@troyronda troyronda Nov 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interface and struct shouldn't be in the same package.

The struct needs to be exported since it is returned by New (hiding these structs is annoying to the developer).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can return the interface instead of a pointer to the struct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where should I move the interface? It's consumed all over the codebase, particularly in tests which usually use the mock packager.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've resolved this by pulling the Packager interface out into pkg/didcomm/common/transport

@@ -208,7 +211,8 @@ func (a *Aries) Context() (*context.Provider, error) {
context.WithInboundTransportEndpoint(a.inboundTransport.Endpoint()),
context.WithStorageProvider(a.storeProvider),
context.WithTransientStorageProvider(a.transientStoreProvider),
context.WithCrypter(a.crypter),
context.WithDefaultPacker(a.packer),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I assume that a.packer must also be in the a.packers list in L215?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packager handles that - if the default packer is not in the list, it's added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically - each Packer defines an encoding type string (returned by Packer.EncodingType()) which is used to key into the packer map. So if a.packer's type is the type of one of the elements of a.packers, then it's assumed to be in the list already.

@@ -92,11 +90,20 @@ func TestFramework(t *testing.T) {
WithKMS(func(ctx api.Provider) (api.CloseableKMS, error) {
return &mockkms.CloseableKMS{SignMessageValue: []byte("mockValue")}, nil
}),
WithCrypter(func(ctx crypto.Provider) (crypto.Crypter, error) {
return &didcomm.MockAuthCrypt{EncryptValue: nil}, nil
WithDefaultPacker(func(ctx envelope.Provider) (envelope.Packer, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really "Default" or is it the outbound packer?

I mean the framework puts defaults in for the Packers list too :).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll rename.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the outbound packer isn't specified, which packer from s.packers is used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though it is also a default in the sense that it's included in the list of inbound packers, since it makes little sense to send messages using a format you're unwilling to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the outbound packer isn't specified, then the framework falls back to initializing the default, which isn't inside s.packers. packager.New requires a default packager to be provided. I'll add an error check in the constructor if the value is nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of "Default" or "Outbound" it is now just "Packer", since it's used for both in/outbound.
The packer list is now InboundPackers, since they're used inbound only right now. #725 is for making those packers usable outbound as well, by remembering the format to use for a particular connection.

pkg/didcomm/packager/api.go Outdated Show resolved Hide resolved
Packager can select Packer for decryption based on the Typ field of
the input message.

Fixes hyperledger-archives#273

Signed-off-by: Filip Burlacu <[email protected]>
@fqutishat fqutishat merged commit f4b7a3c into hyperledger-archives:master Nov 8, 2019
// Packager is the basic implementation of Packager
type Packager struct {
packer packer.Packer
inboundPackers map[string]packer.Packer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why inboundPackers? it's just a list of Packers

a Packer does pack/unpack, regardless of the direction of the message.

any reason for the direction word in the variable inoundPackers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The packer seems to be the only argument doing both pack and unpack.

The inbound packers seems to be unpack only. Perhaps a better name would be unpackers :).

Copy link
Contributor

@baha-ai baha-ai Nov 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the map of packers should not be meant for inbound only, during a connection, if Agent A talks with B in JWE compliant format and Agent C in legacy packer format, then Agent A should use the right outbound Packer for each connection (not just for replying to incoming messages). The the map of Packers is not meant to be for inbound only (Unpack()) calls.

also, packer field above was agreed to be the DefaultPacker which is meant to be used for new connections. For pre-existing/saved connections, the agent should store the corresponding Packer to be used for all subsequent calls (outbound and inbound).

so the word inbound in inboundPacker does not correctly represent the map of Packers as they should not be used for inbound calls only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default doesn’t seem like a good prefix -if we use that then there is a default default packer set by the framework package if the option is not provided.

require.NoError(t, err)

mockedProviders := &mockProvider{
storage: nil,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to set nil variables

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

storage *mockstorage.MockStoreProvider
kms kms.KeyManager
packers []packer.Packer
outboundPacker packer.Packer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure outboundPacker has a meaning, it's just a Packer that should be able to do Pack/Unpack operations.

why add the direction in the variable name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See reply below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should combine these options into one to reduce confusion (and API surface).
#764.

@Moopli Moopli deleted the 160 branch November 14, 2019 21:02
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Create a higher-level Crypter to switch encryption based on key type/algorithm pkg/crypto is misleading
4 participants