@@ -13,11 +13,6 @@ import (
13
13
14
14
var OSSL_MAC_PARAM_DIGEST = C .CString ("digest" )
15
15
16
- var (
17
- fetchHMACOnce sync.Once
18
- evpHMAC C.GO_EVP_MAC_PTR
19
- )
20
-
21
16
// NewHMAC returns a new HMAC using OpenSSL.
22
17
// The function h must return a hash implemented by
23
18
// OpenSSL (for example, h could be openssl.NewSHA256).
@@ -38,14 +33,29 @@ func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
38
33
key = make ([]byte , C .GO_EVP_MAX_MD_SIZE )
39
34
}
40
35
36
+ hmac := & opensslHMAC {
37
+ size : ch .Size (),
38
+ blockSize : ch .BlockSize (),
39
+ }
40
+
41
41
switch vMajor {
42
42
case 1 :
43
- return newHMAC1 (key , ch , md )
43
+ ctx := newHMAC1 (key , md )
44
+ if ctx .ctx == nil {
45
+ return nil
46
+ }
47
+ hmac .ctx1 = ctx
44
48
case 3 :
45
- return newHMAC3 (key , ch , md )
49
+ ctx := newHMAC3 (key , md )
50
+ if ctx .ctx == nil {
51
+ return nil
52
+ }
53
+ hmac .ctx3 = ctx
46
54
default :
47
55
panic (errUnsupportedVersion ())
48
56
}
57
+ runtime .SetFinalizer (hmac , (* opensslHMAC ).finalize )
58
+ return hmac
49
59
}
50
60
51
61
// hmacCtx3 is used for OpenSSL 1.
@@ -67,50 +77,84 @@ type opensslHMAC struct {
67
77
sum []byte
68
78
}
69
79
70
- func newHMAC1 (key []byte , h hash. Hash , md C.GO_EVP_MD_PTR ) * opensslHMAC {
80
+ func newHMAC1 (key []byte , md C.GO_EVP_MD_PTR ) hmacCtx1 {
71
81
ctx := hmacCtxNew ()
72
82
if ctx == nil {
73
83
panic ("openssl: EVP_MAC_CTX_new failed" )
74
84
}
75
85
if C .go_openssl_HMAC_Init_ex (ctx , unsafe .Pointer (& key [0 ]), C .int (len (key )), md , nil ) == 0 {
76
86
panic (newOpenSSLError ("HMAC_Init_ex failed" ))
77
87
}
78
- hmac := & opensslHMAC {
79
- size : h .Size (),
80
- blockSize : h .BlockSize (),
81
- ctx1 : hmacCtx1 {ctx },
82
- }
83
- runtime .SetFinalizer (hmac , (* opensslHMAC ).finalize )
84
- return hmac
88
+ return hmacCtx1 {ctx }
85
89
}
86
90
87
- func newHMAC3 (key []byte , h hash.Hash , md C.GO_EVP_MD_PTR ) * opensslHMAC {
88
- fetchHMACOnce .Do (func () {
89
- name := C .CString ("HMAC" )
90
- evpHMAC = C .go_openssl_EVP_MAC_fetch (nil , name , nil )
91
- C .free (unsafe .Pointer (name ))
92
- })
93
- if evpHMAC == nil {
91
+ var hmacDigestsSupported sync.Map
92
+ var fetchHMAC3 = sync .OnceValue (func () C.GO_EVP_MAC_PTR {
93
+ name := C .CString ("HMAC" )
94
+ mac := C .go_openssl_EVP_MAC_fetch (nil , name , nil )
95
+ C .free (unsafe .Pointer (name ))
96
+ if mac == nil {
94
97
panic ("openssl: HMAC not supported" )
95
98
}
96
- ctx := C .go_openssl_EVP_MAC_CTX_new (evpHMAC )
97
- if ctx == nil {
98
- panic ("openssl: EVP_MAC_CTX_new failed" )
99
- }
100
- digest := C .go_openssl_EVP_MD_get0_name (md )
99
+ return mac
100
+ })
101
+
102
+ func buildHMAC3Params (digest * C.char ) C.GO_OSSL_PARAM_PTR {
101
103
bld := C .go_openssl_OSSL_PARAM_BLD_new ()
102
104
if bld == nil {
103
105
panic (newOpenSSLError ("OSSL_PARAM_BLD_new" ))
104
106
}
105
107
defer C .go_openssl_OSSL_PARAM_BLD_free (bld )
106
108
C .go_openssl_OSSL_PARAM_BLD_push_utf8_string (bld , OSSL_MAC_PARAM_DIGEST , digest , 0 )
107
- params := C .go_openssl_OSSL_PARAM_BLD_to_param (bld )
109
+ return C .go_openssl_OSSL_PARAM_BLD_to_param (bld )
110
+ }
111
+
112
+ func isHMAC3DigestSupported (digest string ) bool {
113
+ if v , ok := hmacDigestsSupported .Load (digest ); ok {
114
+ return v .(bool )
115
+ }
116
+ ctx := C .go_openssl_EVP_MAC_CTX_new (fetchHMAC3 ())
117
+ if ctx == nil {
118
+ panic (newOpenSSLError ("EVP_MAC_CTX_new" ))
119
+ }
120
+ defer C .go_openssl_EVP_MAC_CTX_free (ctx )
121
+
122
+ cdigest := C .CString (digest )
123
+ defer C .free (unsafe .Pointer (cdigest ))
124
+ params := buildHMAC3Params (cdigest )
125
+ if params == nil {
126
+ panic (newOpenSSLError ("OSSL_PARAM_BLD_to_param" ))
127
+ }
128
+ defer C .go_openssl_OSSL_PARAM_free (params )
129
+
130
+ supported := C .go_openssl_EVP_MAC_CTX_set_params (ctx , params ) != 0
131
+ hmacDigestsSupported .Store (digest , supported )
132
+ return supported
133
+ }
134
+
135
+ func newHMAC3 (key []byte , md C.GO_EVP_MD_PTR ) hmacCtx3 {
136
+ digest := C .go_openssl_EVP_MD_get0_name (md )
137
+ if ! isHMAC3DigestSupported (C .GoString (digest )) {
138
+ // The digest is not supported by the HMAC provider.
139
+ // Don't panic here so the Go standard library to
140
+ // fall back to the Go implementation.
141
+ // See https://github.com/golang-fips/openssl/issues/153.
142
+ return hmacCtx3 {}
143
+ }
144
+ params := buildHMAC3Params (digest )
108
145
if params == nil {
109
146
panic (newOpenSSLError ("OSSL_PARAM_BLD_to_param" ))
110
147
}
111
148
defer C .go_openssl_OSSL_PARAM_free (params )
149
+
150
+ ctx := C .go_openssl_EVP_MAC_CTX_new (fetchHMAC3 ())
151
+ if ctx == nil {
152
+ panic (newOpenSSLError ("EVP_MAC_CTX_new" ))
153
+ }
154
+
112
155
if C .go_openssl_EVP_MAC_init (ctx , base (key ), C .size_t (len (key )), params ) == 0 {
113
- panic (newOpenSSLError ("EVP_MAC_init failed" ))
156
+ C .go_openssl_EVP_MAC_CTX_free (ctx )
157
+ panic (newOpenSSLError ("EVP_MAC_init" ))
114
158
}
115
159
var hkey []byte
116
160
if vMinor == 0 && vPatch <= 2 {
@@ -122,13 +166,7 @@ func newHMAC3(key []byte, h hash.Hash, md C.GO_EVP_MD_PTR) *opensslHMAC {
122
166
hkey = make ([]byte , len (key ))
123
167
copy (hkey , key )
124
168
}
125
- hmac := & opensslHMAC {
126
- size : h .Size (),
127
- blockSize : h .BlockSize (),
128
- ctx3 : hmacCtx3 {ctx , hkey },
129
- }
130
- runtime .SetFinalizer (hmac , (* opensslHMAC ).finalize )
131
- return hmac
169
+ return hmacCtx3 {ctx , hkey }
132
170
}
133
171
134
172
func (h * opensslHMAC ) Reset () {
0 commit comments