// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package keccak // Tests include all the ShortMsgKATs provided by the Keccak team at // https://github.com/gvanas/KeccakCodePackage // // They only include the zero-bit case of the bitwise testvectors // published by NIST in the draft of FIPS-202. import ( "bytes" "compress/flate" "encoding" "encoding/hex" "encoding/json" "hash" "math/rand" "os" "strings" "testing" ) const ( testString = "brekeccakkeccak koax koax" katFilename = "testdata/keccakKats.json.deflate" ) // testDigests contains functions returning hash.Hash instances // with output-length equal to the KAT length for SHA-3, Keccak // and SHAKE instances. var testDigests = map[string]func() hash.Hash{ "Keccak-256": NewLegacyKeccak256, "Keccak-512": NewLegacyKeccak512, } // decodeHex converts a hex-encoded string into a raw byte string. func decodeHex(s string) []byte { b, err := hex.DecodeString(s) if err != nil { panic(err) } return b } // structs used to marshal JSON test-cases. type KeccakKats struct { Kats map[string][]struct { Digest string `json:"digest"` Length int64 `json:"length"` Message string `json:"message"` // Defined only for cSHAKE N string `json:"N"` S string `json:"S"` } } // TestKeccakKats tests the SHA-3 and Shake implementations against all the // ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage // (The testvectors are stored in keccakKats.json.deflate due to their length.) func TestKeccakKats(t *testing.T) { // Read the KATs. deflated, err := os.Open(katFilename) if err != nil { t.Errorf("error opening %s: %s", katFilename, err) } file := flate.NewReader(deflated) dec := json.NewDecoder(file) var katSet KeccakKats err = dec.Decode(&katSet) if err != nil { t.Errorf("error decoding KATs: %s", err) } for algo, function := range testDigests { d := function() for _, kat := range katSet.Kats[algo] { d.Reset() in, err := hex.DecodeString(kat.Message) if err != nil { t.Errorf("error decoding KAT: %s", err) } d.Write(in[:kat.Length/8]) got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) if got != kat.Digest { t.Errorf("function=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s", algo, kat.Length, kat.Message, got, kat.Digest) t.Logf("wanted %+v", kat) t.FailNow() } continue } } } // TestKeccak does a basic test of the non-standardized Keccak hash functions. func TestKeccak(t *testing.T) { tests := []struct { fn func() hash.Hash data []byte want string }{ { NewLegacyKeccak256, []byte("abc"), "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", }, { NewLegacyKeccak512, []byte("abc"), "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", }, } for _, u := range tests { h := u.fn() h.Write(u.data) got := h.Sum(nil) want := decodeHex(u.want) if !bytes.Equal(got, want) { t.Errorf("unexpected hash for size %d: got '%x' want '%s'", h.Size()*8, got, u.want) } } } // TestUnalignedWrite tests that writing data in an arbitrary pattern with // small input buffers. func TestUnalignedWrite(t *testing.T) { buf := sequentialBytes(0x10000) for alg, df := range testDigests { d := df() d.Reset() d.Write(buf) want := d.Sum(nil) d.Reset() for i := 0; i < len(buf); { // Cycle through offsets which make a 137 byte sequence. // Because 137 is prime this sequence should exercise all corner cases. offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} for _, j := range offsets { if v := len(buf) - i; v < j { j = v } d.Write(buf[i : i+j]) i += j } } got := d.Sum(nil) if !bytes.Equal(got, want) { t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want) } } } // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. // // The alignment of each slice is intentionally randomized to detect alignment // issues in the implementation. See https://golang.org/issue/37644. // Ideally, the compiler should fuzz the alignment itself. // (See https://golang.org/issue/35128.) func sequentialBytes(size int) []byte { alignmentOffset := rand.Intn(8) result := make([]byte, size+alignmentOffset)[alignmentOffset:] for i := range result { result[i] = byte(i) } return result } func TestMarshalUnmarshal(t *testing.T) { t.Run("Keccak-256", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak256()) }) t.Run("Keccak-512", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak512()) }) } // TODO(filippo): move this to crypto/internal/cryptotest. func testMarshalUnmarshal(t *testing.T, h hash.Hash) { buf := make([]byte, 200) rand.Read(buf) n := rand.Intn(200) h.Write(buf) want := h.Sum(nil) h.Reset() h.Write(buf[:n]) b, err := h.(encoding.BinaryMarshaler).MarshalBinary() if err != nil { t.Errorf("MarshalBinary: %v", err) } h.Write(bytes.Repeat([]byte{0}, 200)) if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary(b); err != nil { t.Errorf("UnmarshalBinary: %v", err) } h.Write(buf[n:]) got := h.Sum(nil) if !bytes.Equal(got, want) { t.Errorf("got %x, want %x", got, want) } } // BenchmarkPermutationFunction measures the speed of the permutation function // with no input data. func BenchmarkPermutationFunction(b *testing.B) { b.SetBytes(int64(200)) var lanes [25]uint64 for i := 0; i < b.N; i++ { keccakF1600(&lanes) } }