-
Notifications
You must be signed in to change notification settings - Fork 0
/
verifier.go
413 lines (339 loc) · 13.9 KB
/
verifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package smartbft
import (
"bytes"
"encoding/base64"
"encoding/hex"
"sync"
"github.com/SmartBFT-Go/consensus/pkg/types"
"github.com/SmartBFT-Go/consensus/smartbftprotos"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/msp"
"github.com/hyperledger/fabric/protos/utils"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
)
//go:generate mockery -dir . -name Sequencer -case underscore -output mocks
// Sequencer returns sequences
type Sequencer interface {
Sequence() uint64
}
//go:generate mockery -dir . -name ConsenterVerifier -case underscore -output mocks
// ConsenterVerifier is used to determine whether a signature from one of the consenters is valid
type ConsenterVerifier interface {
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Evaluate(signatureSet []*common.SignedData) error
}
//go:generate mockery -dir . -name AccessController -case underscore -output mocks
// AccessController is used to determine if a signature of a certain client is valid
type AccessController interface {
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Evaluate(signatureSet []*common.SignedData) error
}
type requestVerifier func(req []byte, isolated bool) (types.RequestInfo, error)
type NodeIdentitiesByID map[uint64][]byte
func (nibd NodeIdentitiesByID) IdentityToID(identity []byte) (uint64, bool) {
sID := &msp.SerializedIdentity{}
if err := proto.Unmarshal(identity, sID); err != nil {
return 0, false
}
for id, currIdentity := range nibd {
currentID := &msp.SerializedIdentity{}
proto.Unmarshal(currIdentity, currentID)
if proto.Equal(currentID, sID) {
return id, true
}
}
return 0, false
}
type Verifier struct {
ReqInspector *RequestInspector
Id2Identity NodeIdentitiesByID
ConsenterVerifier ConsenterVerifier
AccessController AccessController
VerificationSequencer Sequencer
Ledger Ledger
LastCommittedBlockHash string
LastConfigBlockNum uint64
Logger *flogging.FabricLogger
lock sync.RWMutex
ConfigValidator ConfigValidator
}
func (v *Verifier) VerifyProposal(proposal types.Proposal) ([]types.RequestInfo, error) {
block, err := ProposalToBlock(proposal)
if err != nil {
return nil, err
}
if err := verifyHashChain(block, v.lastCommittedHash()); err != nil {
return nil, err
}
requests, err := v.verifyBlockDataAndMetadata(block, proposal.Metadata)
if err != nil {
return nil, err
}
verificationSeq := v.VerificationSequence()
if verificationSeq != uint64(proposal.VerificationSequence) {
return nil, errors.Errorf("expected verification sequence %d, but proposal has %d", verificationSeq, proposal.VerificationSequence)
}
return requests, nil
}
func (v *Verifier) VerifySignature(signature types.Signature) error {
identity, exists := v.Id2Identity[signature.ID]
if !exists {
return errors.Errorf("node with id of %d doesn't exist", signature.ID)
}
return v.AccessController.Evaluate([]*common.SignedData{
{Identity: identity, Data: signature.Msg, Signature: signature.Value},
})
}
func (v *Verifier) VerifyRequest(rawRequest []byte) (types.RequestInfo, error) {
return v.verifyRequest(rawRequest, false)
}
func (v *Verifier) verifyRequest(rawRequest []byte, isolatedReq bool) (types.RequestInfo, error) {
req, err := v.ReqInspector.unwrapReq(rawRequest)
if err != nil {
return types.RequestInfo{}, err
}
err = v.AccessController.Evaluate([]*common.SignedData{
{Identity: req.sigHdr.Creator, Data: req.envelope.Payload, Signature: req.envelope.Signature},
})
if err != nil {
return types.RequestInfo{}, errors.Wrap(err, "access denied")
}
if !isolatedReq && req.chHdr.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {
return types.RequestInfo{}, errors.Errorf("only endorser transactions can be sent with other transactions")
}
switch req.chHdr.Type {
case int32(common.HeaderType_CONFIG):
case int32(common.HeaderType_ORDERER_TRANSACTION):
case int32(common.HeaderType_ENDORSER_TRANSACTION):
default:
return types.RequestInfo{}, errors.Errorf("transaction of type %s is not allowed to be included in blocks", common.HeaderType_name[req.chHdr.Type])
}
if req.chHdr.Type == int32(common.HeaderType_CONFIG) || req.chHdr.Type == int32(common.HeaderType_ORDERER_TRANSACTION) {
err := v.ConfigValidator.ValidateConfig(req.envelope)
if err != nil {
v.Logger.Errorf("Error verifying config update: %v", err)
return types.RequestInfo{}, err
}
}
return v.ReqInspector.requestIDFromSigHeader(req.sigHdr)
}
func (v *Verifier) VerifyConsenterSig(signature types.Signature, prop types.Proposal) error {
identity, exists := v.Id2Identity[signature.ID]
if !exists {
return errors.Errorf("node with id of %d doesn't exist", signature.ID)
}
sig := &Signature{}
if err := sig.Unmarshal(signature.Msg); err != nil {
v.Logger.Errorf("Failed unmarshaling signature from %d: %v", signature.ID, err)
v.Logger.Errorf("Offending signature Msg: %s", base64.StdEncoding.EncodeToString(signature.Msg))
v.Logger.Errorf("Offending signature Value: %s", base64.StdEncoding.EncodeToString(signature.Value))
return errors.Wrap(err, "malformed signature format")
}
if err := v.verifySignatureIsBoundToProposal(sig, identity, prop); err != nil {
return err
}
expectedMsgToBeSigned := util.ConcatenateBytes(sig.OrdererBlockMetadata, sig.SignatureHeader, sig.BlockHeader)
signedData := &common.SignedData{
Signature: signature.Value,
Data: expectedMsgToBeSigned,
Identity: identity,
}
return v.ConsenterVerifier.Evaluate([]*common.SignedData{signedData})
}
func (v *Verifier) VerificationSequence() uint64 {
return v.VerificationSequencer.Sequence()
}
func (v *Verifier) lastCommittedHash() string {
v.lock.RLock()
defer v.lock.RUnlock()
return v.LastCommittedBlockHash
}
func (v *Verifier) lastConfigBlockNum(block *common.Block) uint64 {
if isConfigBlock(block) {
return block.Header.Number
}
v.lock.RLock()
defer v.lock.RUnlock()
return v.LastConfigBlockNum
}
func verifyHashChain(block *common.Block, prevHeaderHash string) error {
thisHdrHashOfPrevHdr := hex.EncodeToString(block.Header.PreviousHash)
if prevHeaderHash != thisHdrHashOfPrevHdr {
return errors.Errorf("previous header hash is %s but expected %s", thisHdrHashOfPrevHdr, prevHeaderHash)
}
dataHash := hex.EncodeToString(block.Header.DataHash)
actualHashOfData := hex.EncodeToString(block.Data.Hash())
if dataHash != actualHashOfData {
return errors.Errorf("data hash is %s but expected %s", dataHash, actualHashOfData)
}
return nil
}
func (v *Verifier) verifyBlockDataAndMetadata(block *common.Block, metadata []byte) ([]types.RequestInfo, error) {
if block.Data == nil || len(block.Data.Data) == 0 {
return nil, errors.New("empty block data")
}
if block.Metadata == nil || len(block.Metadata.Metadata) < len(common.BlockMetadataIndex_name) {
return nil, errors.New("block metadata is either missing or contains too few entries")
}
signatureMetadata, err := utils.GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
if err != nil {
return nil, err
}
ordererMetadataFromSignature := &common.OrdererBlockMetadata{}
if err := proto.Unmarshal(signatureMetadata.Value, ordererMetadataFromSignature); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling OrdererBlockMetadata")
}
// Ensure the view metadata in the block signature and in the proposal are the same
metadataInBlock := &smartbftprotos.ViewMetadata{}
if err := proto.Unmarshal(ordererMetadataFromSignature.ConsenterMetadata, metadataInBlock); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from block")
}
metadataFromProposal := &smartbftprotos.ViewMetadata{}
if err := proto.Unmarshal(metadata, metadataFromProposal); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from proposal")
}
if !proto.Equal(metadataInBlock, metadataFromProposal) {
return nil, errors.Errorf("expected metadata in block to be %v but got %v", metadataFromProposal, metadataInBlock)
}
// Verify last config
lastConfig := v.lastConfigBlockNum(block)
if isConfigBlock(block) {
lastConfig = block.Header.Number
}
if ordererMetadataFromSignature.LastConfig == nil {
return nil, errors.Errorf("last config is nil")
}
if ordererMetadataFromSignature.LastConfig.Index != lastConfig {
return nil, errors.Errorf("last config in block orderer metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
}
rawLastConfig, err := utils.GetMetadataFromBlock(block, common.BlockMetadataIndex_LAST_CONFIG)
if err != nil {
return nil, err
}
lastConf := &common.LastConfig{}
if err := proto.Unmarshal(rawLastConfig.Value, lastConf); err != nil {
return nil, err
}
if lastConf.Index != lastConfig {
return nil, errors.Errorf("last config in block metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
}
return validateTransactions(block.Data.Data, v.verifyRequest)
}
func validateTransactions(blockData [][]byte, verifyReq requestVerifier) ([]types.RequestInfo, error) {
var validationFinished sync.WaitGroup
validationFinished.Add(len(blockData))
type txnValidation struct {
indexInBlock int
extractedInfo types.RequestInfo
validationErr error
}
isolated := len(blockData) == 1
validations := make(chan txnValidation, len(blockData))
for i, payload := range blockData {
go func(indexInBlock int, payload []byte) {
defer validationFinished.Done()
reqInfo, err := verifyReq(payload, isolated)
validations <- txnValidation{
indexInBlock: indexInBlock,
extractedInfo: reqInfo,
validationErr: err,
}
}(i, payload)
}
validationFinished.Wait()
close(validations)
indexToRequestInfo := make(map[int]types.RequestInfo)
for validationResult := range validations {
indexToRequestInfo[validationResult.indexInBlock] = validationResult.extractedInfo
if validationResult.validationErr != nil {
return nil, validationResult.validationErr
}
}
var res []types.RequestInfo
for indexInBlock := range blockData {
res = append(res, indexToRequestInfo[indexInBlock])
}
return res, nil
}
func (v *Verifier) verifySignatureIsBoundToProposal(sig *Signature, identity []byte, prop types.Proposal) error {
// We verify the following fields:
// ConsenterMetadata []byte
// SignatureHeader []byte
// BlockHeader []byte
// OrdererBlockMetadata []byte
// Ensure block header is equal
if !bytes.Equal(prop.Header, sig.BlockHeader) {
v.Logger.Errorf("Expected block header %s but got %s", base64.StdEncoding.EncodeToString(prop.Header),
base64.StdEncoding.EncodeToString(sig.BlockHeader))
return errors.Errorf("mismatched block header")
}
// Ensure signature header matches the identity
sigHdr := &common.SignatureHeader{}
if err := proto.Unmarshal(sig.SignatureHeader, sigHdr); err != nil {
return errors.Wrap(err, "malformed signature header")
}
if !bytes.Equal(sigHdr.Creator, identity) {
v.Logger.Warnf("Expected identity %s but got %s", base64.StdEncoding.EncodeToString(sigHdr.Creator),
base64.StdEncoding.EncodeToString(identity))
return errors.Errorf("identity in signature header does not match expected identity")
}
// Ensure orderer block metadata's consenter MD matches the proposal
ordererMD := &common.OrdererBlockMetadata{}
if err := proto.Unmarshal(sig.OrdererBlockMetadata, ordererMD); err != nil {
return errors.Wrap(err, "malformed orderer metadata in signature")
}
if !bytes.Equal(ordererMD.ConsenterMetadata, prop.Metadata) {
v.Logger.Warnf("Expected consenter metadata %s but got %s in proposal",
base64.StdEncoding.EncodeToString(ordererMD.ConsenterMetadata), base64.StdEncoding.EncodeToString(prop.Metadata))
return errors.Errorf("consenter metadata in OrdererBlockMetadata doesn't match proposal")
}
block, err := ProposalToBlock(prop)
if err != nil {
v.Logger.Warnf("got malformed proposal: %v", err)
return err
}
// Ensure Metadata slice is of the right size
if len(block.Metadata.Metadata) != len(common.BlockMetadataIndex_name) {
return errors.Errorf("block metadata is of size %d but should be of size %d",
len(block.Metadata.Metadata), len(common.BlockMetadataIndex_name))
}
signatureMetadata := &common.Metadata{}
if err := proto.Unmarshal(block.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES], signatureMetadata); err != nil {
return errors.Wrap(err, "malformed signature metadata")
}
ordererMDFromBlock := &common.OrdererBlockMetadata{}
if err := proto.Unmarshal(signatureMetadata.Value, ordererMDFromBlock); err != nil {
return errors.Wrap(err, "malformed orderer metadata in block")
}
// Ensure the block's OrdererBlockMetadata matches the signature.
if !proto.Equal(ordererMDFromBlock, ordererMD) {
return errors.Errorf("signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match")
}
return nil
}
type consenterVerifier struct {
logger *flogging.FabricLogger
channel string
policyManager policies.Manager
}
func (cv *consenterVerifier) Evaluate(signatureSet []*common.SignedData) error {
policy, ok := cv.policyManager.GetPolicy(policies.ChannelOrdererWriters)
if !ok {
cv.logger.Errorf("[%s] Error: could not find policy %s in policy manager %v", cv.channel, policies.ChannelOrdererWriters, cv.policyManager)
return errors.Errorf("could not find policy %s", policies.ChannelOrdererWriters)
}
if cv.logger.IsEnabledFor(zapcore.DebugLevel) {
cv.logger.Debugf("== Evaluating %T Policy %s ==", policy, policies.ChannelOrdererWriters)
defer cv.logger.Debugf("== Done Evaluating %T Policy %s", policy, policies.ChannelOrdererWriters)
}
return policy.Evaluate(signatureSet)
}