Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions keccak_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ func (s *sponge) Read(out []byte) (int, error) {

n := len(out)
for len(out) > 0 {
x := copy(out, s.state[s.readIdx:rate])
s.readIdx += x
out = out[x:]
if s.readIdx == rate {
keccakF1600(&s.state)
s.readIdx = 0
}
x := copy(out, s.state[s.readIdx:rate])
s.readIdx += x
out = out[x:]
}
return n, nil
}
Expand Down
55 changes: 55 additions & 0 deletions keccak_asm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
package keccak

import (
"bytes"
"math/rand"
"strconv"
"testing"

"golang.org/x/crypto/sha3"
)

// TestXorAndPermute verifies the fused XOR+permute assembly entry point
Expand Down Expand Up @@ -34,3 +38,54 @@ func TestXorAndPermute(t *testing.T) {
}
}
}

// TestSpongeReadDefersBoundaryPermutation covers Read(rate) followed by
// Read(k), the call boundary neither TestReadMatchesXCrypto (one read per
// hasher) nor TestReadMultipleCalls (chunks of 37) lands on. Deferring the
// permutation is output-neutral, so readIdx is what pins the deferral; the
// byte comparison catches a reorder that permutes the wrong number of times.
func TestSpongeReadDefersBoundaryPermutation(t *testing.T) {
if !useASM {
t.Skip("hardware acceleration unavailable on this CPU")
}
const input = "input"
for _, k := range []int{1, 7, rate - 1, rate, rate + 1} {
t.Run(strconv.Itoa(k), func(t *testing.T) {
ref := sha3.NewLegacyKeccak256().(KeccakState)
ref.Write([]byte(input))
want := make([]byte, rate+k)
ref.Read(want)

var s sponge
s.Write([]byte(input))
got := make([]byte, rate+k)
s.Read(got[:rate])
if s.readIdx != rate {
t.Fatalf("read index after one full block = %d, want %d", s.readIdx, rate)
}
s.Read(got[rate:])

if !bytes.Equal(got, want) {
t.Fatalf("Read(%d)+Read(%d) = %x, want %x", rate, k, got, want)
}
})
}
}

func BenchmarkSpongeRead(b *testing.B) {
if !useASM {
b.Skip("hardware acceleration unavailable on this CPU")
}
for _, size := range []int{rate - 1, rate, rate + 1} {
b.Run(strconv.Itoa(size), func(b *testing.B) {
out := make([]byte, size)
b.SetBytes(int64(size))
b.ReportAllocs()
for b.Loop() {
var h sponge
h.Write([]byte("input"))
h.Read(out)
}
})
}
}