From 065bb6eb3012d2dcdd8a58b7f772a1542af4520f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 3 Aug 2017 00:02:43 -0400 Subject: [dev.boringcrypto.go1.8] crypto/hmac: use BoringCrypto Change-Id: Id4019d601c615b4835b0337d82be3d508292810e Reviewed-on: https://go-review.googlesource.com/55475 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot Reviewed-by: Adam Langley Reviewed-on: https://go-review.googlesource.com/57936 --- src/crypto/hmac/hmac.go | 8 ++ src/crypto/internal/boring/hmac.go | 137 ++++++++++++++++++++++++++++++++ src/crypto/internal/boring/notboring.go | 2 + 3 files changed, 147 insertions(+) create mode 100644 src/crypto/internal/boring/hmac.go diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 9ef9c448ee..6996963660 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -22,6 +22,7 @@ timing side-channels: package hmac import ( + "crypto/internal/boring" "crypto/subtle" "hash" ) @@ -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() diff --git a/src/crypto/internal/boring/hmac.go b/src/crypto/internal/boring/hmac.go new file mode 100644 index 0000000000..a70bc5ee8b --- /dev/null +++ b/src/crypto/internal/boring/hmac.go @@ -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...) +} diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index 6cd1413239..727247bc61 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -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") } -- cgit v1.2.3-54-g00ecf