From 6096f891a77bfe490cbea7a424c821b5fdb92849 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Wed, 22 Feb 2023 12:48:20 +0100 Subject: [PATCH] Add MarshalBinary/UnmarshalBinary support (#68) --- .github/workflows/go.yml | 2 +- go.mod | 4 +- sha256.go | 80 ++++++++++++++++++ sha256_test.go | 169 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 20a639a..c366823 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -15,7 +15,7 @@ jobs: strategy: max-parallel: 4 matrix: - go-version: [1.16.x, 1.15.x, 1.14.x] + go-version: [1.20.x, 1.19.x, 1.18.x] os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Set up Go ${{ matrix.go-version }} diff --git a/go.mod b/go.mod index cb03f65..68dace0 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/minio/sha256-simd -go 1.13 +go 1.17 require github.com/klauspost/cpuid/v2 v2.2.3 + +require golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect diff --git a/sha256.go b/sha256.go index 00dd797..f146bbd 100644 --- a/sha256.go +++ b/sha256.go @@ -19,6 +19,7 @@ package sha256 import ( "crypto/sha256" "encoding/binary" + "errors" "hash" ) @@ -386,3 +387,82 @@ var _K = []uint32{ 0xbef9a3f7, 0xc67178f2, } + +const ( + magic256 = "sha\x03" + marshaledSize = len(magic256) + 8*4 + chunk + 8 +) + +func (d *digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic256...) + b = appendUint32(b, d.h[0]) + b = appendUint32(b, d.h[1]) + b = appendUint32(b, d.h[2]) + b = appendUint32(b, d.h[3]) + b = appendUint32(b, d.h[4]) + b = appendUint32(b, d.h[5]) + b = appendUint32(b, d.h[6]) + b = appendUint32(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-d.nx] // already zero + b = appendUint64(b, d.len) + return b, nil +} + +func (d *digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic256) || string(b[:len(magic256)]) != magic256 { + return errors.New("crypto/sha256: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("crypto/sha256: invalid hash state size") + } + b = b[len(magic256):] + b, d.h[0] = consumeUint32(b) + b, d.h[1] = consumeUint32(b) + b, d.h[2] = consumeUint32(b) + b, d.h[3] = consumeUint32(b) + b, d.h[4] = consumeUint32(b) + b, d.h[5] = consumeUint32(b) + b, d.h[6] = consumeUint32(b) + b, d.h[7] = consumeUint32(b) + b = b[copy(d.x[:], b):] + b, d.len = consumeUint64(b) + d.nx = int(d.len % chunk) + return nil +} + +func appendUint32(b []byte, v uint32) []byte { + return append(b, + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func appendUint64(b []byte, v uint64) []byte { + return append(b, + byte(v>>56), + byte(v>>48), + byte(v>>40), + byte(v>>32), + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + _ = b[7] + x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 + return b[8:], x +} + +func consumeUint32(b []byte) ([]byte, uint32) { + _ = b[3] + x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 + return b[4:], x +} diff --git a/sha256_test.go b/sha256_test.go index 1a2fef8..729c1ae 100644 --- a/sha256_test.go +++ b/sha256_test.go @@ -50,8 +50,12 @@ package sha256 import ( + "bytes" + "encoding" "encoding/hex" "fmt" + "hash" + "io" "strings" "testing" ) @@ -2315,3 +2319,168 @@ func BenchmarkHash(b *testing.B) { }() } } + +type sha256TestGo struct { + out string + in string + halfState string // marshaled hash state after first half of in written, used by TestGoldenMarshal +} + +var golden256 = []sha256TestGo{ + {"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "", "sha\x03j\t\xe6g\xbbg\xae\x85 0 { + t.Errorf("allocs = %d, want 0", n) + } +}