Skip to content
Open
9 changes: 9 additions & 0 deletions utils/serialization/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@ func UnwrapEnvelope(message []byte) ([]byte, *common.ChannelHeader, error) {

// ParseEnvelope parse the envelope content.
func ParseEnvelope(envelope *common.Envelope) (*common.Payload, *common.ChannelHeader, error) {
if envelope == nil {
return nil, nil, errors.New("nil envelope")
}

payload, err := protoutil.UnmarshalPayload(envelope.Payload)
if err != nil {
return nil, nil, errors.Wrap(err, "error unmarshaling payload")
}

if payload.Header == nil { // Will panic if payload.Header is nil
return nil, nil, errors.New("nil payload header")
}

channelHdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return nil, nil, errors.Wrap(err, "error unmarshaling channel header")
Expand Down
110 changes: 110 additions & 0 deletions utils/serialization/envelope_wrapper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package serialization_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-x-common/protoutil"

"github.com/hyperledger/fabric-x-committer/utils/serialization"
"github.com/hyperledger/fabric-x-committer/utils/test"
)

// TestUnwrapEnvelopeBadInput tests UnwrapEnvelope function with invalid inputs.
func TestUnwrapEnvelopeBadInput(t *testing.T) {
t.Parallel()
t.Run("Not an envelope", func(t *testing.T) {
t.Parallel()
_, _, err := serialization.UnwrapEnvelope([]byte("invalid input"))
require.Error(t, err)
})

t.Run("OK Header with an invalid payload", func(t *testing.T) {
t.Parallel()
envelope := &common.Envelope{
Payload: []byte("not-a-payload"),
}

envelopeBytes := protoutil.MarshalOrPanic(envelope)

_, _, err := serialization.UnwrapEnvelope(envelopeBytes)
require.Error(t, err)
})

t.Run("OK Payload with a nil Header", func(t *testing.T) {
t.Parallel()
payload := &common.Payload{
Header: nil,
Data: []byte("some data"),
}

payloadBytes := protoutil.MarshalOrPanic(payload)
envelope := &common.Envelope{
Payload: payloadBytes,
}

envelopeBytes := protoutil.MarshalOrPanic(envelope)

_, _, err := serialization.UnwrapEnvelope(envelopeBytes)
require.Error(t, err)
})

t.Run("OK payload but invalid ChannelHeader", func(t *testing.T) {
t.Parallel()
header := &common.Header{
ChannelHeader: []byte("not-a-channel-header"),
}
payload := &common.Payload{
Header: header,
Data: []byte("some data"),
}
Comment on lines +61 to +67
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
header := &common.Header{
ChannelHeader: []byte("not-a-channel-header"),
}
payload := &common.Payload{
Header: header,
Data: []byte("some data"),
}
payload := &common.Payload{
Header: &common.Header{
ChannelHeader: []byte("not-a-channel-header"),
},
Data: []byte("some data"),
}

payloadBytes := protoutil.MarshalOrPanic(payload)

envelope := &common.Envelope{
Payload: payloadBytes,
}
envelopeBytes := protoutil.MarshalOrPanic(envelope)

_, _, err := serialization.UnwrapEnvelope(envelopeBytes)
require.Error(t, err)
})
}

// TestUnwrapEnvelopeGoodInput Tests properly wrapped envelope is unwrapped correctly.
func TestUnwrapEnvelopeGoodInput(t *testing.T) {
t.Parallel()
originalPayload := []byte("test payload")

originalChannelHeader := &common.ChannelHeader{
ChannelId: "test-channel",
}

originalHeader := &common.Header{
ChannelHeader: protoutil.MarshalOrPanic(originalChannelHeader),
}

// Wrap
wrappedEnvelope := protoutil.MarshalOrPanic(&common.Envelope{
Payload: protoutil.MarshalOrPanic(&common.Payload{
Header: originalHeader,
Data: originalPayload,
}),
})

// Unwrap
payload, channelHeader, err := serialization.UnwrapEnvelope(wrappedEnvelope)

// -Check 1- Check unwrap envelope has no error
require.NoError(t, err)

// -Check 2- Check we get the correct Payload & Header
require.Equal(t, originalPayload, payload)
test.RequireProtoEqual(t, originalChannelHeader, channelHeader)
}
8 changes: 8 additions & 0 deletions utils/signature/sigtest/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (

// SerializeVerificationKey encodes a ECDSA public key into a PEM file.
func SerializeVerificationKey(key *ecdsa.PublicKey) ([]byte, error) {
if key == nil {
return nil, errors.New("key is nil")
}
x509encodedPub, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return nil, errors.Wrap(err, "cannot serialize public key")
Expand All @@ -32,10 +35,15 @@ func SerializeVerificationKey(key *ecdsa.PublicKey) ([]byte, error) {

// SerializeSigningKey encodes a ECDSA private key into a PEM file.
func SerializeSigningKey(key *ecdsa.PrivateKey) ([]byte, error) {
if key == nil {
return nil, errors.New("key is nil")
}
Comment on lines +38 to +40
Copy link
Contributor

Choose a reason for hiding this comment

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

major: Also add this check to SerializeVerificationKey()


x509encodedPri, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, errors.Wrap(err, "cannot serialize private key")
}

Copy link
Contributor

Choose a reason for hiding this comment

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

super nit: redundant change

return pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: x509encodedPri,
Expand Down
90 changes: 90 additions & 0 deletions utils/signature/sigtest/crypto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package sigtest

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"

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

func TestSerializeVerificationKey(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest merging both tests (verification and serialization) and covering all test cases (including errors).

t.Parallel()
tests := []struct {
name string
curve elliptic.Curve
wantErr bool
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: Remove

}{
{
name: "P256",
curve: elliptic.P256(),
},
{
name: "P384",
curve: elliptic.P384(),
},
{
name: "P224",
curve: elliptic.P224(),
},
{
name: "P521",
curve: elliptic.P521(),
},

// {
// // TODO: find an invalid example?
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: Remove

// name: "Invalid input",
// curve: elliptic.(),
// wantErr: true,
// },
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
privKey, err := ecdsa.GenerateKey(tt.curve, rand.Reader)
require.NoError(t, err)

_, err = SerializeVerificationKey(&privKey.PublicKey)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

func TestSerializeAndParseSigningKey(t *testing.T) {
t.Parallel()

t.Run("Key Empty", func(t *testing.T) {
t.Parallel()
emptyKey := &ecdsa.PrivateKey{}
_, err := SerializeSigningKey(emptyKey)
require.Error(t, err)
})
t.Run("Key is nil", func(t *testing.T) {
t.Parallel()
_, err := SerializeSigningKey(nil)
require.Error(t, err)
})

t.Run("Key OK", func(t *testing.T) {
t.Parallel()
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
key, err := SerializeSigningKey(privateKey)
require.NotNil(t, key)
require.NoError(t, err)
_, err = ParseSigningKey(key)
require.NoError(t, err)
})
}