-
Notifications
You must be signed in to change notification settings - Fork 178
/
checksum.go
100 lines (87 loc) · 3.86 KB
/
checksum.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
package signature
import (
"bytes"
"encoding/binary"
"fmt"
"hash/crc32"
"github.com/onflow/flow-go/model/flow"
)
// CheckSumLen is fixed to be 4 bytes
const CheckSumLen = 4
func checksum(data []byte) [CheckSumLen]byte {
// since the checksum is only for detecting honest mistake,
// crc32 is enough
sum := crc32.ChecksumIEEE(data)
// converting the uint32 checksum value into [4]byte
var sumBytes [CheckSumLen]byte
binary.BigEndian.PutUint32(sumBytes[:], sum)
return sumBytes
}
// CheckSumFromIdentities returns checksum for the given identities
func CheckSumFromIdentities(identities []flow.Identifier) [CheckSumLen]byte {
return checksum(EncodeIdentities(identities))
}
// EncodeIdentities will concatenation all the identities into bytes
func EncodeIdentities(identities []flow.Identifier) []byte {
// a simple concatenation is deterministic, since each identifier has fixed length.
encoded := make([]byte, 0, len(identities)*flow.IdentifierLen)
for _, id := range identities {
encoded = append(encoded, id[:]...)
}
return encoded
}
// PrefixCheckSum prefix the given data with the checksum of the given identifier list
func PrefixCheckSum(canonicalList []flow.Identifier, signrIndices []byte) []byte {
sum := CheckSumFromIdentities(canonicalList)
prefixed := make([]byte, 0, len(sum)+len(signrIndices))
prefixed = append(prefixed, sum[:]...)
prefixed = append(prefixed, signrIndices[:]...)
return prefixed
}
// SplitCheckSum splits the given bytes into two parts:
// - prefixed checksum of the canonical identifier list
// - the signerIndices
// Expected error during normal operations:
// - ErrInvalidChecksum if the input is shorter than the expected checksum contained therein
func SplitCheckSum(checkSumPrefixedSignerIndices []byte) ([CheckSumLen]byte, []byte, error) {
if len(checkSumPrefixedSignerIndices) < CheckSumLen {
return [CheckSumLen]byte{}, nil,
fmt.Errorf("expect checkSumPrefixedSignerIndices to have at least %v bytes, but got %v: %w",
CheckSumLen, len(checkSumPrefixedSignerIndices), ErrInvalidChecksum)
}
var sum [CheckSumLen]byte
copy(sum[:], checkSumPrefixedSignerIndices[:CheckSumLen])
signerIndices := checkSumPrefixedSignerIndices[CheckSumLen:]
return sum, signerIndices, nil
}
// CompareAndExtract reads the checksum from the given `checkSumPrefixedSignerIndices`
// and compares it with the checksum of the given identifier list.
// It returns the signer indices if the checksum matches.
// Inputs:
// - canonicalList is the canonical list from decoder's view
// - checkSumPrefixedSignerIndices is the signer indices created by the encoder,
// and prefixed with the checksum of the canonical list from encoder's view.
//
// Expected error during normal operations:
// - ErrInvalidChecksum if the input is shorter than the expected checksum contained therein
func CompareAndExtract(canonicalList []flow.Identifier, checkSumPrefixedSignerIndices []byte) ([]byte, error) {
// the checkSumPrefixedSignerIndices bytes contains two parts:
// 1. the checksum of the canonical identifier list from encoder's view
// 2. the signer indices
// so split them
encoderChecksum, signerIndices, err := SplitCheckSum(checkSumPrefixedSignerIndices)
if err != nil {
return nil, fmt.Errorf("could not split checksum: %w", err)
}
// this canonicalList here is from decoder's view.
// by comparing the checksum of the canonical list from encoder's view
// and the full canonical list from decoder's view, we can tell if the encoder
// encodes the signer indices using the same list as decoder.
decoderChecksum := CheckSumFromIdentities(canonicalList)
match := bytes.Equal(encoderChecksum[:], decoderChecksum[:])
if !match {
return nil, fmt.Errorf("decoder sees a canonical list %v, which has a different checksum %x than the encoder's checksum %x: %w",
canonicalList, decoderChecksum, encoderChecksum, ErrInvalidChecksum)
}
return signerIndices, nil
}