Source file src/crypto/internal/boring/hmac.go

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build boringcrypto && linux && (amd64 || arm64) && !android && !msan
     6  
     7  package boring
     8  
     9  // #include "goboringcrypto.h"
    10  import "C"
    11  import (
    12  	"bytes"
    13  	"crypto"
    14  	"hash"
    15  	"runtime"
    16  	"unsafe"
    17  )
    18  
    19  // hashToMD converts a hash.Hash implementation from this package
    20  // to a BoringCrypto *C.GO_EVP_MD.
    21  func hashToMD(h hash.Hash) *C.GO_EVP_MD {
    22  	switch h.(type) {
    23  	case *sha1Hash:
    24  		return C._goboringcrypto_EVP_sha1()
    25  	case *sha224Hash:
    26  		return C._goboringcrypto_EVP_sha224()
    27  	case *sha256Hash:
    28  		return C._goboringcrypto_EVP_sha256()
    29  	case *sha384Hash:
    30  		return C._goboringcrypto_EVP_sha384()
    31  	case *sha512Hash:
    32  		return C._goboringcrypto_EVP_sha512()
    33  	}
    34  	return nil
    35  }
    36  
    37  // cryptoHashToMD converts a crypto.Hash
    38  // to a BoringCrypto *C.GO_EVP_MD.
    39  func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD {
    40  	switch ch {
    41  	case crypto.MD5:
    42  		return C._goboringcrypto_EVP_md5()
    43  	case crypto.MD5SHA1:
    44  		return C._goboringcrypto_EVP_md5_sha1()
    45  	case crypto.SHA1:
    46  		return C._goboringcrypto_EVP_sha1()
    47  	case crypto.SHA224:
    48  		return C._goboringcrypto_EVP_sha224()
    49  	case crypto.SHA256:
    50  		return C._goboringcrypto_EVP_sha256()
    51  	case crypto.SHA384:
    52  		return C._goboringcrypto_EVP_sha384()
    53  	case crypto.SHA512:
    54  		return C._goboringcrypto_EVP_sha512()
    55  	}
    56  	return nil
    57  }
    58  
    59  // NewHMAC returns a new HMAC using BoringCrypto.
    60  // The function h must return a hash implemented by
    61  // BoringCrypto (for example, h could be boring.NewSHA256).
    62  // If h is not recognized, NewHMAC returns nil.
    63  func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
    64  	ch := h()
    65  	md := hashToMD(ch)
    66  	if md == nil {
    67  		return nil
    68  	}
    69  
    70  	// Note: Could hash down long keys here using EVP_Digest.
    71  	hkey := bytes.Clone(key)
    72  	hmac := &boringHMAC{
    73  		md:        md,
    74  		size:      ch.Size(),
    75  		blockSize: ch.BlockSize(),
    76  		key:       hkey,
    77  	}
    78  	hmac.Reset()
    79  	return hmac
    80  }
    81  
    82  type boringHMAC struct {
    83  	md          *C.GO_EVP_MD
    84  	ctx         C.GO_HMAC_CTX
    85  	ctx2        C.GO_HMAC_CTX
    86  	size        int
    87  	blockSize   int
    88  	key         []byte
    89  	sum         []byte
    90  	needCleanup bool
    91  }
    92  
    93  func (h *boringHMAC) Reset() {
    94  	if h.needCleanup {
    95  		C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
    96  	} else {
    97  		h.needCleanup = true
    98  		// Note: Because of the finalizer, any time h.ctx is passed to cgo,
    99  		// that call must be followed by a call to runtime.KeepAlive(h),
   100  		// to make sure h is not collected (and finalized) before the cgo
   101  		// call returns.
   102  		runtime.SetFinalizer(h, (*boringHMAC).finalize)
   103  	}
   104  	C._goboringcrypto_HMAC_CTX_init(&h.ctx)
   105  
   106  	if C._goboringcrypto_HMAC_Init(&h.ctx, unsafe.Pointer(base(h.key)), C.int(len(h.key)), h.md) == 0 {
   107  		panic("boringcrypto: HMAC_Init failed")
   108  	}
   109  	if int(C._goboringcrypto_HMAC_size(&h.ctx)) != h.size {
   110  		println("boringcrypto: HMAC size:", C._goboringcrypto_HMAC_size(&h.ctx), "!=", h.size)
   111  		panic("boringcrypto: HMAC size mismatch")
   112  	}
   113  	runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure.
   114  	h.sum = nil
   115  }
   116  
   117  func (h *boringHMAC) finalize() {
   118  	C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
   119  }
   120  
   121  func (h *boringHMAC) Write(p []byte) (int, error) {
   122  	if len(p) > 0 {
   123  		C._goboringcrypto_HMAC_Update(&h.ctx, (*C.uint8_t)(unsafe.Pointer(&p[0])), C.size_t(len(p)))
   124  	}
   125  	runtime.KeepAlive(h)
   126  	return len(p), nil
   127  }
   128  
   129  func (h *boringHMAC) Size() int {
   130  	return h.size
   131  }
   132  
   133  func (h *boringHMAC) BlockSize() int {
   134  	return h.blockSize
   135  }
   136  
   137  func (h *boringHMAC) Sum(in []byte) []byte {
   138  	if h.sum == nil {
   139  		size := h.Size()
   140  		h.sum = make([]byte, size)
   141  	}
   142  	// Make copy of context because Go hash.Hash mandates
   143  	// that Sum has no effect on the underlying stream.
   144  	// In particular it is OK to Sum, then Write more, then Sum again,
   145  	// and the second Sum acts as if the first didn't happen.
   146  	C._goboringcrypto_HMAC_CTX_init(&h.ctx2)
   147  	if C._goboringcrypto_HMAC_CTX_copy_ex(&h.ctx2, &h.ctx) == 0 {
   148  		panic("boringcrypto: HMAC_CTX_copy_ex failed")
   149  	}
   150  	C._goboringcrypto_HMAC_Final(&h.ctx2, (*C.uint8_t)(unsafe.Pointer(&h.sum[0])), nil)
   151  	C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx2)
   152  	return append(in, h.sum...)
   153  }
   154  

View as plain text