github.com/insolar/x-crypto@v0.0.0-20191031140942-75fab8a325f6/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 // CheckMAC reports whether messageMAC is a valid HMAC tag for message. 15 func CheckMAC(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 "github.com/insolar/x-crypto/subtle" 26 "hash" 27 ) 28 29 // FIPS 198-1: 30 // https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf 31 32 // key is zero padded to the block size of the hash function 33 // ipad = 0x36 byte repeated for key length 34 // opad = 0x5c byte repeated for key length 35 // hmac = H([key ^ opad] H([key ^ ipad] text)) 36 37 type hmac struct { 38 size int 39 blocksize int 40 opad, ipad []byte 41 outer, inner hash.Hash 42 } 43 44 func (h *hmac) Sum(in []byte) []byte { 45 origLen := len(in) 46 in = h.inner.Sum(in) 47 h.outer.Reset() 48 h.outer.Write(h.opad) 49 h.outer.Write(in[origLen:]) 50 return h.outer.Sum(in[:origLen]) 51 } 52 53 func (h *hmac) Write(p []byte) (n int, err error) { 54 return h.inner.Write(p) 55 } 56 57 func (h *hmac) Size() int { return h.size } 58 59 func (h *hmac) BlockSize() int { return h.blocksize } 60 61 func (h *hmac) Reset() { 62 h.inner.Reset() 63 h.inner.Write(h.ipad) 64 } 65 66 // New returns a new HMAC hash using the given hash.Hash type and key. 67 // Note that unlike other hash implementations in the standard library, 68 // the returned Hash does not implement encoding.BinaryMarshaler 69 // or encoding.BinaryUnmarshaler. 70 func New(h func() hash.Hash, key []byte) hash.Hash { 71 hm := new(hmac) 72 hm.outer = h() 73 hm.inner = h() 74 hm.size = hm.inner.Size() 75 hm.blocksize = hm.inner.BlockSize() 76 hm.ipad = make([]byte, hm.blocksize) 77 hm.opad = make([]byte, hm.blocksize) 78 if len(key) > hm.blocksize { 79 // If key is too big, hash it. 80 hm.outer.Write(key) 81 key = hm.outer.Sum(nil) 82 } 83 copy(hm.ipad, key) 84 copy(hm.opad, key) 85 for i := range hm.ipad { 86 hm.ipad[i] ^= 0x36 87 } 88 for i := range hm.opad { 89 hm.opad[i] ^= 0x5c 90 } 91 hm.inner.Write(hm.ipad) 92 return hm 93 } 94 95 // Equal compares two MACs for equality without leaking timing information. 96 func Equal(mac1, mac2 []byte) bool { 97 // We don't have to be constant time if the lengths of the MACs are 98 // different as that suggests that a completely different hash function 99 // was used. 100 return subtle.ConstantTimeCompare(mac1, mac2) == 1 101 }