github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/crypto/hmac/hmac.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as 7 defined in U.S. Federal Information Processing Standards Publication 198. 8 An HMAC is a cryptographic hash that uses a key to sign a message. 9 The receiver verifies the hash by recomputing it using the same key. 10 11 Receivers should be careful to use Equal to compare MACs in order to avoid 12 timing side-channels: 13 14 // ValidMAC reports whether messageMAC is a valid HMAC tag for message. 15 func ValidMAC(message, messageMAC, key []byte) bool { 16 mac := hmac.New(sha256.New, key) 17 mac.Write(message) 18 expectedMAC := mac.Sum(nil) 19 return hmac.Equal(messageMAC, expectedMAC) 20 } 21 */ 22 package hmac 23 24 import ( 25 "crypto/internal/boring" 26 "crypto/subtle" 27 "hash" 28 ) 29 30 // FIPS 198-1: 31 // https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf 32 33 // key is zero padded to the block size of the hash function 34 // ipad = 0x36 byte repeated for key length 35 // opad = 0x5c byte repeated for key length 36 // hmac = H([key ^ opad] H([key ^ ipad] text)) 37 38 // marshalable is the combination of encoding.BinaryMarshaler and 39 // encoding.BinaryUnmarshaler. Their method definitions are repeated here to 40 // avoid a dependency on the encoding package. 41 type marshalable interface { 42 MarshalBinary() ([]byte, error) 43 UnmarshalBinary([]byte) error 44 } 45 46 type hmac struct { 47 opad, ipad []byte 48 outer, inner hash.Hash 49 50 // If marshaled is true, then opad and ipad do not contain a padded 51 // copy of the key, but rather the marshaled state of outer/inner after 52 // opad/ipad has been fed into it. 53 marshaled bool 54 } 55 56 func (h *hmac) Sum(in []byte) []byte { 57 origLen := len(in) 58 in = h.inner.Sum(in) 59 60 if h.marshaled { 61 if err := h.outer.(marshalable).UnmarshalBinary(h.opad); err != nil { 62 panic(err) 63 } 64 } else { 65 h.outer.Reset() 66 h.outer.Write(h.opad) 67 } 68 h.outer.Write(in[origLen:]) 69 return h.outer.Sum(in[:origLen]) 70 } 71 72 func (h *hmac) Write(p []byte) (n int, err error) { 73 return h.inner.Write(p) 74 } 75 76 func (h *hmac) Size() int { return h.outer.Size() } 77 func (h *hmac) BlockSize() int { return h.inner.BlockSize() } 78 79 func (h *hmac) Reset() { 80 if h.marshaled { 81 if err := h.inner.(marshalable).UnmarshalBinary(h.ipad); err != nil { 82 panic(err) 83 } 84 return 85 } 86 87 h.inner.Reset() 88 h.inner.Write(h.ipad) 89 90 // If the underlying hash is marshalable, we can save some time by 91 // saving a copy of the hash state now, and restoring it on future 92 // calls to Reset and Sum instead of writing ipad/opad every time. 93 // 94 // If either hash is unmarshalable for whatever reason, 95 // it's safe to bail out here. 96 marshalableInner, innerOK := h.inner.(marshalable) 97 if !innerOK { 98 return 99 } 100 marshalableOuter, outerOK := h.outer.(marshalable) 101 if !outerOK { 102 return 103 } 104 105 imarshal, err := marshalableInner.MarshalBinary() 106 if err != nil { 107 return 108 } 109 110 h.outer.Reset() 111 h.outer.Write(h.opad) 112 omarshal, err := marshalableOuter.MarshalBinary() 113 if err != nil { 114 return 115 } 116 117 // Marshaling succeeded; save the marshaled state for later 118 h.ipad = imarshal 119 h.opad = omarshal 120 h.marshaled = true 121 } 122 123 // New returns a new HMAC hash using the given [hash.Hash] type and key. 124 // New functions like sha256.New from [crypto/sha256] can be used as h. 125 // h must return a new Hash every time it is called. 126 // Note that unlike other hash implementations in the standard library, 127 // the returned Hash does not implement [encoding.BinaryMarshaler] 128 // or [encoding.BinaryUnmarshaler]. 129 func New(h func() hash.Hash, key []byte) hash.Hash { 130 if boring.Enabled { 131 hm := boring.NewHMAC(h, key) 132 if hm != nil { 133 return hm 134 } 135 // BoringCrypto did not recognize h, so fall through to standard Go code. 136 } 137 hm := new(hmac) 138 hm.outer = h() 139 hm.inner = h() 140 unique := true 141 func() { 142 defer func() { 143 // The comparison might panic if the underlying types are not comparable. 144 _ = recover() 145 }() 146 if hm.outer == hm.inner { 147 unique = false 148 } 149 }() 150 if !unique { 151 panic("crypto/hmac: hash generation function does not produce unique values") 152 } 153 blocksize := hm.inner.BlockSize() 154 hm.ipad = make([]byte, blocksize) 155 hm.opad = make([]byte, blocksize) 156 if len(key) > blocksize { 157 // If key is too big, hash it. 158 hm.outer.Write(key) 159 key = hm.outer.Sum(nil) 160 } 161 copy(hm.ipad, key) 162 copy(hm.opad, key) 163 for i := range hm.ipad { 164 hm.ipad[i] ^= 0x36 165 } 166 for i := range hm.opad { 167 hm.opad[i] ^= 0x5c 168 } 169 hm.inner.Write(hm.ipad) 170 171 return hm 172 } 173 174 // Equal compares two MACs for equality without leaking timing information. 175 func Equal(mac1, mac2 []byte) bool { 176 // We don't have to be constant time if the lengths of the MACs are 177 // different as that suggests that a completely different hash function 178 // was used. 179 return subtle.ConstantTimeCompare(mac1, mac2) == 1 180 }