Source file src/crypto/internal/fips/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  // Package hmac implements HMAC according to [FIPS 198-1].
     6  //
     7  // [FIPS 198-1]: https://doi.org/10.6028/NIST.FIPS.198-1
     8  package hmac
     9  
    10  import (
    11  	"crypto/internal/fips"
    12  	"crypto/internal/fips/sha256"
    13  	"crypto/internal/fips/sha3"
    14  	"crypto/internal/fips/sha512"
    15  )
    16  
    17  // key is zero padded to the block size of the hash function
    18  // ipad = 0x36 byte repeated for key length
    19  // opad = 0x5c byte repeated for key length
    20  // hmac = H([key ^ opad] H([key ^ ipad] text))
    21  
    22  // marshalable is the combination of encoding.BinaryMarshaler and
    23  // encoding.BinaryUnmarshaler. Their method definitions are repeated here to
    24  // avoid a dependency on the encoding package.
    25  type marshalable interface {
    26  	MarshalBinary() ([]byte, error)
    27  	UnmarshalBinary([]byte) error
    28  }
    29  
    30  type HMAC struct {
    31  	opad, ipad   []byte
    32  	outer, inner fips.Hash
    33  
    34  	// If marshaled is true, then opad and ipad do not contain a padded
    35  	// copy of the key, but rather the marshaled state of outer/inner after
    36  	// opad/ipad has been fed into it.
    37  	marshaled bool
    38  }
    39  
    40  func (h *HMAC) Sum(in []byte) []byte {
    41  	origLen := len(in)
    42  	in = h.inner.Sum(in)
    43  
    44  	if h.marshaled {
    45  		if err := h.outer.(marshalable).UnmarshalBinary(h.opad); err != nil {
    46  			panic(err)
    47  		}
    48  	} else {
    49  		h.outer.Reset()
    50  		h.outer.Write(h.opad)
    51  	}
    52  	h.outer.Write(in[origLen:])
    53  	return h.outer.Sum(in[:origLen])
    54  }
    55  
    56  func (h *HMAC) Write(p []byte) (n int, err error) {
    57  	return h.inner.Write(p)
    58  }
    59  
    60  func (h *HMAC) Size() int      { return h.outer.Size() }
    61  func (h *HMAC) BlockSize() int { return h.inner.BlockSize() }
    62  
    63  func (h *HMAC) Reset() {
    64  	if h.marshaled {
    65  		if err := h.inner.(marshalable).UnmarshalBinary(h.ipad); err != nil {
    66  			panic(err)
    67  		}
    68  		return
    69  	}
    70  
    71  	h.inner.Reset()
    72  	h.inner.Write(h.ipad)
    73  
    74  	// If the underlying hash is marshalable, we can save some time by saving a
    75  	// copy of the hash state now, and restoring it on future calls to Reset and
    76  	// Sum instead of writing ipad/opad every time.
    77  	//
    78  	// We do this on Reset to avoid slowing down the common single-use case.
    79  	//
    80  	// This is allowed by FIPS 198-1, Section 6: "Conceptually, the intermediate
    81  	// results of the compression function on the B-byte blocks (K0 ⊕ ipad) and
    82  	// (K0 ⊕ opad) can be precomputed once, at the time of generation of the key
    83  	// K, or before its first use. These intermediate results can be stored and
    84  	// then used to initialize H each time that a message needs to be
    85  	// authenticated using the same key. [...] These stored intermediate values
    86  	// shall be treated and protected in the same manner as secret keys."
    87  	marshalableInner, innerOK := h.inner.(marshalable)
    88  	if !innerOK {
    89  		return
    90  	}
    91  	marshalableOuter, outerOK := h.outer.(marshalable)
    92  	if !outerOK {
    93  		return
    94  	}
    95  
    96  	imarshal, err := marshalableInner.MarshalBinary()
    97  	if err != nil {
    98  		return
    99  	}
   100  
   101  	h.outer.Reset()
   102  	h.outer.Write(h.opad)
   103  	omarshal, err := marshalableOuter.MarshalBinary()
   104  	if err != nil {
   105  		return
   106  	}
   107  
   108  	// Marshaling succeeded; save the marshaled state for later
   109  	h.ipad = imarshal
   110  	h.opad = omarshal
   111  	h.marshaled = true
   112  }
   113  
   114  // New returns a new HMAC hash using the given [fips.Hash] type and key.
   115  func New[H fips.Hash](h func() H, key []byte) *HMAC {
   116  	hm := new(HMAC)
   117  	hm.outer = h()
   118  	hm.inner = h()
   119  	unique := true
   120  	func() {
   121  		defer func() {
   122  			// The comparison might panic if the underlying types are not comparable.
   123  			_ = recover()
   124  		}()
   125  		if hm.outer == hm.inner {
   126  			unique = false
   127  		}
   128  	}()
   129  	if !unique {
   130  		panic("crypto/hmac: hash generation function does not produce unique values")
   131  	}
   132  	setServiceIndicator(hm.outer, key)
   133  	blocksize := hm.inner.BlockSize()
   134  	hm.ipad = make([]byte, blocksize)
   135  	hm.opad = make([]byte, blocksize)
   136  	if len(key) > blocksize {
   137  		// If key is too big, hash it.
   138  		hm.outer.Write(key)
   139  		key = hm.outer.Sum(nil)
   140  	}
   141  	copy(hm.ipad, key)
   142  	copy(hm.opad, key)
   143  	for i := range hm.ipad {
   144  		hm.ipad[i] ^= 0x36
   145  	}
   146  	for i := range hm.opad {
   147  		hm.opad[i] ^= 0x5c
   148  	}
   149  	hm.inner.Write(hm.ipad)
   150  
   151  	return hm
   152  }
   153  
   154  func setServiceIndicator(h fips.Hash, key []byte) {
   155  	// Per FIPS 140-3 IG C.M, key lengths below 112 bits are only allowed for
   156  	// legacy use (i.e. verification only) and we don't support that.
   157  	if len(key) < 112/8 {
   158  		fips.RecordNonApproved()
   159  	}
   160  
   161  	switch h.(type) {
   162  	case *sha256.Digest, *sha512.Digest, *sha3.Digest:
   163  		fips.RecordApproved()
   164  	default:
   165  		fips.RecordNonApproved()
   166  	}
   167  }
   168  

View as plain text