Source file src/crypto/internal/fips140/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/fips140"
    12  	"crypto/internal/fips140/sha256"
    13  	"crypto/internal/fips140/sha3"
    14  	"crypto/internal/fips140/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 fips140.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  	// forHKDF and keyLen are stored to inform the service indicator decision.
    40  	forHKDF bool
    41  	keyLen  int
    42  }
    43  
    44  func (h *HMAC) Sum(in []byte) []byte {
    45  	// Per FIPS 140-3 IG C.M, key lengths below 112 bits are only allowed for
    46  	// legacy use (i.e. verification only) and we don't support that. However,
    47  	// HKDF uses the HMAC key for the salt, which is allowed to be shorter.
    48  	if h.keyLen < 112/8 && !h.forHKDF {
    49  		fips140.RecordNonApproved()
    50  	}
    51  	switch h.inner.(type) {
    52  	case *sha256.Digest, *sha512.Digest, *sha3.Digest:
    53  	default:
    54  		fips140.RecordNonApproved()
    55  	}
    56  
    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 saving a
    91  	// copy of the hash state now, and restoring it on future calls to Reset and
    92  	// Sum instead of writing ipad/opad every time.
    93  	//
    94  	// We do this on Reset to avoid slowing down the common single-use case.
    95  	//
    96  	// This is allowed by FIPS 198-1, Section 6: "Conceptually, the intermediate
    97  	// results of the compression function on the B-byte blocks (K0 ⊕ ipad) and
    98  	// (K0 ⊕ opad) can be precomputed once, at the time of generation of the key
    99  	// K, or before its first use. These intermediate results can be stored and
   100  	// then used to initialize H each time that a message needs to be
   101  	// authenticated using the same key. [...] These stored intermediate values
   102  	// shall be treated and protected in the same manner as secret keys."
   103  	marshalableInner, innerOK := h.inner.(marshalable)
   104  	if !innerOK {
   105  		return
   106  	}
   107  	marshalableOuter, outerOK := h.outer.(marshalable)
   108  	if !outerOK {
   109  		return
   110  	}
   111  
   112  	imarshal, err := marshalableInner.MarshalBinary()
   113  	if err != nil {
   114  		return
   115  	}
   116  
   117  	h.outer.Reset()
   118  	h.outer.Write(h.opad)
   119  	omarshal, err := marshalableOuter.MarshalBinary()
   120  	if err != nil {
   121  		return
   122  	}
   123  
   124  	// Marshaling succeeded; save the marshaled state for later
   125  	h.ipad = imarshal
   126  	h.opad = omarshal
   127  	h.marshaled = true
   128  }
   129  
   130  // New returns a new HMAC hash using the given [fips140.Hash] type and key.
   131  func New[H fips140.Hash](h func() H, key []byte) *HMAC {
   132  	hm := &HMAC{keyLen: len(key)}
   133  	hm.outer = h()
   134  	hm.inner = h()
   135  	unique := true
   136  	func() {
   137  		defer func() {
   138  			// The comparison might panic if the underlying types are not comparable.
   139  			_ = recover()
   140  		}()
   141  		if hm.outer == hm.inner {
   142  			unique = false
   143  		}
   144  	}()
   145  	if !unique {
   146  		panic("crypto/hmac: hash generation function does not produce unique values")
   147  	}
   148  	blocksize := hm.inner.BlockSize()
   149  	hm.ipad = make([]byte, blocksize)
   150  	hm.opad = make([]byte, blocksize)
   151  	if len(key) > blocksize {
   152  		// If key is too big, hash it.
   153  		hm.outer.Write(key)
   154  		key = hm.outer.Sum(nil)
   155  	}
   156  	copy(hm.ipad, key)
   157  	copy(hm.opad, key)
   158  	for i := range hm.ipad {
   159  		hm.ipad[i] ^= 0x36
   160  	}
   161  	for i := range hm.opad {
   162  		hm.opad[i] ^= 0x5c
   163  	}
   164  	hm.inner.Write(hm.ipad)
   165  
   166  	return hm
   167  }
   168  
   169  // MarkAsUsedInKDF records that this HMAC instance is used as part of a KDF.
   170  func MarkAsUsedInKDF(h *HMAC) {
   171  	h.forHKDF = true
   172  }
   173  

View as plain text