diff --git a/.travis.yml b/.travis.yml index 4ca8348..a03c8e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,9 @@ sudo: required language: go +go: + - 1.11.x + - 1.12.x + - master matrix: include: diff --git a/Makefile b/Makefile index da7812c..afec0cd 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,6 @@ MK_FILE_PATH = $(lastword $(MAKEFILE_LIST)) PRJ_DIR = $(abspath $(dir $(MK_FILE_PATH))) GO ?= go -GOPATH_LOCAL = $(PRJ_DIR)/build/ -GOPATH_DIR = src/github.com/henrydcase/nobs VENDOR_DIR = tls_vendor OPTS ?= -v NOASM ?= @@ -31,37 +29,19 @@ ifeq ($(DBG),1) OPTS_ENV+= GOTRACEBACK=crash # enable core dumps endif -TARGETS ?= \ - dh \ - drbg \ - ec \ - hash \ - kem \ - utils - -prep-%: - mkdir -p $(GOPATH_LOCAL)/$(GOPATH_DIR) - cp -rf $* $(GOPATH_LOCAL)/$(GOPATH_DIR)/$* - -make_dirs: - mkdir -p $(GOPATH_LOCAL)/$(GOPATH_DIR) - cp -rf etc $(GOPATH_LOCAL)/$(GOPATH_DIR) - -test: clean make_dirs $(addprefix prep-,$(TARGETS)) - cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test $(OPTS) $(TEST_PATH) +test: + $(OPTS_ENV) $(GO) test $(OPTS) $(TEST_PATH) cover: - cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test \ - -race -coverprofile=coverage_$(NOASM).txt -covermode=atomic $(OPTS) $(TEST_PATH) - cat $(GOPATH_LOCAL)/coverage_$(NOASM).txt >> coverage.txt + $(GO) test \ + -coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH) -bench: clean $(addprefix prep-,$(TARGETS)) - cd $(GOPATH_LOCAL); GOCACHE=$(GOCACHE) GOPATH=$(GOPATH_LOCAL) $(GO) test \ - $(BENCH_OPTS) $(TEST_PATH) +bench: + $(GO) test $(BENCH_OPTS) $(TEST_PATH) clean: - rm -rf $(GOPATH_LOCAL) rm -rf $(VENDOR_DIR) + rm -rf coverage.txt vendor-sidh-for-tls: clean mkdir -p $(VENDOR_DIR)/github_com/henrydcase/nobs/ @@ -69,7 +49,7 @@ vendor-sidh-for-tls: clean find $(VENDOR_DIR) -type f -print0 -name "*.go" | xargs -0 sed -i 's/github\.com/github_com/g' pprof-cpu: - $(GO) tool pprof $(GOPATH_LOCAL)/cpu.out + $(GO) tool pprof cpu.out pprof-mem: - $(GO) tool pprof $(GOPATH_LOCAL)/mem0.out + $(GO) tool pprof mem0.out diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..867b7fc --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/henrydcase/nobs + +go 1.12 diff --git a/hash/shake/sha3.go b/hash/shake/sha3.go index 8c8a945..2c01bb8 100644 --- a/hash/shake/sha3.go +++ b/hash/shake/sha3.go @@ -83,7 +83,7 @@ func (d *state) permute() { case spongeAbsorbing: // If we're absorbing, we need to xor the input into the state // before applying the permutation. - xorInUnaligned(d, d.buf) + xorIn(d, d.buf) d.buf = d.storage[:0] keccakF1600(&d.a) case spongeSqueezing: @@ -91,7 +91,7 @@ func (d *state) permute() { // copying more output. keccakF1600(&d.a) d.buf = d.storage[:d.rate] - copyOutUnaligned(d, d.buf) + copyOut(d, d.buf) } } @@ -119,7 +119,7 @@ func (d *state) padAndPermute(dsbyte byte) { d.permute() d.state = spongeSqueezing d.buf = d.storage[:d.rate] - copyOutUnaligned(d, d.buf) + copyOut(d, d.buf) } // Write absorbs more data into the hash's state. It produces an error @@ -136,7 +136,7 @@ func (d *state) Write(p []byte) (written int, err error) { for len(p) > 0 { if len(d.buf) == 0 && len(p) >= d.rate { // The fast path; absorb a full "rate" bytes of input and apply the permutation. - xorInUnaligned(d, p[:d.rate]) + xorIn(d, p[:d.rate]) p = p[d.rate:] keccakF1600(&d.a) } else { diff --git a/hash/shake/sha3_test.go b/hash/shake/sha3_test.go index a649e19..774d02f 100644 --- a/hash/shake/sha3_test.go +++ b/hash/shake/sha3_test.go @@ -22,11 +22,6 @@ import ( "testing" ) -var ( - xorIn = xorInUnaligned - copyOut = copyOutUnaligned -) - const ( testString = "brekeccakkeccak koax koax" katFilename = "testdata/keccakKats.json.deflate" @@ -68,66 +63,53 @@ type KeccakKats struct { } } -func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) { - xorInOrig, copyOutOrig := xorIn, copyOut - xorIn, copyOut = xorInGeneric, copyOutGeneric - testf("generic") - if xorImplementationUnaligned != "generic" { - xorIn, copyOut = xorInGeneric, copyOutGeneric - testf("unaligned") - } - xorIn, copyOut = xorInOrig, copyOutOrig -} - // 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) { - testUnalignedAndGeneric(t, func(impl string) { - // 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) - } + // 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, v := range testShakes { - for _, kat := range katSet.Kats[algo] { - N, err := hex.DecodeString(kat.N) - if err != nil { - t.Errorf("error decoding KAT: %s", err) - } - - S, err := hex.DecodeString(kat.S) - if err != nil { - t.Errorf("error decoding KAT: %s", err) - } - d := v.constructor(N, S) - in, err := hex.DecodeString(kat.Message) - if err != nil { - t.Errorf("error decoding KAT: %s", err) - } - - d.Write(in[:kat.Length/8]) - out := make([]byte, len(kat.Digest)/2) - d.Read(out) - got := strings.ToUpper(hex.EncodeToString(out)) - if got != kat.Digest { - t.Errorf("function=%s, implementation=%s, length=%d N:%s\n S:%s\nmessage:\n %s \ngot:\n %s\nwanted:\n %s", - algo, impl, kat.Length, kat.N, kat.S, kat.Message, got, kat.Digest) - t.Logf("wanted %+v", kat) - t.FailNow() - } - continue + for algo, v := range testShakes { + for _, kat := range katSet.Kats[algo] { + N, err := hex.DecodeString(kat.N) + if err != nil { + t.Errorf("error decoding KAT: %s", err) } + + S, err := hex.DecodeString(kat.S) + if err != nil { + t.Errorf("error decoding KAT: %s", err) + } + d := v.constructor(N, S) + in, err := hex.DecodeString(kat.Message) + if err != nil { + t.Errorf("error decoding KAT: %s", err) + } + + d.Write(in[:kat.Length/8]) + out := make([]byte, len(kat.Digest)/2) + d.Read(out) + got := strings.ToUpper(hex.EncodeToString(out)) + if got != kat.Digest { + t.Errorf("function=%s, length=%d N:%s\n S:%s\nmessage:\n %s \ngot:\n %s\nwanted:\n %s", + algo, kat.Length, kat.N, kat.S, 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. @@ -158,37 +140,35 @@ func TestKeccak(t *testing.T) { // TestUnalignedWrite tests that writing data in an arbitrary pattern with // small input buffers. func TestUnalignedWrite(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - buf := sequentialBytes(0x10000) + buf := sequentialBytes(0x10000) - // Same for SHAKE - for alg, df := range testShakes { - want := make([]byte, 16) - got := make([]byte, 16) - d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr)) + // Same for SHAKE + for alg, df := range testShakes { + want := make([]byte, 16) + got := make([]byte, 16) + d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr)) - d.Reset() - d.Write(buf) - d.Read(want) - 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 + d.Reset() + d.Write(buf) + d.Read(want) + 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.Read(got) - if !bytes.Equal(got, want) { - t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want) + d.Write(buf[i : i+j]) + i += j } } - }) + d.Read(got) + if !bytes.Equal(got, want) { + t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want) + } + } } /* TODO: To redesign those tests and unlock @@ -231,26 +211,24 @@ func TestAppendNoRealloc(t *testing.T) { // TestSqueezing checks that squeezing the full output a single time produces // the same output as repeatedly squeezing the instance. func TestSqueezing(t *testing.T) { - testUnalignedAndGeneric(t, func(impl string) { - for algo, v := range testShakes { - d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) - d0.Write([]byte(testString)) - ref := make([]byte, 32) - d0.Read(ref) + for algo, v := range testShakes { + d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) + d0.Write([]byte(testString)) + ref := make([]byte, 32) + d0.Read(ref) - d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) - d1.Write([]byte(testString)) - var multiple []byte - for range ref { - one := make([]byte, 1) - d1.Read(one) - multiple = append(multiple, one...) - } - if !bytes.Equal(ref, multiple) { - t.Errorf("%s (%s): squeezing %d bytes one at a time failed", algo, impl, len(ref)) - } + d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) + d1.Write([]byte(testString)) + var multiple []byte + for range ref { + one := make([]byte, 1) + d1.Read(one) + multiple = append(multiple, one...) } - }) + if !bytes.Equal(ref, multiple) { + t.Errorf("%s : squeezing %d bytes one at a time failed", algo, len(ref)) + } + } } // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. diff --git a/hash/shake/xor_generic.go b/hash/shake/xor_generic.go index 9292820..867cab3 100644 --- a/hash/shake/xor_generic.go +++ b/hash/shake/xor_generic.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !amd64,!386,!ppc64le + package shake import "encoding/binary" @@ -9,7 +11,7 @@ import "encoding/binary" // xorInGeneric xors the bytes in buf into the state; it // makes no non-portable assumptions about memory layout // or alignment. -func xorInGeneric(d *state, buf []byte) { +func xorIn(d *state, buf []byte) { n := len(buf) / 8 for i := 0; i < n; i++ { @@ -20,11 +22,9 @@ func xorInGeneric(d *state, buf []byte) { } // copyOutGeneric copies ulint64s to a byte buffer. -func copyOutGeneric(d *state, b []byte) { +func copyOut(d *state, b []byte) { for i := 0; len(b) >= 8; i++ { binary.LittleEndian.PutUint64(b, d.a[i]) b = b[8:] } } - -const xorImplementationGeneric = "generic" diff --git a/hash/shake/xor_unaligned.go b/hash/shake/xor_unaligned.go index 9de3d4c..d7348f0 100644 --- a/hash/shake/xor_unaligned.go +++ b/hash/shake/xor_unaligned.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build amd64 arm64 386 ppc64le +// +build amd64 386 ppc64le // +build !appengine package shake import "unsafe" -func xorInUnaligned(d *state, buf []byte) { +func xorIn(d *state, buf []byte) { bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0])) n := len(buf) if n >= 72 { @@ -45,9 +45,7 @@ func xorInUnaligned(d *state, buf []byte) { } } -func copyOutUnaligned(d *state, buf []byte) { +func copyOut(d *state, buf []byte) { ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) copy(buf, ab[:]) } - -const xorImplementationUnaligned = "unaligned"