Skip to content

Commit

Permalink
crypto/hmac: panic if reusing hash.Hash values
Browse files Browse the repository at this point in the history
Also put Reset in the correct place for the other
benchmarks.

name           old time/op    new time/op    delta
NewWriteSum-8    1.01µs ± 0%    1.01µs ± 1%   ~     (p=0.945 n=9+9)

name           old speed      new speed      delta
NewWriteSum-8  31.7MB/s ± 0%  31.6MB/s ± 1%   ~     (p=0.948 n=9+9)

name           old alloc/op   new alloc/op   delta
NewWriteSum-8      544B ± 0%      544B ± 0%   ~     (all equal)

name           old allocs/op  new allocs/op  delta
NewWriteSum-8      7.00 ± 0%      7.00 ± 0%   ~     (all equal)

Fixes #41089

Change-Id: I3dae660adbe4993963130bf3c2636bd53899164b
Reviewed-on: https://go-review.googlesource.com/c/go/+/261960
Trust: Katie Hockman <katie@golang.org>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
  • Loading branch information
katiehockman committed Oct 19, 2020
1 parent 19f6422 commit 2a206c7
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
8 changes: 8 additions & 0 deletions doc/go1.16.html
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ <h2 id="library">Core library</h2>
TODO
</p>

<h3 id="crypto/hmac"><a href="/pkg/crypto/hmac">crypto/hmac</a></h3>

<p><!-- CL 261960 -->
<a href="/pkg/crypto/hmac/#New">New</a> will now panic if separate calls to
the hash generation function fail to return new values. Previously, the
behavior was undefined and invalid outputs were sometimes generated.
</p>

<h3 id="crypto/tls"><a href="/pkg/crypto/tls">crypto/tls</a></h3>

<p><!-- CL 256897 -->
Expand Down
15 changes: 15 additions & 0 deletions src/crypto/hmac/hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,28 @@ func (h *hmac) Reset() {
}

// New returns a new HMAC hash using the given hash.Hash type and key.
// New functions like sha256.New from crypto/sha256 can be used as h.
// h must return a new Hash every time it is called.
// Note that unlike other hash implementations in the standard library,
// the returned Hash does not implement encoding.BinaryMarshaler
// or encoding.BinaryUnmarshaler.
func New(h func() hash.Hash, key []byte) hash.Hash {
hm := new(hmac)
hm.outer = h()
hm.inner = h()
unique := true
func() {
defer func() {
// The comparison might panic if the underlying types are not comparable.
_ = recover()
}()
if hm.outer == hm.inner {
unique = false
}
}()
if !unique {
panic("crypto/hmac: hash generation function does not produce unique values")
}
blocksize := hm.inner.BlockSize()
hm.ipad = make([]byte, blocksize)
hm.opad = make([]byte, blocksize)
Expand Down
24 changes: 23 additions & 1 deletion src/crypto/hmac/hmac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,17 @@ func TestHMAC(t *testing.T) {
}
}

func TestNonUniqueHash(t *testing.T) {
sha := sha256.New()
defer func() {
err := recover()
if err == nil {
t.Error("expected panic when calling New with a non-unique hash generation function")
}
}()
New(func() hash.Hash { return sha }, []byte("bytes"))
}

// justHash implements just the hash.Hash methods and nothing else
type justHash struct {
hash.Hash
Expand Down Expand Up @@ -587,8 +598,8 @@ func BenchmarkHMACSHA256_1K(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
h.Write(buf)
h.Reset()
mac := h.Sum(nil)
h.Reset()
buf[0] = mac[0]
}
}
Expand All @@ -600,7 +611,18 @@ func BenchmarkHMACSHA256_32(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
h.Write(buf)
mac := h.Sum(nil)
h.Reset()
buf[0] = mac[0]
}
}

func BenchmarkNewWriteSum(b *testing.B) {
buf := make([]byte, 32)
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
h := New(sha256.New, make([]byte, 32))
h.Write(buf)
mac := h.Sum(nil)
buf[0] = mac[0]
}
Expand Down

4 comments on commit 2a206c7

@MikeSofaer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I ask what this was for? It broke my key generation, and I am not sure what to do.

@ianlancetaylor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use GitHub for code review, and very few people see comments on GitHub commits. Please ask your question on the golang-nuts mailing list, or, if appropriate, on https://golang.org/cl/261960.

@katiehockman
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @ianlancetaylor's suggestion.

But, since I happened to see it, I can provide a bit of clarity. First, please read over the details in #41089 which describes the motivation for the change.

It broke my key generation, and I am not sure what to do.

If it broke your key generation, then that likely means that your hash function wasn't producing unique outputs, and should be updated.

So rather than this:

sha := sha256.New()
hmac := hmac.New(func() hash.Hash { return sha }, []byte("your text"))

You can either do this:

hmac := hmac.New(func() hash.Hash { return sha256.New() }, []byte("your text"))

Or more simply, this:

hmac := hmac.New(sha245.New, []byte("your text"))

@MikeSofaer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thank you! I think my actual key generation happens in Cosmos SDK code that I don't control, but I'm also way behind on that SDK, and upgrading now, so hopefully this issue will go away.

Please sign in to comment.