Skip to content

Commit f416eba

Browse files
Kris KwiatkowskiFiloSottile
Kris Kwiatkowski
authored andcommitted
sha3: add cSHAKE support
This patch implements 128- and 256-bit version of customizable variant of SHAKE function (cSHAKE). * Implementation based on NIST FIPS 202 * Test data file has been updated with cSHAKE KATs. I've copied examples from NIST document available here: https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and -guidelines/documents/examples/cshake_samples.pdf Fixes #25395 Change-Id: Icbbc4232f3d9a28b3d6ead51937c2e60c00e5d8c Reviewed-on: https://go-review.googlesource.com/c/crypto/+/111281 Reviewed-by: Filippo Valsorda <[email protected]>
1 parent 92d88b0 commit f416eba

File tree

3 files changed

+270
-30
lines changed

3 files changed

+270
-30
lines changed

Diff for: sha3/sha3_test.go

+162-25
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,6 @@ const (
2727
katFilename = "testdata/keccakKats.json.deflate"
2828
)
2929

30-
// Internal-use instances of SHAKE used to test against KATs.
31-
func newHashShake128() hash.Hash {
32-
return &state{rate: 168, dsbyte: 0x1f, outputLen: 512}
33-
}
34-
func newHashShake256() hash.Hash {
35-
return &state{rate: 136, dsbyte: 0x1f, outputLen: 512}
36-
}
37-
3830
// testDigests contains functions returning hash.Hash instances
3931
// with output-length equal to the KAT length for SHA-3, Keccak
4032
// and SHAKE instances.
@@ -45,15 +37,20 @@ var testDigests = map[string]func() hash.Hash{
4537
"SHA3-512": New512,
4638
"Keccak-256": NewLegacyKeccak256,
4739
"Keccak-512": NewLegacyKeccak512,
48-
"SHAKE128": newHashShake128,
49-
"SHAKE256": newHashShake256,
5040
}
5141

52-
// testShakes contains functions that return ShakeHash instances for
53-
// testing the ShakeHash-specific interface.
54-
var testShakes = map[string]func() ShakeHash{
55-
"SHAKE128": NewShake128,
56-
"SHAKE256": NewShake256,
42+
// testShakes contains functions that return sha3.ShakeHash instances for
43+
// with output-length equal to the KAT length.
44+
var testShakes = map[string]struct {
45+
constructor func(N []byte, S []byte) ShakeHash
46+
defAlgoName string
47+
defCustomStr string
48+
}{
49+
// NewCShake without customization produces same result as SHAKE
50+
"SHAKE128": {NewCShake128, "", ""},
51+
"SHAKE256": {NewCShake256, "", ""},
52+
"cSHAKE128": {NewCShake128, "CSHAKE128", "CustomStrign"},
53+
"cSHAKE256": {NewCShake256, "CSHAKE256", "CustomStrign"},
5754
}
5855

5956
// decodeHex converts a hex-encoded string into a raw byte string.
@@ -71,6 +68,10 @@ type KeccakKats struct {
7168
Digest string `json:"digest"`
7269
Length int64 `json:"length"`
7370
Message string `json:"message"`
71+
72+
// Defined only for cSHAKE
73+
N string `json:"N"`
74+
S string `json:"S"`
7475
}
7576
}
7677

@@ -103,10 +104,9 @@ func TestKeccakKats(t *testing.T) {
103104
t.Errorf("error decoding KATs: %s", err)
104105
}
105106

106-
// Do the KATs.
107-
for functionName, kats := range katSet.Kats {
108-
d := testDigests[functionName]()
109-
for _, kat := range kats {
107+
for algo, function := range testDigests {
108+
d := function()
109+
for _, kat := range katSet.Kats[algo] {
110110
d.Reset()
111111
in, err := hex.DecodeString(kat.Message)
112112
if err != nil {
@@ -115,8 +115,39 @@ func TestKeccakKats(t *testing.T) {
115115
d.Write(in[:kat.Length/8])
116116
got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
117117
if got != kat.Digest {
118-
t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
119-
functionName, impl, kat.Length, kat.Message, got, kat.Digest)
118+
t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
119+
algo, impl, kat.Length, kat.Message, got, kat.Digest)
120+
t.Logf("wanted %+v", kat)
121+
t.FailNow()
122+
}
123+
continue
124+
}
125+
}
126+
127+
for algo, v := range testShakes {
128+
for _, kat := range katSet.Kats[algo] {
129+
N, err := hex.DecodeString(kat.N)
130+
if err != nil {
131+
t.Errorf("error decoding KAT: %s", err)
132+
}
133+
134+
S, err := hex.DecodeString(kat.S)
135+
if err != nil {
136+
t.Errorf("error decoding KAT: %s", err)
137+
}
138+
d := v.constructor(N, S)
139+
in, err := hex.DecodeString(kat.Message)
140+
if err != nil {
141+
t.Errorf("error decoding KAT: %s", err)
142+
}
143+
144+
d.Write(in[:kat.Length/8])
145+
out := make([]byte, len(kat.Digest)/2)
146+
d.Read(out)
147+
got := strings.ToUpper(hex.EncodeToString(out))
148+
if got != kat.Digest {
149+
t.Errorf("function=%s, implementation=%s, length=%d N:%s\n S:%s\nmessage:\n %s \ngot:\n %s\nwanted:\n %s",
150+
algo, impl, kat.Length, kat.N, kat.S, kat.Message, got, kat.Digest)
120151
t.Logf("wanted %+v", kat)
121152
t.FailNow()
122153
}
@@ -184,6 +215,34 @@ func TestUnalignedWrite(t *testing.T) {
184215
t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
185216
}
186217
}
218+
219+
// Same for SHAKE
220+
for alg, df := range testShakes {
221+
want := make([]byte, 16)
222+
got := make([]byte, 16)
223+
d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr))
224+
225+
d.Reset()
226+
d.Write(buf)
227+
d.Read(want)
228+
d.Reset()
229+
for i := 0; i < len(buf); {
230+
// Cycle through offsets which make a 137 byte sequence.
231+
// Because 137 is prime this sequence should exercise all corner cases.
232+
offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
233+
for _, j := range offsets {
234+
if v := len(buf) - i; v < j {
235+
j = v
236+
}
237+
d.Write(buf[i : i+j])
238+
i += j
239+
}
240+
}
241+
d.Read(got)
242+
if !bytes.Equal(got, want) {
243+
t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
244+
}
245+
}
187246
})
188247
}
189248

@@ -225,13 +284,13 @@ func TestAppendNoRealloc(t *testing.T) {
225284
// the same output as repeatedly squeezing the instance.
226285
func TestSqueezing(t *testing.T) {
227286
testUnalignedAndGeneric(t, func(impl string) {
228-
for functionName, newShakeHash := range testShakes {
229-
d0 := newShakeHash()
287+
for algo, v := range testShakes {
288+
d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
230289
d0.Write([]byte(testString))
231290
ref := make([]byte, 32)
232291
d0.Read(ref)
233292

234-
d1 := newShakeHash()
293+
d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
235294
d1.Write([]byte(testString))
236295
var multiple []byte
237296
for range ref {
@@ -240,7 +299,7 @@ func TestSqueezing(t *testing.T) {
240299
multiple = append(multiple, one...)
241300
}
242301
if !bytes.Equal(ref, multiple) {
243-
t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref))
302+
t.Errorf("%s (%s): squeezing %d bytes one at a time failed", algo, impl, len(ref))
244303
}
245304
}
246305
})
@@ -255,6 +314,50 @@ func sequentialBytes(size int) []byte {
255314
return result
256315
}
257316

317+
func TestReset(t *testing.T) {
318+
out1 := make([]byte, 32)
319+
out2 := make([]byte, 32)
320+
321+
for _, v := range testShakes {
322+
// Calculate hash for the first time
323+
c := v.constructor(nil, []byte{0x99, 0x98})
324+
c.Write(sequentialBytes(0x100))
325+
c.Read(out1)
326+
327+
// Calculate hash again
328+
c.Reset()
329+
c.Write(sequentialBytes(0x100))
330+
c.Read(out2)
331+
332+
if !bytes.Equal(out1, out2) {
333+
t.Error("\nExpected:\n", out1, "\ngot:\n", out2)
334+
}
335+
}
336+
}
337+
338+
func TestClone(t *testing.T) {
339+
out1 := make([]byte, 16)
340+
out2 := make([]byte, 16)
341+
in := sequentialBytes(0x100)
342+
343+
for _, v := range testShakes {
344+
h1 := v.constructor(nil, []byte{0x01})
345+
h1.Write([]byte{0x01})
346+
347+
h2 := h1.Clone()
348+
349+
h1.Write(in)
350+
h1.Read(out1)
351+
352+
h2.Write(in)
353+
h2.Read(out2)
354+
355+
if !bytes.Equal(out1, out2) {
356+
t.Error("\nExpected:\n", hex.EncodeToString(out1), "\ngot:\n", hex.EncodeToString(out2))
357+
}
358+
}
359+
}
360+
258361
// BenchmarkPermutationFunction measures the speed of the permutation function
259362
// with no input data.
260363
func BenchmarkPermutationFunction(b *testing.B) {
@@ -341,3 +444,37 @@ func Example_mac() {
341444
fmt.Printf("%x\n", h)
342445
// Output: 78de2974bd2711d5549ffd32b753ef0f5fa80a0db2556db60f0987eb8a9218ff
343446
}
447+
448+
func ExampleNewCShake256() {
449+
out := make([]byte, 32)
450+
msg := []byte("The quick brown fox jumps over the lazy dog")
451+
452+
// Example 1: Simple cshake
453+
c1 := NewCShake256([]byte("NAME"), []byte("Partition1"))
454+
c1.Write(msg)
455+
c1.Read(out)
456+
fmt.Println(hex.EncodeToString(out))
457+
458+
// Example 2: Different customization string produces different digest
459+
c1 = NewCShake256([]byte("NAME"), []byte("Partition2"))
460+
c1.Write(msg)
461+
c1.Read(out)
462+
fmt.Println(hex.EncodeToString(out))
463+
464+
// Example 3: Longer output length produces longer digest
465+
out = make([]byte, 64)
466+
c1 = NewCShake256([]byte("NAME"), []byte("Partition1"))
467+
c1.Write(msg)
468+
c1.Read(out)
469+
fmt.Println(hex.EncodeToString(out))
470+
471+
// Example 4: Next read produces different result
472+
c1.Read(out)
473+
fmt.Println(hex.EncodeToString(out))
474+
475+
// Output:
476+
//a90a4c6ca9af2156eba43dc8398279e6b60dcd56fb21837afe6c308fd4ceb05b
477+
//a8db03e71f3e4da5c4eee9d28333cdd355f51cef3c567e59be5beb4ecdbb28f0
478+
//a90a4c6ca9af2156eba43dc8398279e6b60dcd56fb21837afe6c308fd4ceb05b9dd98c6ee866ca7dc5a39d53e960f400bcd5a19c8a2d6ec6459f63696543a0d8
479+
//85e73a72228d08b46515553ca3a29d47df3047e5d84b12d6c2c63e579f4fd1105716b7838e92e981863907f434bfd4443c9e56ea09da998d2f9b47db71988109
480+
}

0 commit comments

Comments
 (0)