Source file src/crypto/hmac/hmac.go

     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  }
   181  

View as plain text