Skip to content

Commit

Permalink
FAB-16286 IT with forged RWset for LSCC IP failure
Browse files Browse the repository at this point in the history
Although the legacy lifecycle is deprecated, the fundamental security
guarantees of the original lifecycle have never had a proper automated
test.  Because so much has changed in the chaincode execution and
lifecycle components, it seems especially prudent to add an automated
test before the v2.0 release.

Unfortunately, automating the instantiation policy checks is not
straightforward, as the peer's LSCC chaincode deliberately checks
instantiation policy and refuses to create non-conformant transactions.

This CR introduces a new instantiation policy integration test, forming
custom self-endorsed transactions with custom (forged) RW sets.  It
performs:

1) A failed lscc deploy because IP is not satisfied
2) A legitimate lscc deploy when IP is satisfied
3) A failed lscc upgrade because IP is not satisfied
4) A legitimate lscc upgrade when IP is satisfied

Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
Change-Id: I14846f8d22fbc67c9bbaa98d551eedf533586153
  • Loading branch information
Jason Yellick committed Nov 15, 2019
1 parent 7b19faa commit a0ab031
Showing 1 changed file with 365 additions and 0 deletions.
365 changes: 365 additions & 0 deletions integration/e2e/instantiation_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
/*
Copyright IBM Corp All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package e2e

import (
"crypto/sha256"
"encoding/hex"
"io/ioutil"
"os"
"syscall"
"time"

docker "github.com/fsouza/go-dockerclient"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/ledger/rwset"
"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/cmd/common/signer"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/integration/nwo"
"github.com/hyperledger/fabric/integration/nwo/commands"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
"github.com/tedsuo/ifrit"
)

var _ = Describe("InstantiationPolicy", func() {
var (
testDir string
client *docker.Client
network *nwo.Network
process ifrit.Process
)

BeforeEach(func() {
var err error
testDir, err = ioutil.TempDir("", "e2e")
Expect(err).NotTo(HaveOccurred())

client, err = docker.NewClientFromEnv()
Expect(err).NotTo(HaveOccurred())
})

AfterEach(func() {
if process != nil {
process.Signal(syscall.SIGTERM)
Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
}
if network != nil {
network.Cleanup()
}
os.RemoveAll(testDir)
})

Describe("single node etcdraft network with single peer", func() {
BeforeEach(func() {
config := nwo.MinimalRaft()
config.Profiles[1].Organizations = []string{"Org1", "Org2"}
network = nwo.New(config, testDir, client, StartPort(), components)
network.GenerateConfigTree()
network.Bootstrap()

networkRunner := network.NetworkGroupRunner()
process = ifrit.Invoke(networkRunner)
Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
})

It("honors the instantiation policy", func() {
orderer := network.Orderer("orderer")

org1Peer := network.Peer("Org1", "peer0")
org2Peer := network.Peer("Org2", "peer0")

network.CreateAndJoinChannel(orderer, "testchannel")

By("attempting to deploy with an unsatisfied instantiation policy")

goodDeploy := LSCCOperation{
Operation: "deploy",
ChannelID: "testchannel",
Name: "fakecc",
Version: "badip",
InstantiationOrgs: []*nwo.Organization{
network.Organization("Org2"),
},
}

nwo.Broadcast(network, orderer, goodDeploy.Tx(PeerSigner(network, org1Peer)))

nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 2, org1Peer)

Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: badip"))

By("attempting to deploy with a satisfied instantiation policy")

badDeploy := LSCCOperation{
Operation: "deploy",
ChannelID: "testchannel",
Name: "fakecc",
Version: "goodip",
InstantiationOrgs: []*nwo.Organization{
network.Organization("Org1"),
},
}

nwo.Broadcast(network, orderer, badDeploy.Tx(PeerSigner(network, org1Peer)))

nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 3, org1Peer)
Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: goodip"))

By("upgrading without satisfying the previous instantiation policy")

badUpgrade := LSCCOperation{
Operation: "upgrade",
ChannelID: "testchannel",
Name: "fakecc",
Version: "wrongsubmitter",
InstantiationOrgs: []*nwo.Organization{
network.Organization("Org2"),
},
}

nwo.Broadcast(network, orderer, badUpgrade.Tx(PeerSigner(network, org2Peer)))

nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 4, org1Peer)
Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: wrongsubmitter"))

By("upgrading while satisfying the previous instantiation policy")

goodUpgrade := LSCCOperation{
Operation: "upgrade",
ChannelID: "testchannel",
Name: "fakecc",
Version: "rightsubmitter",
InstantiationOrgs: []*nwo.Organization{
network.Organization("Org1"),
},
}

nwo.Broadcast(network, orderer, goodUpgrade.Tx(PeerSigner(network, org1Peer)))

nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 5, org1Peer)
Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: rightsubmitter"))
})
})

})

func PeerSigner(n *nwo.Network, p *nwo.Peer) *signer.Signer {
conf := signer.Config{
MSPID: n.Organization(p.Organization).MSPID,
IdentityPath: n.PeerUserCert(p, "Admin"),
KeyPath: n.PeerUserKey(p, "Admin"),
}

signer, err := signer.NewSigner(conf)
Expect(err).NotTo(HaveOccurred())

return signer
}

func MarshalOrPanic(msg proto.Message) []byte {
b, err := proto.Marshal(msg)
if err != nil {
panic(err)
}
return b
}

func ListInstantiatedLegacy(n *nwo.Network, p *nwo.Peer, channel string) *gbytes.Buffer {
sess, err := n.PeerAdminSession(p, commands.ChaincodeListInstantiatedLegacy{
ChannelID: channel,
ClientAuth: n.ClientAuthRequired,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
return sess.Buffer()
}

type LSCCOperation struct {
Operation string
ChannelID string
Name string
Version string
InstantiationOrgs []*nwo.Organization
}

func (lo *LSCCOperation) Tx(signer *signer.Signer) *common.Envelope {
creatorBytes, err := signer.Serialize()
Expect(err).NotTo(HaveOccurred())

var instantiationMSPIDs []string
for _, org := range lo.InstantiationOrgs {
instantiationMSPIDs = append(instantiationMSPIDs, org.MSPID)
}
instantiationPolicy := cauthdsl.SignedByAnyMember(instantiationMSPIDs)

nonce, err := time.Now().MarshalBinary()
Expect(err).NotTo(HaveOccurred())

timestamp, err := ptypes.TimestampProto(time.Now().UTC())
Expect(err).NotTo(HaveOccurred())

hasher := sha256.New()
hasher.Write(nonce)
hasher.Write(creatorBytes)
txid := hex.EncodeToString(hasher.Sum(nil))

signatureHeaderBytes := MarshalOrPanic(&common.SignatureHeader{
Creator: creatorBytes,
Nonce: nonce,
})

channelHeaderBytes := MarshalOrPanic(&common.ChannelHeader{
ChannelId: lo.ChannelID,
Extension: MarshalOrPanic(&peer.ChaincodeHeaderExtension{
ChaincodeId: &peer.ChaincodeID{
Name: "lscc",
},
}),
Timestamp: timestamp,
TxId: txid,
Type: int32(common.HeaderType_ENDORSER_TRANSACTION),
})

chaincodeDataBytes := MarshalOrPanic(&peer.ChaincodeData{
Data: []byte("a-big-bunch-of-fakery"),
Id: []byte("a-friendly-fake-chaincode"),
Escc: "escc",
Vscc: "vscc",
Name: lo.Name,
Version: lo.Version,
InstantiationPolicy: instantiationPolicy,
Policy: nil, // EndorsementPolicy deliberately left nil
})

proposalPayloadBytes := MarshalOrPanic(&peer.ChaincodeProposalPayload{
Input: MarshalOrPanic(&peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Input: &peer.ChaincodeInput{
Args: [][]byte{
[]byte(lo.Operation),
[]byte(lo.ChannelID),
MarshalOrPanic(&peer.ChaincodeDeploymentSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
ChaincodeId: &peer.ChaincodeID{
Name: lo.Name,
Version: lo.Version,
},
Input: &peer.ChaincodeInput{
Args: [][]byte{[]byte("bogus-init-arg")},
},
Type: peer.ChaincodeSpec_GOLANG,
},
}),
{}, // Endorsement policy bytes deliberately empty
[]byte("escc"),
[]byte("vscc"),
},
},
Type: peer.ChaincodeSpec_GOLANG,
},
}),
})

propHash := sha256.New()
propHash.Write(channelHeaderBytes)
propHash.Write(signatureHeaderBytes)
propHash.Write(proposalPayloadBytes)
proposalHash := propHash.Sum(nil)[:]

proposalResponsePayloadBytes := MarshalOrPanic(&peer.ProposalResponsePayload{
ProposalHash: proposalHash,
Extension: MarshalOrPanic(&peer.ChaincodeAction{
ChaincodeId: &peer.ChaincodeID{
Name: "lscc",
Version: "syscc",
},
Events: MarshalOrPanic(&peer.ChaincodeEvent{
ChaincodeId: "lscc",
EventName: lo.Operation,
Payload: MarshalOrPanic(&peer.LifecycleEvent{
ChaincodeName: lo.Name,
}),
}),
Response: &peer.Response{
Payload: chaincodeDataBytes,
Status: 200,
},
Results: MarshalOrPanic(&rwset.TxReadWriteSet{
DataModel: rwset.TxReadWriteSet_KV,
NsRwset: []*rwset.NsReadWriteSet{
{
Namespace: "lscc",
Rwset: MarshalOrPanic(&kvrwset.KVRWSet{
Writes: []*kvrwset.KVWrite{
{
Key: lo.Name,
Value: chaincodeDataBytes,
},
},
}),
},
{
Namespace: lo.Name,
Rwset: MarshalOrPanic(&kvrwset.KVRWSet{
Writes: []*kvrwset.KVWrite{
{
Key: "bogus-key",
Value: []byte("bogus-value"),
},
},
}),
},
},
}),
}),
})

endorsementSignature, err := signer.Sign(append(proposalResponsePayloadBytes, creatorBytes...))
Expect(err).NotTo(HaveOccurred())

payloadBytes := MarshalOrPanic(&common.Payload{
Header: &common.Header{
ChannelHeader: channelHeaderBytes,
SignatureHeader: signatureHeaderBytes,
},
Data: MarshalOrPanic(&peer.Transaction{
Actions: []*peer.TransactionAction{
{
Header: signatureHeaderBytes,
Payload: MarshalOrPanic(&peer.ChaincodeActionPayload{
ChaincodeProposalPayload: proposalPayloadBytes,
Action: &peer.ChaincodeEndorsedAction{
ProposalResponsePayload: proposalResponsePayloadBytes,
Endorsements: []*peer.Endorsement{
{
Endorser: creatorBytes,
Signature: endorsementSignature,
},
},
},
}),
},
},
}),
})

envSignature, err := signer.Sign(payloadBytes)
Expect(err).NotTo(HaveOccurred())

return &common.Envelope{
Payload: payloadBytes,
Signature: envSignature,
}
}

0 comments on commit a0ab031

Please sign in to comment.