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