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
7package boring
8
9// #include "goboringcrypto.h"
10import "C"
11import (
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.
21func 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.
39func 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.
63func 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
82type 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
93func (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
117func (h *boringHMAC) finalize() {
118	C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx)
119}
120
121func (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
129func (h *boringHMAC) Size() int {
130	return h.size
131}
132
133func (h *boringHMAC) BlockSize() int {
134	return h.blockSize
135}
136
137func (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