Skip to content

Commit

Permalink
[dev.boringcrypto] crypto/hmac: use BoringCrypto
Browse files Browse the repository at this point in the history
Change-Id: Id4019d601c615b4835b0337d82be3d508292810e
Reviewed-on: https://go-review.googlesource.com/55475
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
  • Loading branch information
rsc committed Aug 17, 2017
1 parent 96d6718 commit 74e33c4
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/crypto/hmac/hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ timing side-channels:
package hmac

import (
"crypto/internal/boring"
"crypto/subtle"
"hash"
)
Expand Down Expand Up @@ -65,6 +66,13 @@ func (h *hmac) Reset() {

// New returns a new HMAC hash using the given hash.Hash type and key.
func New(h func() hash.Hash, key []byte) hash.Hash {
if boring.Enabled {
hm := boring.NewHMAC(h, key)
if hm != nil {
return hm
}
// BoringCrypto did not recognize h, so fall through to standard Go code.
}
hm := new(hmac)
hm.outer = h()
hm.inner = h()
Expand Down
137 changes: 137 additions & 0 deletions src/crypto/internal/boring/hmac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux,amd64
// +build !cmd_go_bootstrap

package boring

// #include "goboringcrypto.h"
import "C"
import (
"hash"
"runtime"
"unsafe"
)

// hashToMD converts a hash.Hash implementation from this package
// to a BoringCrypto *C.GO_EVP_MD.
func hashToMD(h hash.Hash) *C.GO_EVP_MD {
switch h.(type) {
case *sha1Hash:
return C._goboringcrypto_EVP_sha1()
case *sha224Hash:
return C._goboringcrypto_EVP_sha224()
case *sha256Hash:
return C._goboringcrypto_EVP_sha256()
case *sha384Hash:
return C._goboringcrypto_EVP_sha384()
case *sha512Hash:
return C._goboringcrypto_EVP_sha512()
}
return nil
}

// NewHMAC returns a new HMAC using BoringCrypto.
// The function h must return a hash implemented by
// BoringCrypto (for example, h could be boring.NewSHA256).
// If h is not recognized, NewHMAC returns nil.
func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
ch := h()
md := hashToMD(ch)
if md == nil {
return nil
}

// Note: Could hash down long keys here using EVP_Digest.
hkey := make([]byte, len(key))
copy(hkey, key)
hmac := &boringHMAC{
md: md,
size: ch.Size(),
blockSize: ch.BlockSize(),
key: hkey,
}
hmac.Reset()
return hmac
}

type boringHMAC struct {
md *C.GO_EVP_MD
ctx C.GO_HMAC_CTX
size int
blockSize int
key []byte
sum []byte
needCleanup bool
}

func (h *boringHMAC) Reset() {
if h.needCleanup {
C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
} else {
h.needCleanup = true
runtime.SetFinalizer(h, (*boringHMAC).finalize)
}
C._goboringcrypto_HMAC_CTX_init(&h.ctx)

if C._goboringcrypto_HMAC_Init(&h.ctx, unsafe.Pointer(&h.key[0]), C.int(len(h.key)), h.md) == 0 {
panic("boringcrypto: HMAC_Init failed")
}
if int(C._goboringcrypto_HMAC_size(&h.ctx)) != h.size {
println("boringcrypto: HMAC size:", C._goboringcrypto_HMAC_size(&h.ctx), "!=", h.size)
panic("boringcrypto: HMAC size mismatch")
}
h.sum = nil
}

func (h *boringHMAC) finalize() {
C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
}

var badSum = make([]byte, 1)

func (h *boringHMAC) Write(p []byte) (int, error) {
if h.sum != nil {
h.sum = badSum
} else if len(p) > 0 {
C._goboringcrypto_HMAC_Update(&h.ctx, (*C.uint8_t)(unsafe.Pointer(&p[0])), C.size_t(len(p)))
}
return len(p), nil
}

func (h *boringHMAC) Size() int {
return h.size
}

func (h *boringHMAC) BlockSize() int {
return h.blockSize
}

func (h *boringHMAC) Sum(in []byte) []byte {
if h.sum == nil {
size := h.Size()
h.sum = make([]byte, size)
C._goboringcrypto_HMAC_Final(&h.ctx, (*C.uint8_t)(unsafe.Pointer(&h.sum[0])), nil)

// Clean up and disable finalizer since most of the time
// there is no Reset coming. If we do get a Reset, we will
// re-enable the finalizer. We have a saved copy of the key.
C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
h.needCleanup = false
runtime.SetFinalizer(h, nil)
} else if &h.sum[0] == &badSum[0] {
// crypto/tls's tls10.MAC method calls Write after Sum,
// in an attempt to do more-like-constant-time checksums
// during TLS 1.0 SHA1-based MACs. We can't support that,
// so we ignore the Write in that case above, but we also
// poison h.sum so that future Sum calls panic, to avoid
// returning the pre-Write checksum.
// We expect no code actually does Sum, Write, Sum.
// Under FIPS restrictions, crypto/tls would not use
// any SHA1-based MACs anyway.
panic("boringcrypto: hmac used with Sum, Write, Sum")
}
return append(in, h.sum...)
}
2 changes: 2 additions & 0 deletions src/crypto/internal/boring/notboring.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ func NewSHA224() hash.Hash { panic("boringcrypto: not available") }
func NewSHA256() hash.Hash { panic("boringcrypto: not available") }
func NewSHA384() hash.Hash { panic("boringcrypto: not available") }
func NewSHA512() hash.Hash { panic("boringcrypto: not available") }

func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: not available") }

0 comments on commit 74e33c4

Please sign in to comment.