Black Lives Matter. Support the Equal Justice Initiative.

Source file src/crypto/hmac/hmac.go

Documentation: crypto/hmac

     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/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  // Marshalable is the combination of encoding.BinaryMarshaler and
    38  // encoding.BinaryUnmarshaler. Their method definitions are repeated here to
    39  // avoid a dependency on the encoding package.
    40  type marshalable interface {
    41  	MarshalBinary() ([]byte, error)
    42  	UnmarshalBinary([]byte) error
    43  }
    44  
    45  type hmac struct {
    46  	opad, ipad   []byte
    47  	outer, inner hash.Hash
    48  
    49  	// If marshaled is true, then opad and ipad do not contain a padded
    50  	// copy of the key, but rather the marshaled state of outer/inner after
    51  	// opad/ipad has been fed into it.
    52  	marshaled bool
    53  }
    54  
    55  func (h *hmac) Sum(in []byte) []byte {
    56  	origLen := len(in)
    57  	in = h.inner.Sum(in)
    58  
    59  	if h.marshaled {
    60  		if err := h.outer.(marshalable).UnmarshalBinary(h.opad); err != nil {
    61  			panic(err)
    62  		}
    63  	} else {
    64  		h.outer.Reset()
    65  		h.outer.Write(h.opad)
    66  	}
    67  	h.outer.Write(in[origLen:])
    68  	return h.outer.Sum(in[:origLen])
    69  }
    70  
    71  func (h *hmac) Write(p []byte) (n int, err error) {
    72  	return h.inner.Write(p)
    73  }
    74  
    75  func (h *hmac) Size() int      { return h.outer.Size() }
    76  func (h *hmac) BlockSize() int { return h.inner.BlockSize() }
    77  
    78  func (h *hmac) Reset() {
    79  	if h.marshaled {
    80  		if err := h.inner.(marshalable).UnmarshalBinary(h.ipad); err != nil {
    81  			panic(err)
    82  		}
    83  		return
    84  	}
    85  
    86  	h.inner.Reset()
    87  	h.inner.Write(h.ipad)
    88  
    89  	// If the underlying hash is marshalable, we can save some time by
    90  	// saving a copy of the hash state now, and restoring it on future
    91  	// calls to Reset and Sum instead of writing ipad/opad every time.
    92  	//
    93  	// If either hash is unmarshalable for whatever reason,
    94  	// it's safe to bail out here.
    95  	marshalableInner, innerOK := h.inner.(marshalable)
    96  	if !innerOK {
    97  		return
    98  	}
    99  	marshalableOuter, outerOK := h.outer.(marshalable)
   100  	if !outerOK {
   101  		return
   102  	}
   103  
   104  	imarshal, err := marshalableInner.MarshalBinary()
   105  	if err != nil {
   106  		return
   107  	}
   108  
   109  	h.outer.Reset()
   110  	h.outer.Write(h.opad)
   111  	omarshal, err := marshalableOuter.MarshalBinary()
   112  	if err != nil {
   113  		return
   114  	}
   115  
   116  	// Marshaling succeeded; save the marshaled state for later
   117  	h.ipad = imarshal
   118  	h.opad = omarshal
   119  	h.marshaled = true
   120  }
   121  
   122  // New returns a new HMAC hash using the given hash.Hash type and key.
   123  // Note that unlike other hash implementations in the standard library,
   124  // the returned Hash does not implement encoding.BinaryMarshaler
   125  // or encoding.BinaryUnmarshaler.
   126  func New(h func() hash.Hash, key []byte) hash.Hash {
   127  	hm := new(hmac)
   128  	hm.outer = h()
   129  	hm.inner = h()
   130  	blocksize := hm.inner.BlockSize()
   131  	hm.ipad = make([]byte, blocksize)
   132  	hm.opad = make([]byte, blocksize)
   133  	if len(key) > blocksize {
   134  		// If key is too big, hash it.
   135  		hm.outer.Write(key)
   136  		key = hm.outer.Sum(nil)
   137  	}
   138  	copy(hm.ipad, key)
   139  	copy(hm.opad, key)
   140  	for i := range hm.ipad {
   141  		hm.ipad[i] ^= 0x36
   142  	}
   143  	for i := range hm.opad {
   144  		hm.opad[i] ^= 0x5c
   145  	}
   146  	hm.inner.Write(hm.ipad)
   147  
   148  	return hm
   149  }
   150  
   151  // Equal compares two MACs for equality without leaking timing information.
   152  func Equal(mac1, mac2 []byte) bool {
   153  	// We don't have to be constant time if the lengths of the MACs are
   154  	// different as that suggests that a completely different hash function
   155  	// was used.
   156  	return subtle.ConstantTimeCompare(mac1, mac2) == 1
   157  }
   158  

View as plain text