• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/*
6Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
7defined in U.S. Federal Information Processing Standards Publication 198.
8An HMAC is a cryptographic hash that uses a key to sign a message.
9The receiver verifies the hash by recomputing it using the same key.
10
11Receivers should be careful to use Equal to compare MACs in order to avoid
12timing 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*/
22package hmac
23
24import (
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.
41type marshalable interface {
42	MarshalBinary() ([]byte, error)
43	UnmarshalBinary([]byte) error
44}
45
46type 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
56func (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
72func (h *hmac) Write(p []byte) (n int, err error) {
73	return h.inner.Write(p)
74}
75
76func (h *hmac) Size() int      { return h.outer.Size() }
77func (h *hmac) BlockSize() int { return h.inner.BlockSize() }
78
79func (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].
129func 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.
175func 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