| // 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 && !cmd_go_bootstrap && !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...) |
| } |