hmac.mx raw

   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 = []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