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 }