@@ -16,6 +16,9 @@ import (
1616 "github.com/microsoft/go-crypto-winnative/internal/bcrypt"
1717)
1818
19+ // maxHashSize is the size of SHA512 and SHA3_512, the largest hashes we support.
20+ const maxHashSize = 64
21+
1922// SupportsHash returns true if a hash.Hash implementation is supported for h.
2023func SupportsHash (h crypto.Hash ) bool {
2124 switch h {
@@ -84,27 +87,6 @@ func SHA512(p []byte) (sum [64]byte) {
8487 return
8588}
8689
87- func SHA3_256 (p []byte ) (sum [32 ]byte ) {
88- if err := hashOneShot (bcrypt .SHA3_256_ALGORITHM , p , sum [:]); err != nil {
89- panic ("bcrypt: SHA3_256 failed" )
90- }
91- return
92- }
93-
94- func SHA3_384 (p []byte ) (sum [48 ]byte ) {
95- if err := hashOneShot (bcrypt .SHA3_384_ALGORITHM , p , sum [:]); err != nil {
96- panic ("bcrypt: SHA3_384 failed" )
97- }
98- return
99- }
100-
101- func SHA3_512 (p []byte ) (sum [64 ]byte ) {
102- if err := hashOneShot (bcrypt .SHA3_512_ALGORITHM , p , sum [:]); err != nil {
103- panic ("bcrypt: SHA3_512 failed" )
104- }
105- return
106- }
107-
10890// NewMD4 returns a new MD4 hash.
10991func NewMD4 () hash.Hash {
11092 return newHashX (bcrypt .MD4_ALGORITHM , bcrypt .ALG_NONE_FLAG , nil )
@@ -135,21 +117,6 @@ func NewSHA512() hash.Hash {
135117 return newHashX (bcrypt .SHA512_ALGORITHM , bcrypt .ALG_NONE_FLAG , nil )
136118}
137119
138- // NewSHA3_256 returns a new SHA256 hash.
139- func NewSHA3_256 () hash.Hash {
140- return newHashX (bcrypt .SHA3_256_ALGORITHM , bcrypt .ALG_NONE_FLAG , nil )
141- }
142-
143- // NewSHA3_384 returns a new SHA384 hash.
144- func NewSHA3_384 () hash.Hash {
145- return newHashX (bcrypt .SHA3_384_ALGORITHM , bcrypt .ALG_NONE_FLAG , nil )
146- }
147-
148- // NewSHA3_512 returns a new SHA512 hash.
149- func NewSHA3_512 () hash.Hash {
150- return newHashX (bcrypt .SHA3_512_ALGORITHM , bcrypt .ALG_NONE_FLAG , nil )
151- }
152-
153120type hashAlgorithm struct {
154121 handle bcrypt.ALG_HANDLE
155122 id string
@@ -181,11 +148,11 @@ func hashToID(h hash.Hash) string {
181148 return hx .alg .id
182149}
183150
151+ // hashX implements [hash.Hash].
184152type hashX struct {
185- alg * hashAlgorithm
186- _ctx bcrypt.HASH_HANDLE // access it using withCtx
153+ alg * hashAlgorithm
154+ ctx bcrypt.HASH_HANDLE
187155
188- buf []byte
189156 key []byte
190157}
191158
@@ -196,88 +163,75 @@ func newHashX(id string, flag bcrypt.AlgorithmProviderFlags, key []byte) *hashX
196163 panic (err )
197164 }
198165 h := & hashX {alg : alg , key : bytes .Clone (key )}
199- // Don't allocate hx.buf nor call bcrypt.CreateHash yet,
200- // which would be wasteful if the caller only wants to know
201- // the hash type. This is a common pattern in this package,
202- // as some functions accept a `func() hash.Hash` parameter
203- // and call it just to know the hash type.
204- runtime .SetFinalizer (h , (* hashX ).finalize )
166+ // Don't call bcrypt.CreateHash yet, it would be wasteful
167+ // if the caller only wants to know the hash type. This
168+ // is a common pattern in this package, as some functions
169+ // accept a `func() hash.Hash` parameter and call it just
170+ // to know the hash type.
205171 return h
206172}
207173
208174func (h * hashX ) finalize () {
209- if h ._ctx != 0 {
210- bcrypt .DestroyHash (h ._ctx )
211- }
175+ bcrypt .DestroyHash (h .ctx )
212176}
213177
214- func (h * hashX ) withCtx ( fn func ( ctx bcrypt. HASH_HANDLE ) error ) error {
178+ func (h * hashX ) init () {
215179 defer runtime .KeepAlive (h )
216- if h ._ctx == 0 {
217- err := bcrypt .CreateHash (h .alg .handle , & h ._ctx , nil , h .key , 0 )
218- if err != nil {
219- panic (err )
220- }
180+ if h .ctx != 0 {
181+ return
182+ }
183+ err := bcrypt .CreateHash (h .alg .handle , & h .ctx , nil , h .key , bcrypt .HASH_REUSABLE_FLAG )
184+ if err != nil {
185+ panic (err )
221186 }
222- return fn ( h . _ctx )
187+ runtime . SetFinalizer ( h , ( * hashX ). finalize )
223188}
224189
225190func (h * hashX ) Clone () (hash.Hash , error ) {
191+ defer runtime .KeepAlive (h )
226192 h2 := & hashX {alg : h .alg , key : bytes .Clone (h .key )}
227- err := h .withCtx (func (ctx bcrypt.HASH_HANDLE ) error {
228- return bcrypt .DuplicateHash (ctx , & h2 ._ctx , nil , 0 )
229- })
230- if err != nil {
231- return nil , err
193+ if h .ctx != 0 {
194+ err := bcrypt .DuplicateHash (h .ctx , & h2 .ctx , nil , 0 )
195+ if err != nil {
196+ return nil , err
197+ }
198+ runtime .SetFinalizer (h2 , (* hashX ).finalize )
232199 }
233- runtime .SetFinalizer (h2 , (* hashX ).finalize )
234200 return h2 , nil
235201}
236202
237203func (h * hashX ) Reset () {
238- if h . _ctx != 0 {
239- bcrypt . DestroyHash ( h . _ctx )
240- h . _ctx = 0
204+ defer runtime . KeepAlive ( h )
205+ if h . ctx != 0 {
206+ hashReset ( h . ctx , h . Size ())
241207 }
242208}
243209
244210func (h * hashX ) Write (p []byte ) (n int , err error ) {
245- err = h .withCtx (func (ctx bcrypt.HASH_HANDLE ) error {
246- for n < len (p ) && err == nil {
247- nn := len32 (p [n :])
248- err = bcrypt .HashData (h ._ctx , p [n :n + nn ], 0 )
249- n += nn
250- }
251- return err
252- })
253- if err != nil {
254- // hash.Hash interface mandates Write should never return an error.
255- panic (err )
256- }
211+ defer runtime .KeepAlive (h )
212+ h .init ()
213+ hashData (h .ctx , p )
257214 return len (p ), nil
258215}
259216
260217func (h * hashX ) WriteString (s string ) (int , error ) {
261- // TODO: use unsafe.StringData once we drop support
262- // for go1.19 and earlier.
263- hdr := (* struct {
264- Data * byte
265- Len int
266- })(unsafe .Pointer (& s ))
267- return h .Write (unsafe .Slice (hdr .Data , len (s )))
218+ defer runtime .KeepAlive (h )
219+ return h .Write (unsafe .Slice (unsafe .StringData (s ), len (s )))
268220}
269221
270222func (h * hashX ) WriteByte (c byte ) error {
271- err := h .withCtx (func (ctx bcrypt.HASH_HANDLE ) error {
272- return bcrypt .HashDataRaw (h ._ctx , & c , 1 , 0 )
273- })
274- if err != nil {
275- // hash.Hash interface mandates Write should never return an error.
276- panic (err )
277- }
223+ defer runtime .KeepAlive (h )
224+ h .init ()
225+ hashByte (h .ctx , c )
278226 return nil
279227}
280228
229+ func (h * hashX ) Sum (in []byte ) []byte {
230+ defer runtime .KeepAlive (h )
231+ h .init ()
232+ return hashSum (h .ctx , h .Size (), in )
233+ }
234+
281235func (h * hashX ) Size () int {
282236 return int (h .alg .size )
283237}
@@ -286,21 +240,55 @@ func (h *hashX) BlockSize() int {
286240 return int (h .alg .blockSize )
287241}
288242
289- func (h * hashX ) Sum (in []byte ) []byte {
243+ // hashData writes p to ctx. It panics on error.
244+ func hashData (ctx bcrypt.HASH_HANDLE , p []byte ) {
245+ var n int
246+ var err error
247+ for n < len (p ) && err == nil {
248+ nn := len32 (p [n :])
249+ err = bcrypt .HashData (ctx , p [n :n + nn ], 0 )
250+ n += nn
251+ }
252+ if err != nil {
253+ panic (err )
254+ }
255+ }
256+
257+ // hashByte writes c to ctx. It panics on error.
258+ func hashByte (ctx bcrypt.HASH_HANDLE , c byte ) {
259+ err := bcrypt .HashDataRaw (ctx , & c , 1 , 0 )
260+ if err != nil {
261+ panic (err )
262+ }
263+ }
264+
265+ // hashSum writes the hash of ctx to in and returns the result.
266+ // size is the size of the hash output.
267+ // It panics on error.
268+ func hashSum (ctx bcrypt.HASH_HANDLE , size int , in []byte ) []byte {
290269 var ctx2 bcrypt.HASH_HANDLE
291- err := h .withCtx (func (ctx bcrypt.HASH_HANDLE ) error {
292- return bcrypt .DuplicateHash (ctx , & ctx2 , nil , 0 )
293- })
270+ err := bcrypt .DuplicateHash (ctx , & ctx2 , nil , 0 )
294271 if err != nil {
295272 panic (err )
296273 }
297274 defer bcrypt .DestroyHash (ctx2 )
298- if h .buf == nil {
299- h .buf = make ([]byte , h .alg .size )
300- }
301- err = bcrypt .FinishHash (ctx2 , h .buf , 0 )
275+ buf := make ([]byte , size , maxHashSize ) // explicit cap to allow stack allocation
276+ err = bcrypt .FinishHash (ctx2 , buf , 0 )
302277 if err != nil {
303278 panic (err )
304279 }
305- return append (in , h .buf ... )
280+ return append (in , buf ... )
281+ }
282+
283+ // hashReset resets the hash state of ctx.
284+ // size is the size of the hash output.
285+ // It panics on error.
286+ func hashReset (ctx bcrypt.HASH_HANDLE , size int ) {
287+ // bcrypt.FinishHash expects the output buffer to match the hash size.
288+ // We don't care about the output, so we just pass a stack-allocated buffer
289+ // that is large enough to hold the largest hash size we support.
290+ var discard [maxHashSize ]byte
291+ if err := bcrypt .FinishHash (ctx , discard [:size ], 0 ); err != nil {
292+ panic (err )
293+ }
306294}
0 commit comments