github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/signature/checksum.go (about) 1 package signature 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "hash/crc32" 8 9 "github.com/onflow/flow-go/model/flow" 10 ) 11 12 // CheckSumLen is fixed to be 4 bytes 13 const CheckSumLen = 4 14 15 func checksum(data []byte) [CheckSumLen]byte { 16 // since the checksum is only for detecting honest mistake, 17 // crc32 is enough 18 sum := crc32.ChecksumIEEE(data) 19 // converting the uint32 checksum value into [4]byte 20 var sumBytes [CheckSumLen]byte 21 binary.BigEndian.PutUint32(sumBytes[:], sum) 22 return sumBytes 23 } 24 25 // CheckSumFromIdentities returns checksum for the given identities 26 func CheckSumFromIdentities(identities []flow.Identifier) [CheckSumLen]byte { 27 return checksum(EncodeIdentities(identities)) 28 } 29 30 // EncodeIdentities will concatenation all the identities into bytes 31 func EncodeIdentities(identities []flow.Identifier) []byte { 32 // a simple concatenation is deterministic, since each identifier has fixed length. 33 encoded := make([]byte, 0, len(identities)*flow.IdentifierLen) 34 for _, id := range identities { 35 encoded = append(encoded, id[:]...) 36 } 37 return encoded 38 } 39 40 // PrefixCheckSum prefix the given data with the checksum of the given identifier list 41 func PrefixCheckSum(canonicalList []flow.Identifier, signrIndices []byte) []byte { 42 sum := CheckSumFromIdentities(canonicalList) 43 prefixed := make([]byte, 0, len(sum)+len(signrIndices)) 44 prefixed = append(prefixed, sum[:]...) 45 prefixed = append(prefixed, signrIndices[:]...) 46 return prefixed 47 } 48 49 // SplitCheckSum splits the given bytes into two parts: 50 // - prefixed checksum of the canonical identifier list 51 // - the signerIndices 52 // Expected error during normal operations: 53 // - ErrInvalidChecksum if the input is shorter than the expected checksum contained therein 54 func SplitCheckSum(checkSumPrefixedSignerIndices []byte) ([CheckSumLen]byte, []byte, error) { 55 if len(checkSumPrefixedSignerIndices) < CheckSumLen { 56 return [CheckSumLen]byte{}, nil, 57 fmt.Errorf("expect checkSumPrefixedSignerIndices to have at least %v bytes, but got %v: %w", 58 CheckSumLen, len(checkSumPrefixedSignerIndices), ErrInvalidChecksum) 59 } 60 61 var sum [CheckSumLen]byte 62 copy(sum[:], checkSumPrefixedSignerIndices[:CheckSumLen]) 63 signerIndices := checkSumPrefixedSignerIndices[CheckSumLen:] 64 65 return sum, signerIndices, nil 66 } 67 68 // CompareAndExtract reads the checksum from the given `checkSumPrefixedSignerIndices` 69 // and compares it with the checksum of the given identifier list. 70 // It returns the signer indices if the checksum matches. 71 // Inputs: 72 // - canonicalList is the canonical list from decoder's view 73 // - checkSumPrefixedSignerIndices is the signer indices created by the encoder, 74 // and prefixed with the checksum of the canonical list from encoder's view. 75 // 76 // Expected error during normal operations: 77 // - ErrInvalidChecksum if the input is shorter than the expected checksum contained therein 78 func CompareAndExtract(canonicalList []flow.Identifier, checkSumPrefixedSignerIndices []byte) ([]byte, error) { 79 // the checkSumPrefixedSignerIndices bytes contains two parts: 80 // 1. the checksum of the canonical identifier list from encoder's view 81 // 2. the signer indices 82 // so split them 83 encoderChecksum, signerIndices, err := SplitCheckSum(checkSumPrefixedSignerIndices) 84 if err != nil { 85 return nil, fmt.Errorf("could not split checksum: %w", err) 86 } 87 88 // this canonicalList here is from decoder's view. 89 // by comparing the checksum of the canonical list from encoder's view 90 // and the full canonical list from decoder's view, we can tell if the encoder 91 // encodes the signer indices using the same list as decoder. 92 decoderChecksum := CheckSumFromIdentities(canonicalList) 93 match := bytes.Equal(encoderChecksum[:], decoderChecksum[:]) 94 if !match { 95 return nil, fmt.Errorf("decoder sees a canonical list %v, which has a different checksum %x than the encoder's checksum %x: %w", 96 canonicalList, decoderChecksum, encoderChecksum, ErrInvalidChecksum) 97 } 98 99 return signerIndices, nil 100 }