Skip to content

Commit

Permalink
Only proto.Marshal ConfigUpdate once to ensure valid sigs
Browse files Browse the repository at this point in the history
Due to the non-deterministic nature of proto.Marshal, the configtx library
should marshal the config update once and pass the marshaled bytes around
for signatures.

FAB-17959

Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
  • Loading branch information
wlahti authored and ale-linux committed Jun 8, 2020
1 parent c1d76dc commit 2fa88a5
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 49 deletions.
47 changes: 29 additions & 18 deletions configtx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,34 +103,42 @@ func (c *ConfigTx) UpdatedConfig() *cb.Config {
return c.updated
}

// ComputeUpdate computes the ConfigUpdate from a base and modified config transaction.
func (c *ConfigTx) ComputeUpdate(channelID string) (*cb.ConfigUpdate, error) {
// ComputeMarshaledUpdate computes the ConfigUpdate from a base and modified
// config transaction and returns the marshaled bytes.
func (c *ConfigTx) ComputeMarshaledUpdate(channelID string) ([]byte, error) {
if channelID == "" {
return nil, errors.New("channel ID is required")
}

updt, err := computeConfigUpdate(c.original, c.updated)
update, err := computeConfigUpdate(c.original, c.updated)
if err != nil {
return nil, fmt.Errorf("failed to compute update: %v", err)
}

updt.ChannelId = channelID

return updt, nil
}
update.ChannelId = channelID

// NewEnvelope creates an envelope with the provided config update and config signatures.
func NewEnvelope(c *cb.ConfigUpdate, signatures ...*cb.ConfigSignature) (*cb.Envelope, error) {
cBytes, err := proto.Marshal(c)
marshaledUpdate, err := proto.Marshal(update)
if err != nil {
return nil, fmt.Errorf("marshaling config update: %v", err)
}

return marshaledUpdate, nil
}

// NewEnvelope creates an envelope with the provided marshaled config update
// and config signatures.
func NewEnvelope(marshaledUpdate []byte, signatures ...*cb.ConfigSignature) (*cb.Envelope, error) {
configUpdateEnvelope := &cb.ConfigUpdateEnvelope{
ConfigUpdate: cBytes,
ConfigUpdate: marshaledUpdate,
Signatures: signatures,
}

c := &cb.ConfigUpdate{}
err := proto.Unmarshal(marshaledUpdate, c)
if err != nil {
return nil, fmt.Errorf("unmarshaling config update: %v", err)
}

envelope, err := newEnvelope(cb.HeaderType_CONFIG_UPDATE, c.ChannelId, configUpdateEnvelope)
if err != nil {
return nil, err
Expand All @@ -139,11 +147,10 @@ func NewEnvelope(c *cb.ConfigUpdate, signatures ...*cb.ConfigSignature) (*cb.Env
return envelope, nil
}

// NewCreateChannelTx creates a create channel config update transaction using the
// provided application channel configuration.
func NewCreateChannelTx(channelConfig Channel, channelID string) (*cb.ConfigUpdate, error) {
var err error

// NewMarshaledCreateChannelTx creates a create channel config update
// transaction using the provided application channel configuration and returns
// the marshaled bytes.
func NewMarshaledCreateChannelTx(channelConfig Channel, channelID string) ([]byte, error) {
if channelID == "" {
return nil, errors.New("profile's channel ID is required")
}
Expand All @@ -153,12 +160,16 @@ func NewCreateChannelTx(channelConfig Channel, channelID string) (*cb.ConfigUpda
return nil, fmt.Errorf("creating default config template: %v", err)
}

configUpdate, err := newChannelCreateConfigUpdate(channelID, channelConfig, ct)
update, err := newChannelCreateConfigUpdate(channelID, channelConfig, ct)
if err != nil {
return nil, fmt.Errorf("creating channel create config update: %v", err)
}

return configUpdate, nil
marshaledUpdate, err := proto.Marshal(update)
if err != nil {
return nil, fmt.Errorf("marshaling config update: %v", err)
}
return marshaledUpdate, nil
}

// NewSystemChannelGenesisBlock creates a genesis block using the provided consortiums and orderer
Expand Down
35 changes: 19 additions & 16 deletions configtx/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ func TestNewCreateChannelTx(t *testing.T) {
profile := baseProfile(t)

// creating a create channel transaction
createChannelTx, err := NewCreateChannelTx(profile, "testchannel")
marshaledCreateChannelTx, err := NewMarshaledCreateChannelTx(profile, "testchannel")
gt.Expect(err).NotTo(HaveOccurred())
envelope, err := NewEnvelope(createChannelTx)
envelope, err := NewEnvelope(marshaledCreateChannelTx)
gt.Expect(err).NotTo(HaveOccurred())
gt.Expect(envelope).ToNot(BeNil())

Expand Down Expand Up @@ -415,8 +415,8 @@ func TestNewCreateChannelTxFailure(t *testing.T) {

profile := tt.profileMod()

createChannelTx, err := NewCreateChannelTx(profile, tt.channelID)
gt.Expect(createChannelTx).To(BeNil())
marshaledCreateChannelTx, err := NewMarshaledCreateChannelTx(profile, tt.channelID)
gt.Expect(marshaledCreateChannelTx).To(BeNil())
gt.Expect(err).To(MatchError(tt.err))
})
}
Expand Down Expand Up @@ -1147,14 +1147,14 @@ func TestNewEnvelopeFailures(t *testing.T) {
t.Parallel()

tests := []struct {
spec string
configUpdate *cb.ConfigUpdate
expectedErr string
spec string
marshaledUpdate []byte
expectedErr string
}{
{
spec: "when no config update is provided",
configUpdate: nil,
expectedErr: "marshaling config update: proto: Marshal called with nil",
spec: "when the marshaled config update isn't a config update",
marshaledUpdate: []byte("not-a-config-update"),
expectedErr: "unmarshaling config update: proto: can't skip unknown wire type 6",
},
}

Expand All @@ -1164,14 +1164,14 @@ func TestNewEnvelopeFailures(t *testing.T) {
t.Parallel()
gt := NewGomegaWithT(t)

env, err := NewEnvelope(tc.configUpdate)
env, err := NewEnvelope(tc.marshaledUpdate)
gt.Expect(err).To(MatchError(tc.expectedErr))
gt.Expect(env).To(BeNil())
})
}
}

func TestComputeUpdate(t *testing.T) {
func TestComputeMarshaledUpdate(t *testing.T) {
t.Parallel()
gt := NewGomegaWithT(t)

Expand Down Expand Up @@ -1228,9 +1228,12 @@ func TestComputeUpdate(t *testing.T) {
WriteSet: expectedWriteSet,
}

configUpdate, err := c.ComputeUpdate(channelID)
marshaledUpdate, err := c.ComputeMarshaledUpdate(channelID)
gt.Expect(err).NotTo(HaveOccurred())
gt.Expect(configUpdate).To(Equal(&expectedConfig))
configUpdate := &cb.ConfigUpdate{}
err = proto.Unmarshal(marshaledUpdate, configUpdate)
gt.Expect(err).NotTo(HaveOccurred())
gt.Expect(proto.Equal(configUpdate, &expectedConfig)).To(BeTrue())
}

func TestComputeUpdateFailures(t *testing.T) {
Expand Down Expand Up @@ -1264,9 +1267,9 @@ func TestComputeUpdateFailures(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
gt := NewGomegaWithT(t)
configUpdate, err := c.ComputeUpdate(test.channelID)
marshaledUpdate, err := c.ComputeMarshaledUpdate(test.channelID)
gt.Expect(err).To(MatchError(test.expectedErr))
gt.Expect(configUpdate).To(BeNil())
gt.Expect(marshaledUpdate).To(BeNil())
})
}
}
Expand Down
10 changes: 5 additions & 5 deletions configtx/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func Example_basic() {
}

// Compute the delta
configUpdate, err := c.ComputeUpdate("testsyschannel")
marshaledUpdate, err := c.ComputeMarshaledUpdate("testsyschannel")
if err != nil {
panic(err)
}
Expand All @@ -115,7 +115,7 @@ func Example_basic() {

for _, si := range signingIdentities {
// Create a signature for the config update with the specified signer identity
configSignature, err := si.CreateConfigSignature(configUpdate)
configSignature, err := si.CreateConfigSignature(marshaledUpdate)
if err != nil {
panic(err)
}
Expand All @@ -124,7 +124,7 @@ func Example_basic() {
}

// Create the envelope with the list of config signatures
env, err := configtx.NewEnvelope(configUpdate, configSignatures...)
env, err := configtx.NewEnvelope(marshaledUpdate, configSignatures...)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -461,7 +461,7 @@ func ExampleNewSystemChannelGenesisBlock() {
}
}

func ExampleNewCreateChannelTx() {
func ExampleNewMarshaledCreateChannelTx() {
channel := configtx.Channel{
Consortium: "SampleConsortium",
Application: configtx.Application{
Expand Down Expand Up @@ -502,7 +502,7 @@ func ExampleNewCreateChannelTx() {
},
}

_, err := configtx.NewCreateChannelTx(channel, "testchannel")
_, err := configtx.NewMarshaledCreateChannelTx(channel, "testchannel")
if err != nil {
panic(err)
}
Expand Down
9 changes: 2 additions & 7 deletions configtx/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func toLowS(key ecdsa.PublicKey, sig ecdsaSignature) ecdsaSignature {

// CreateConfigSignature creates a config signature for the the given configuration
// update using the specified signing identity.
func (s *SigningIdentity) CreateConfigSignature(configUpdate *cb.ConfigUpdate) (*cb.ConfigSignature, error) {
func (s *SigningIdentity) CreateConfigSignature(marshaledUpdate []byte) (*cb.ConfigSignature, error) {
signatureHeader, err := s.signatureHeader()
if err != nil {
return nil, fmt.Errorf("creating signature header: %v", err)
Expand All @@ -105,14 +105,9 @@ func (s *SigningIdentity) CreateConfigSignature(configUpdate *cb.ConfigUpdate) (
SignatureHeader: header,
}

configUpdateBytes, err := proto.Marshal(configUpdate)
if err != nil {
return nil, fmt.Errorf("marshaling config update: %v", err)
}

configSignature.Signature, err = s.Sign(
rand.Reader,
concatenateBytes(configSignature.SignatureHeader, configUpdateBytes),
concatenateBytes(configSignature.SignatureHeader, marshaledUpdate),
nil,
)
if err != nil {
Expand Down
96 changes: 93 additions & 3 deletions configtx/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func TestCreateSignature(t *testing.T) {
MSPID: "test-msp",
}

configSignature, err := signingIdentity.CreateConfigSignature(&cb.ConfigUpdate{})
configSignature, err := signingIdentity.CreateConfigSignature([]byte("config"))
gt.Expect(err).NotTo(HaveOccurred())

sh, err := signingIdentity.signatureHeader()
Expand Down Expand Up @@ -137,11 +137,13 @@ func TestSignEnvelope(t *testing.T) {
configUpdate := &cb.ConfigUpdate{
ChannelId: "testchannel",
}
configSignature, err := signingIdentity.CreateConfigSignature(configUpdate)
marshaledUpdate, err := proto.Marshal(configUpdate)
gt.Expect(err).NotTo(HaveOccurred())
configSignature, err := signingIdentity.CreateConfigSignature(marshaledUpdate)
gt.Expect(err).NotTo(HaveOccurred())

// create signed config envelope
env, err := NewEnvelope(configUpdate, configSignature)
env, err := NewEnvelope(marshaledUpdate, configSignature)
gt.Expect(err).NotTo(HaveOccurred())
err = signingIdentity.SignEnvelope(env)
gt.Expect(err).NotTo(HaveOccurred())
Expand All @@ -164,6 +166,94 @@ func TestSignEnvelope(t *testing.T) {
gt.Expect(expectedSignatures.Signature).To(Equal(configSignature.Signature))
}

func TestSignEnvelopeWithAnchorPeers(t *testing.T) {
t.Parallel()
gt := NewGomegaWithT(t)

baseApplicationConf, _ := baseApplication(t)

applicationGroup, err := newApplicationGroup(baseApplicationConf)
gt.Expect(err).NotTo(HaveOccurred())

config := &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
ApplicationGroupKey: applicationGroup,
},
Values: map[string]*cb.ConfigValue{},
Policies: map[string]*cb.ConfigPolicy{},
},
}

c := New(config)

newOrg1AnchorPeer := Address{
Host: "host3",
Port: 123,
}

newOrg2AnchorPeer := Address{
Host: "host4",
Port: 123,
}

err = c.Application().Organization("Org1").AddAnchorPeer(newOrg1AnchorPeer)
gt.Expect(err).NotTo(HaveOccurred())

err = c.Application().Organization("Org2").AddAnchorPeer(newOrg2AnchorPeer)
gt.Expect(err).NotTo(HaveOccurred())

// create signingIdentity
cert, privateKey := generateCACertAndPrivateKey(t, "org1.example.com")
signingIdentity := SigningIdentity{
Certificate: cert,
PrivateKey: privateKey,
MSPID: "test-msp",
}

configUpdate, err := c.ComputeMarshaledUpdate("fake-channel")
gt.Expect(err).NotTo(HaveOccurred())

configSignature, err := signingIdentity.CreateConfigSignature(configUpdate)
gt.Expect(err).NotTo(HaveOccurred())

// create signed config envelope
env, err := NewEnvelope(configUpdate, configSignature)
gt.Expect(err).NotTo(HaveOccurred())
err = signingIdentity.SignEnvelope(env)
gt.Expect(err).NotTo(HaveOccurred())

// check envelope signature is valid
// env.Signature
sig := &ecdsaSignature{}
_, err = asn1.Unmarshal(env.Signature, sig)
gt.Expect(err).NotTo(HaveOccurred())
hash := sha256.New()
hash.Write(env.Payload)
digest := hash.Sum(nil)
valid := ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), digest, sig.R, sig.S)
gt.Expect(valid).To(BeTrue())

payload := &cb.Payload{}
err = proto.Unmarshal(env.Payload, payload)
gt.Expect(err).NotTo(HaveOccurred())

configUpdateEnvelope := &cb.ConfigUpdateEnvelope{}
err = proto.Unmarshal(payload.Data, configUpdateEnvelope)
gt.Expect(err).NotTo(HaveOccurred())
gt.Expect(configUpdateEnvelope.Signatures).To(HaveLen(1))

sig = &ecdsaSignature{}
configSig := configUpdateEnvelope.Signatures[0]
_, err = asn1.Unmarshal(configSig.Signature, sig)
gt.Expect(err).NotTo(HaveOccurred())
hash = sha256.New()
hash.Write(concatenateBytes(configSig.SignatureHeader, configUpdateEnvelope.ConfigUpdate))
digest = hash.Sum(nil)
valid = ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), digest, sig.R, sig.S)
gt.Expect(valid).To(BeTrue())
}

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

Expand Down

0 comments on commit 2fa88a5

Please sign in to comment.