Skip to content

Commit 4252a9f

Browse files
committed
[FAB-4252] Check duplicated identities in VSCC
Malicious users might try to duplicate proposal response endorsed by the same identity to pass endorsement policies. Each peer need to check such duplicated identities before policy evaluation phase. This patchset is to fix such potential issue in VSCC Change-Id: I05b29fafa818d7090373f0dd79b79be04974678d Signed-off-by: Xi Xue Jia <[email protected]>
1 parent 961ccab commit 4252a9f

File tree

2 files changed

+98
-25
lines changed

2 files changed

+98
-25
lines changed

core/scc/vscc/validator_onevalidsignature.go

+49-17
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,17 @@ import (
3333
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
3434
"github.com/hyperledger/fabric/protos/common"
3535
"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
36+
"github.com/hyperledger/fabric/protos/msp"
3637
pb "github.com/hyperledger/fabric/protos/peer"
3738
"github.com/hyperledger/fabric/protos/utils"
3839
)
3940

4041
var logger = flogging.MustGetLogger("vscc")
4142

43+
const (
44+
DUPLICATED_IDENTITY_ERROR = "Endorsement policy evaluation failure might be caused by duplicated identities"
45+
)
46+
4247
// ValidatorOneValidSignature implements the default transaction validation policy,
4348
// which is to check the correctness of the read-write set and the endorsement
4449
// signatures
@@ -62,7 +67,7 @@ func (vscc *ValidatorOneValidSignature) Init(stub shim.ChaincodeStubInterface) p
6267
// chaincodes to provide more sophisticated policy processing such as enabling
6368
// policy specification to be coded as a transaction of the chaincode and the client
6469
// selecting which policy to use for validation using parameter function
65-
// @return serialized Block of valid and invalid transactions indentified
70+
// @return serialized Block of valid and invalid transactions identified
6671
// Note that Peer calls this function with 3 arguments, where args[0] is the
6772
// function name, args[1] is the Envelope and args[2] is the validation policy
6873
func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
@@ -134,27 +139,19 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
134139
return shim.Error(err.Error())
135140
}
136141

137-
// this is the first part of the signed message
138-
prespBytes := cap.Action.ProposalResponsePayload
139-
140-
// build the signature set for the evaluation
141-
signatureSet := make([]*common.SignedData, len(cap.Action.Endorsements))
142-
143-
// loop through each of the endorsements and build the signature set
144-
for i, endorsement := range cap.Action.Endorsements {
145-
signatureSet[i] = &common.SignedData{
146-
// set the data that is signed; concatenation of proposal response bytes and endorser ID
147-
Data: append(prespBytes, endorsement.Endorser...),
148-
// set the identity that signs the message: it's the endorser
149-
Identity: endorsement.Endorser,
150-
// set the signature
151-
Signature: endorsement.Signature,
152-
}
142+
signatureSet, err := vscc.deduplicateIdentity(cap)
143+
if err != nil {
144+
return shim.Error(err.Error())
153145
}
154146

155147
// evaluate the signature set against the policy
156148
err = policy.Evaluate(signatureSet)
157149
if err != nil {
150+
logger.Warningf("Endorsement policy failure for transaction txid=%s, err: %s", chdr.GetTxId(), err.Error())
151+
if len(signatureSet) < len(cap.Action.Endorsements) {
152+
// Warning: duplicated identities exist, endorsement failure might be cause by this reason
153+
return shim.Error(DUPLICATED_IDENTITY_ERROR)
154+
}
158155
return shim.Error(fmt.Sprintf("VSCC error: policy evaluation failed, err %s", err))
159156
}
160157

@@ -425,3 +422,38 @@ func (vscc *ValidatorOneValidSignature) getInstantiatedCC(chid, ccid string) (cd
425422
exists = true
426423
return
427424
}
425+
426+
func (vscc *ValidatorOneValidSignature) deduplicateIdentity(cap *pb.ChaincodeActionPayload) ([]*common.SignedData, error) {
427+
// this is the first part of the signed message
428+
prespBytes := cap.Action.ProposalResponsePayload
429+
430+
// build the signature set for the evaluation
431+
signatureSet := []*common.SignedData{}
432+
signatureMap := make(map[string]struct{})
433+
// loop through each of the endorsements and build the signature set
434+
for _, endorsement := range cap.Action.Endorsements {
435+
//unmarshal endorser bytes
436+
serializedIdentity := &msp.SerializedIdentity{}
437+
if err := proto.Unmarshal(endorsement.Endorser, serializedIdentity); err != nil {
438+
logger.Errorf("Unmarshal endorser error: %s", err)
439+
return nil, fmt.Errorf("Unmarshal endorser error: %s", err)
440+
}
441+
identity := serializedIdentity.Mspid + string(serializedIdentity.IdBytes)
442+
if _, ok := signatureMap[identity]; ok {
443+
// Endorsement with the same identity has already been added
444+
logger.Warningf("Ignoring duplicated identity, Mspid: %s, pem:\n%s", serializedIdentity.Mspid, serializedIdentity.IdBytes)
445+
continue
446+
}
447+
signatureSet = append(signatureSet, &common.SignedData{
448+
// set the data that is signed; concatenation of proposal response bytes and endorser ID
449+
Data: append(prespBytes, endorsement.Endorser...),
450+
// set the identity that signs the message: it's the endorser
451+
Identity: endorsement.Endorser,
452+
// set the signature
453+
Signature: endorsement.Signature})
454+
signatureMap[identity] = struct{}{}
455+
}
456+
457+
logger.Debugf("Signature set is of size %d out of %d endorsement(s)", len(signatureSet), len(cap.Action.Endorsements))
458+
return signatureSet, nil
459+
}

core/scc/vscc/validator_onevalidsignature_test.go

+49-8
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,36 @@ import (
4444
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
4545
"github.com/hyperledger/fabric/msp/mgmt/testtools"
4646
"github.com/hyperledger/fabric/protos/common"
47+
mspproto "github.com/hyperledger/fabric/protos/msp"
4748
"github.com/hyperledger/fabric/protos/peer"
4849
"github.com/hyperledger/fabric/protos/utils"
4950
"github.com/stretchr/testify/assert"
5051
)
5152

52-
func createTx() (*common.Envelope, *peer.ProposalResponse, error) {
53+
func createTx(endorsedByDuplicatedIdentity bool) (*common.Envelope, error) {
5354
ccid := &peer.ChaincodeID{Name: "foo", Version: "v1"}
5455
cis := &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{ChaincodeId: ccid}}
5556

5657
prop, _, err := utils.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sid)
5758
if err != nil {
58-
return nil, nil, err
59+
return nil, err
5960
}
6061

6162
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, &peer.Response{Status: 200}, []byte("res"), nil, ccid, nil, id)
6263
if err != nil {
63-
return nil, nil, err
64+
return nil, err
6465
}
6566

66-
env, err := utils.CreateSignedTx(prop, id, presp)
67+
var env *common.Envelope
68+
if endorsedByDuplicatedIdentity {
69+
env, err = utils.CreateSignedTx(prop, id, presp, presp)
70+
} else {
71+
env, err = utils.CreateSignedTx(prop, id, presp)
72+
}
6773
if err != nil {
68-
return nil, nil, err
74+
return nil, err
6975
}
70-
71-
return env, presp, err
76+
return env, err
7277
}
7378

7479
func processSignedCDS(cds *peer.ChaincodeDeploymentSpec, policy *common.SignaturePolicyEnvelope) ([]byte, error) {
@@ -217,6 +222,24 @@ func getSignedByMSPMemberPolicy(mspID string) ([]byte, error) {
217222
return b, err
218223
}
219224

225+
func getSignedByOneMemberTwicePolicy(mspID string) ([]byte, error) {
226+
principal := &mspproto.MSPPrincipal{
227+
PrincipalClassification: mspproto.MSPPrincipal_ROLE,
228+
Principal: utils.MarshalOrPanic(&mspproto.MSPRole{Role: mspproto.MSPRole_MEMBER, MspIdentifier: mspID})}
229+
230+
p := &common.SignaturePolicyEnvelope{
231+
Version: 0,
232+
Rule: cauthdsl.NOutOf(2, []*common.SignaturePolicy{cauthdsl.SignedBy(0), cauthdsl.SignedBy(0)}),
233+
Identities: []*mspproto.MSPPrincipal{principal},
234+
}
235+
b, err := utils.Marshal(p)
236+
if err != nil {
237+
return nil, fmt.Errorf("Could not marshal policy, err %s", err)
238+
}
239+
240+
return b, err
241+
}
242+
220243
func getSignedByMSPAdminPolicy(mspID string) ([]byte, error) {
221244
p := cauthdsl.SignedByMspAdmin(mspID)
222245

@@ -276,7 +299,7 @@ func TestInvoke(t *testing.T) {
276299
t.Fatalf("vscc invoke should have failed")
277300
}
278301

279-
tx, _, err := createTx()
302+
tx, err := createTx(false)
280303
if err != nil {
281304
t.Fatalf("createTx returned err %s", err)
282305
}
@@ -328,6 +351,24 @@ func TestInvoke(t *testing.T) {
328351
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
329352
t.Fatalf("vscc invoke should have failed")
330353
}
354+
355+
// bad path: signed by duplicated MSP identity
356+
policy, err = getSignedByOneMemberTwicePolicy(mspid)
357+
if err != nil {
358+
t.Fatalf("failed getting policy, err %s", err)
359+
}
360+
tx, err = createTx(true)
361+
if err != nil {
362+
t.Fatalf("createTx returned err %s", err)
363+
}
364+
envBytes, err = utils.GetBytesEnvelope(tx)
365+
if err != nil {
366+
t.Fatalf("GetBytesEnvelope returned err %s", err)
367+
}
368+
args = [][]byte{[]byte("dv"), envBytes, policy}
369+
if res := stub.MockInvoke("1", args); res.Status == shim.OK || res.Message != DUPLICATED_IDENTITY_ERROR {
370+
t.Fatalf("vscc invoke should have failed due to policy evaluation failure caused by duplicated identity")
371+
}
331372
}
332373

333374
func TestInvalidFunction(t *testing.T) {

0 commit comments

Comments
 (0)