Skip to content

Commit 8a8a1c1

Browse files
committed
[FAB-12500] Deliver client hash chain validation
This change set adds hash chain validation utilities for the deliver client for an ordering service node of a cluster type, to be used for replication between ordering nodes. Change-Id: Ic604558a3d47752da4898ef1e25e982eaf178f86 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent b54d771 commit 8a8a1c1

File tree

3 files changed

+382
-0
lines changed

3 files changed

+382
-0
lines changed

orderer/common/cluster/mocks/block_verifier.go

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

orderer/common/cluster/util.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ package cluster
88

99
import (
1010
"bytes"
11+
"encoding/hex"
1112
"encoding/pem"
1213
"sync/atomic"
1314

15+
"github.com/hyperledger/fabric/common/util"
1416
"github.com/hyperledger/fabric/core/comm"
17+
"github.com/hyperledger/fabric/protos/common"
18+
"github.com/hyperledger/fabric/protos/utils"
1519
"github.com/pkg/errors"
1620
"google.golang.org/grpc"
1721
)
@@ -141,3 +145,120 @@ func DERtoPEM(der []byte) string {
141145
Bytes: der,
142146
}))
143147
}
148+
149+
// StandardDialerDialer wraps a PredicateDialer
150+
// to a standard cluster.Dialer that passes in a nil verify function
151+
type StandardDialerDialer struct {
152+
Dialer *PredicateDialer
153+
}
154+
155+
func (bdp *StandardDialerDialer) Dial(address string) (*grpc.ClientConn, error) {
156+
return bdp.Dialer.Dial(address, nil)
157+
}
158+
159+
//go:generate mockery -dir . -name BlockVerifier -case underscore -output ./mocks/
160+
161+
// BlockVerifier verifies block signatures.
162+
type BlockVerifier interface {
163+
// VerifyBlockSignature verifies a signature of a block
164+
VerifyBlockSignature(sd []*common.SignedData) error
165+
}
166+
167+
// BlockSequenceVerifier verifies that the given consecutive sequence
168+
// of blocks is valid.
169+
type BlockSequenceVerifier func([]*common.Block) error
170+
171+
// Dialer creates a gRPC connection to a remote address
172+
type Dialer interface {
173+
Dial(address string) (*grpc.ClientConn, error)
174+
}
175+
176+
// VerifyBlocks verifies the given consecutive sequence of blocks is valid,
177+
// and returns nil if it's valid, else an error.
178+
func VerifyBlocks(blockBuff []*common.Block, signatureVerifier BlockVerifier) error {
179+
if len(blockBuff) == 0 {
180+
return errors.New("buffer is empty")
181+
}
182+
// First, we verify that the block hash in every block is:
183+
// Equal to the hash in the header
184+
// Equal to the previous hash in the succeeding block
185+
for i := range blockBuff {
186+
if err := VerifyBlockHash(i, blockBuff); err != nil {
187+
return err
188+
}
189+
}
190+
191+
// Verify the last block's signature
192+
lastBlock := blockBuff[len(blockBuff)-1]
193+
return VerifyBlockSignature(lastBlock, signatureVerifier)
194+
}
195+
196+
// VerifyBlockHash verifies the hash chain of the block with the given index
197+
// among the blocks of the given block buffer.
198+
func VerifyBlockHash(indexInBuffer int, blockBuff []*common.Block) error {
199+
if len(blockBuff) <= indexInBuffer {
200+
return errors.Errorf("index %d out of bounds (total %d blocks)", indexInBuffer, len(blockBuff))
201+
}
202+
block := blockBuff[indexInBuffer]
203+
if block.Header == nil {
204+
return errors.New("missing block header")
205+
}
206+
seq := block.Header.Number
207+
dataHash := block.Data.Hash()
208+
// Verify data hash matches the hash in the header
209+
if !bytes.Equal(dataHash, block.Header.DataHash) {
210+
computedHash := hex.EncodeToString(dataHash)
211+
claimedHash := hex.EncodeToString(block.Header.DataHash)
212+
return errors.Errorf("computed hash of block (%d) (%s) doesn't match claimed hash (%s)",
213+
seq, computedHash, claimedHash)
214+
}
215+
// We have a previous block in the buffer, ensure current block's previous hash matches the previous one.
216+
if indexInBuffer > 0 {
217+
prevBlock := blockBuff[indexInBuffer-1]
218+
currSeq := block.Header.Number
219+
if prevBlock.Header == nil {
220+
return errors.New("previous block header is nil")
221+
}
222+
prevSeq := prevBlock.Header.Number
223+
if prevSeq+1 != currSeq {
224+
return errors.Errorf("sequences %d and %d were received consecutively", prevSeq, currSeq)
225+
}
226+
if !bytes.Equal(block.Header.PreviousHash, prevBlock.Header.Hash()) {
227+
claimedPrevHash := hex.EncodeToString(block.Header.PreviousHash)
228+
actualPrevHash := hex.EncodeToString(prevBlock.Header.Hash())
229+
return errors.Errorf("block %d's hash (%s) mismatches %d's prev block hash (%s)",
230+
currSeq, actualPrevHash, prevSeq, claimedPrevHash)
231+
}
232+
}
233+
return nil
234+
}
235+
236+
// VerifyBlockSignature verifies the signature on the block with the given BlockVerifier
237+
func VerifyBlockSignature(block *common.Block, verifier BlockVerifier) error {
238+
if block.Metadata == nil || len(block.Metadata.Metadata) <= int(common.BlockMetadataIndex_SIGNATURES) {
239+
return errors.New("no metadata in block")
240+
}
241+
metadata, err := utils.GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
242+
if err != nil {
243+
return errors.Errorf("failed unmarshaling medatata for signatures: %v", err)
244+
}
245+
246+
var signatureSet []*common.SignedData
247+
for _, metadataSignature := range metadata.Signatures {
248+
sigHdr, err := utils.GetSignatureHeader(metadataSignature.SignatureHeader)
249+
if err != nil {
250+
return errors.Errorf("failed unmarshaling signature header for block with id %d: %v",
251+
block.Header.Number, err)
252+
}
253+
signatureSet = append(signatureSet,
254+
&common.SignedData{
255+
Identity: sigHdr.Creator,
256+
Data: util.ConcatenateBytes(metadata.Value,
257+
metadataSignature.SignatureHeader, block.Header.Bytes()),
258+
Signature: metadataSignature.Signature,
259+
},
260+
)
261+
}
262+
263+
return verifier.VerifyBlockSignature(signatureSet)
264+
}

0 commit comments

Comments
 (0)