Skip to content

Commit 80ea76e

Browse files
FiloSottilegopherbot
authored andcommitted
sha3: fix padding for long cSHAKE parameters
We used to compute the incorrect value if len(initBlock) % rate == 0. Also, add a test vector for golang/go#66232, confirmed to fail on GOARCH=386 without CL 570876. Fixes golang/go#69169 Change-Id: I3f2400926fca111dd0ca1327d6b5975e51b28f96 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/616576 Reviewed-by: Andrew Ekstedt <andrew.ekstedt@gmail.com> Reviewed-by: Daniel McCarney <daniel@binaryparadox.net> Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org>
1 parent c17aa50 commit 80ea76e

File tree

2 files changed

+134
-22
lines changed

2 files changed

+134
-22
lines changed

sha3/sha3_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"encoding/json"
1818
"fmt"
1919
"hash"
20+
"io"
2021
"math/rand"
2122
"os"
2223
"strings"
@@ -375,6 +376,116 @@ func TestClone(t *testing.T) {
375376
}
376377
}
377378

379+
func TestCSHAKEAccumulated(t *testing.T) {
380+
// Generated with pycryptodome@3.20.0
381+
//
382+
// from Crypto.Hash import cSHAKE128
383+
// rng = cSHAKE128.new()
384+
// acc = cSHAKE128.new()
385+
// for n in range(200):
386+
// N = rng.read(n)
387+
// for s in range(200):
388+
// S = rng.read(s)
389+
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
390+
// c.update(rng.read(100))
391+
// acc.update(c.read(200))
392+
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
393+
// c.update(rng.read(168))
394+
// acc.update(c.read(200))
395+
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
396+
// c.update(rng.read(200))
397+
// acc.update(c.read(200))
398+
// print(acc.read(32).hex())
399+
//
400+
// and with @noble/hashes@v1.5.0
401+
//
402+
// import { bytesToHex } from "@noble/hashes/utils";
403+
// import { cshake128 } from "@noble/hashes/sha3-addons";
404+
// const rng = cshake128.create();
405+
// const acc = cshake128.create();
406+
// for (let n = 0; n < 200; n++) {
407+
// const N = rng.xof(n);
408+
// for (let s = 0; s < 200; s++) {
409+
// const S = rng.xof(s);
410+
// let c = cshake128.create({ NISTfn: N, personalization: S });
411+
// c.update(rng.xof(100));
412+
// acc.update(c.xof(200));
413+
// c = cshake128.create({ NISTfn: N, personalization: S });
414+
// c.update(rng.xof(168));
415+
// acc.update(c.xof(200));
416+
// c = cshake128.create({ NISTfn: N, personalization: S });
417+
// c.update(rng.xof(200));
418+
// acc.update(c.xof(200));
419+
// }
420+
// }
421+
// console.log(bytesToHex(acc.xof(32)));
422+
//
423+
t.Run("cSHAKE128", func(t *testing.T) {
424+
testCSHAKEAccumulated(t, NewCShake128, rate128,
425+
"bb14f8657c6ec5403d0b0e2ef3d3393497e9d3b1a9a9e8e6c81dbaa5fd809252")
426+
})
427+
t.Run("cSHAKE256", func(t *testing.T) {
428+
testCSHAKEAccumulated(t, NewCShake256, rate256,
429+
"0baaf9250c6e25f0c14ea5c7f9bfde54c8a922c8276437db28f3895bdf6eeeef")
430+
})
431+
}
432+
433+
func testCSHAKEAccumulated(t *testing.T, newCShake func(N, S []byte) ShakeHash, rate int64, exp string) {
434+
rnd := newCShake(nil, nil)
435+
acc := newCShake(nil, nil)
436+
for n := 0; n < 200; n++ {
437+
N := make([]byte, n)
438+
rnd.Read(N)
439+
for s := 0; s < 200; s++ {
440+
S := make([]byte, s)
441+
rnd.Read(S)
442+
443+
c := newCShake(N, S)
444+
io.CopyN(c, rnd, 100 /* < rate */)
445+
io.CopyN(acc, c, 200)
446+
447+
c.Reset()
448+
io.CopyN(c, rnd, rate)
449+
io.CopyN(acc, c, 200)
450+
451+
c.Reset()
452+
io.CopyN(c, rnd, 200 /* > rate */)
453+
io.CopyN(acc, c, 200)
454+
}
455+
}
456+
if got := hex.EncodeToString(acc.Sum(nil)[:32]); got != exp {
457+
t.Errorf("got %s, want %s", got, exp)
458+
}
459+
}
460+
461+
func TestCSHAKELargeS(t *testing.T) {
462+
if testing.Short() {
463+
t.Skip("skipping test in short mode.")
464+
}
465+
466+
// See https://go.dev/issue/66232.
467+
const s = (1<<32)/8 + 1000 // s * 8 > 2^32
468+
S := make([]byte, s)
469+
rnd := NewShake128()
470+
rnd.Read(S)
471+
c := NewCShake128(nil, S)
472+
io.CopyN(c, rnd, 1000)
473+
474+
// Generated with pycryptodome@3.20.0
475+
//
476+
// from Crypto.Hash import cSHAKE128
477+
// rng = cSHAKE128.new()
478+
// S = rng.read(536871912)
479+
// c = cSHAKE128.new(custom=S)
480+
// c.update(rng.read(1000))
481+
// print(c.read(32).hex())
482+
//
483+
exp := "2cb9f237767e98f2614b8779cf096a52da9b3a849280bbddec820771ae529cf0"
484+
if got := hex.EncodeToString(c.Sum(nil)); got != exp {
485+
t.Errorf("got %s, want %s", got, exp)
486+
}
487+
}
488+
378489
// BenchmarkPermutationFunction measures the speed of the permutation function
379490
// with no input data.
380491
func BenchmarkPermutationFunction(b *testing.B) {

sha3/shake.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"encoding/binary"
2020
"hash"
2121
"io"
22+
"math/bits"
2223
)
2324

2425
// ShakeHash defines the interface to hash functions that support
@@ -58,33 +59,33 @@ const (
5859
rate256 = 136
5960
)
6061

61-
func bytepad(input []byte, w int) []byte {
62-
// leftEncode always returns max 9 bytes
63-
buf := make([]byte, 0, 9+len(input)+w)
64-
buf = append(buf, leftEncode(uint64(w))...)
65-
buf = append(buf, input...)
66-
padlen := w - (len(buf) % w)
67-
return append(buf, make([]byte, padlen)...)
68-
}
69-
70-
func leftEncode(value uint64) []byte {
71-
var b [9]byte
72-
binary.BigEndian.PutUint64(b[1:], value)
73-
// Trim all but last leading zero bytes
74-
i := byte(1)
75-
for i < 8 && b[i] == 0 {
76-
i++
62+
func bytepad(data []byte, rate int) []byte {
63+
out := make([]byte, 0, 9+len(data)+rate-1)
64+
out = append(out, leftEncode(uint64(rate))...)
65+
out = append(out, data...)
66+
if padlen := rate - len(out)%rate; padlen < rate {
67+
out = append(out, make([]byte, padlen)...)
7768
}
78-
// Prepend number of encoded bytes
79-
b[i-1] = 9 - i
80-
return b[i-1:]
69+
return out
70+
}
71+
72+
func leftEncode(x uint64) []byte {
73+
// Let n be the smallest positive integer for which 2^(8n) > x.
74+
n := (bits.Len64(x) + 7) / 8
75+
if n == 0 {
76+
n = 1
77+
}
78+
// Return n || x with n as a byte and x an n bytes in big-endian order.
79+
b := make([]byte, 9)
80+
binary.BigEndian.PutUint64(b[1:], x)
81+
b = b[9-n-1:]
82+
b[0] = byte(n)
83+
return b
8184
}
8285

8386
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
8487
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
85-
86-
// leftEncode returns max 9 bytes
87-
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
88+
c.initBlock = make([]byte, 0, 9+len(N)+9+len(S)) // leftEncode returns max 9 bytes
8889
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N))*8)...)
8990
c.initBlock = append(c.initBlock, N...)
9091
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S))*8)...)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy