Skip to content

Commit 75e3e1b

Browse files
authored
Merge pull request #80 from microsoft/cshake
Add support for CSHAKE
2 parents af9d6d7 + f7a768e commit 75e3e1b

File tree

5 files changed

+655
-116
lines changed

5 files changed

+655
-116
lines changed

cng/hash.go

Lines changed: 87 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
2023
func 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.
10991
func 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-
153120
type 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].
184152
type 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

208174
func (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

225190
func (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

237203
func (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

244210
func (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

260217
func (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

270222
func (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+
281235
func (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
}

cng/hash_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ func cryptoToHash(h crypto.Hash) func() hash.Hash {
3232
case crypto.SHA512:
3333
return cng.NewSHA512
3434
case crypto.SHA3_256:
35-
return cng.NewSHA3_256
35+
return func() hash.Hash { return cng.NewSHA3_256() }
3636
case crypto.SHA3_384:
37-
return cng.NewSHA3_384
37+
return func() hash.Hash { return cng.NewSHA3_384() }
3838
case crypto.SHA3_512:
39-
return cng.NewSHA3_512
39+
return func() hash.Hash { return cng.NewSHA3_512() }
4040
}
4141
return nil
4242
}
@@ -156,15 +156,15 @@ func TestHash_OneShot(t *testing.T) {
156156
return b[:]
157157
}},
158158
{crypto.SHA3_256, func(p []byte) []byte {
159-
b := cng.SHA3_256(p)
159+
b := cng.SumSHA3_256(p)
160160
return b[:]
161161
}},
162162
{crypto.SHA3_384, func(p []byte) []byte {
163-
b := cng.SHA3_384(p)
163+
b := cng.SumSHA3_384(p)
164164
return b[:]
165165
}},
166166
{crypto.SHA3_512, func(p []byte) []byte {
167-
b := cng.SHA3_512(p)
167+
b := cng.SumSHA3_512(p)
168168
return b[:]
169169
}},
170170
}

0 commit comments

Comments
 (0)