| // 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. | |
| //go:build boringcrypto && linux && (amd64 || arm64) && !android && !msan | |
| package boring | |
| // #include "goboringcrypto.h" | |
| import "C" | |
| import ( | |
| "bytes" | |
| "crypto" | |
| "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 | |
| } | |
| // cryptoHashToMD converts a crypto.Hash | |
| // to a BoringCrypto *C.GO_EVP_MD. | |
| func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD { | |
| switch ch { | |
| case crypto.MD5: | |
| return C._goboringcrypto_EVP_md5() | |
| case crypto.MD5SHA1: | |
| return C._goboringcrypto_EVP_md5_sha1() | |
| case crypto.SHA1: | |
| return C._goboringcrypto_EVP_sha1() | |
| case crypto.SHA224: | |
| return C._goboringcrypto_EVP_sha224() | |
| case crypto.SHA256: | |
| return C._goboringcrypto_EVP_sha256() | |
| case crypto.SHA384: | |
| return C._goboringcrypto_EVP_sha384() | |
| case crypto.SHA512: | |
| 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 := bytes.Clone(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 | |
| ctx2 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 | |
| // Note: Because of the finalizer, any time h.ctx is passed to cgo, | |
| // that call must be followed by a call to runtime.KeepAlive(h), | |
| // to make sure h is not collected (and finalized) before the cgo | |
| // call returns. | |
| runtime.SetFinalizer(h, (*boringHMAC).finalize) | |
| } | |
| C._goboringcrypto_HMAC_CTX_init(&h.ctx) | |
| if C._goboringcrypto_HMAC_Init(&h.ctx, unsafe.Pointer(base(h.key)), 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") | |
| } | |
| runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure. | |
| h.sum = nil | |
| } | |
| func (h *boringHMAC) finalize() { | |
| C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx) | |
| } | |
| func (h *boringHMAC) Write(p []byte) (int, error) { | |
| if len(p) > 0 { | |
| C._goboringcrypto_HMAC_Update(&h.ctx, (*C.uint8_t)(unsafe.Pointer(&p[0])), C.size_t(len(p))) | |
| } | |
| runtime.KeepAlive(h) | |
| 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) | |
| } | |
| // Make copy of context because Go hash.Hash mandates | |
| // that Sum has no effect on the underlying stream. | |
| // In particular it is OK to Sum, then Write more, then Sum again, | |
| // and the second Sum acts as if the first didn't happen. | |
| C._goboringcrypto_HMAC_CTX_init(&h.ctx2) | |
| if C._goboringcrypto_HMAC_CTX_copy_ex(&h.ctx2, &h.ctx) == 0 { | |
| panic("boringcrypto: HMAC_CTX_copy_ex failed") | |
| } | |
| C._goboringcrypto_HMAC_Final(&h.ctx2, (*C.uint8_t)(unsafe.Pointer(&h.sum[0])), nil) | |
| C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx2) | |
| return append(in, h.sum...) | |
| } |