github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/crypto/hash.go (about)

     1  package crypto
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/onflow/crypto/hash"
     8  
     9  	"github.com/onflow/flow-go/model/flow"
    10  )
    11  
    12  // constant tag length
    13  const tagLength = flow.DomainTagLength
    14  
    15  // prefixedHashing embeds a crypto hasher and implements
    16  // hashing with a prefix : prefixedHashing(data) = hasher(prefix || data)
    17  //
    18  // Prefixes are padded tags till 32 bytes to guarantee prefixedHashers are independent
    19  // hashers.
    20  // Prefixes are disabled with the particular tag value ""
    21  type prefixedHashing struct {
    22  	hash.Hasher
    23  	usePrefix bool
    24  	tag       [tagLength]byte
    25  }
    26  
    27  // paddedDomainTag converts a string into a padded byte array.
    28  func paddedDomainTag(s string) ([tagLength]byte, error) {
    29  	var tag [tagLength]byte
    30  	if len(s) > tagLength {
    31  		return tag, fmt.Errorf("domain tag cannot be longer than %d characters, got %s", tagLength, s)
    32  	}
    33  	copy(tag[:], s)
    34  	return tag, nil
    35  }
    36  
    37  var hasherCreators = map[hash.HashingAlgorithm](func() hash.Hasher){
    38  	hash.SHA2_256:   hash.NewSHA2_256,
    39  	hash.SHA3_256:   hash.NewSHA3_256,
    40  	hash.SHA2_384:   hash.NewSHA2_384,
    41  	hash.SHA3_384:   hash.NewSHA3_384,
    42  	hash.Keccak_256: hash.NewKeccak_256,
    43  }
    44  
    45  // NewPrefixedHashing returns a new hasher that prefixes the tag for all
    46  // hash computations (only when tag is not empty).
    47  //
    48  // Supported algorithms are SHA2-256, SHA2-384, SHA3-256, SHA3-384 and Keccak256.
    49  func NewPrefixedHashing(algo hash.HashingAlgorithm, tag string) (hash.Hasher, error) {
    50  
    51  	hasherCreator, hasCreator := hasherCreators[algo]
    52  	if !hasCreator {
    53  		return nil, errors.New("hashing algorithm is not supported for prefixed algorithm")
    54  	}
    55  
    56  	paddedTag, err := paddedDomainTag(tag)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("prefixed hashing failed: %w", err)
    59  	}
    60  
    61  	return &prefixedHashing{
    62  		Hasher: hasherCreator(),
    63  		// if tag is empty, do not use any prefix (standard hashing)
    64  		usePrefix: tag != "",
    65  		tag:       paddedTag,
    66  	}, nil
    67  }
    68  
    69  // ComputeHash calculates and returns the digest of input byte array prefixed by a tag.
    70  // Not thread-safe
    71  func (s *prefixedHashing) ComputeHash(data []byte) hash.Hash {
    72  	s.Reset()
    73  	_, _ = s.Write(data)
    74  	return s.Hasher.SumHash()
    75  }
    76  
    77  // Reset gets the hasher back to its original state.
    78  func (s *prefixedHashing) Reset() {
    79  	s.Hasher.Reset()
    80  	// include the tag only when using a prefix is enabled
    81  	if s.usePrefix {
    82  		_, _ = s.Write(s.tag[:])
    83  	}
    84  }