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  }