diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0578f5c..0703f39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.19.x] + go-version: [1.20.x] platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1d972bc..c61b8d0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,7 +18,7 @@ jobs: - name: Setup Go uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 with: - go-version: 1.19.x + go-version: 1.20.x - name: Run fuzzers env: FUZZ_TIME: 30m diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 719a657..eab7647 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x] + go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} @@ -43,7 +43,7 @@ jobs: - name: Setup Go uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 with: - go-version: 1.19.x + go-version: 1.20.x - name: Run benchmarks run: make bench - name: Run fuzzers diff --git a/internal/const.go b/internal/const.go new file mode 100644 index 0000000..c6e87dc --- /dev/null +++ b/internal/const.go @@ -0,0 +1,38 @@ +package shared + +const ( + // Constants for the SHA-1 hash function. + K0 = 0x5A827999 + K1 = 0x6ED9EBA1 + K2 = 0x8F1BBCDC + K3 = 0xCA62C1D6 + + // Initial values for the buffer variables: h0, h1, h2, h3, h4. + Init0 = 0x67452301 + Init1 = 0xEFCDAB89 + Init2 = 0x98BADCFE + Init3 = 0x10325476 + Init4 = 0xC3D2E1F0 + + // Initial values for the temporary variables (ihvtmp0, ihvtmp1, ihvtmp2, ihvtmp3, ihvtmp4) during the SHA recompression step. + InitTmp0 = 0xD5 + InitTmp1 = 0x394 + InitTmp2 = 0x8152A8 + InitTmp3 = 0x0 + InitTmp4 = 0xA7ECE0 + + // SHA1 contains 2 buffers, each based off 5 32-bit words. + WordBuffers = 5 + + // The output of SHA1 is 20 bytes (160 bits). + Size = 20 + + // Rounds represents the number of steps required to process each chunk. + Rounds = 80 + + // SHA1 processes the input data in chunks. Each chunk contains 64 bytes. + Chunk = 64 + + Magic = "shacd\x01" + MarshaledSize = len(Magic) + 5*4 + Chunk + 8 +) diff --git a/sha1block.go b/sha1block.go index cf050ef..75c8db1 100644 --- a/sha1block.go +++ b/sha1block.go @@ -3,113 +3,110 @@ // license that can be found in the LICENSE file. // Originally from: https://github.com/go/blob/master/src/crypto/sha1/sha1block.go +// It has been modified to support collision detection. package sha1cd import ( + "fmt" "math/bits" + shared "github.com/pjbgf/sha1cd/internal" "github.com/pjbgf/sha1cd/ubc" ) -const ( - msize = 80 - - _K0 = 0x5A827999 - _K1 = 0x6ED9EBA1 - _K2 = 0x8F1BBCDC - _K3 = 0xCA62C1D6 -) - -// TODO: Implement SIMD support. -func block(dig *digest, p []byte) { - blockGeneric(dig, p) -} - // blockGeneric is a portable, pure Go version of the SHA-1 block step. // It's used by sha1block_generic.go and tests. -func blockGeneric(dig *digest, p []byte) { +func block(dig *digest, p []byte) { var w [16]uint32 - h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] - for len(p) >= chunk { - m1 := [msize]uint32{} + // cs stores the pre-step compression state for only the steps required for the + // collision detection, which are 0, 58 and 65. + // Refer to ubc/const.go for more details. + cs := [3][shared.WordBuffers]uint32{} - // Can interlace the computation of w with the - // rounds below if needed for speed. - for i := 0; i < 16; i++ { - j := i * 4 - w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) - } + h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] + for len(p) >= shared.Chunk { + m1 := [shared.Rounds]uint32{} + hi := 1 + // Collision attacks are thwarted by hashing a detected near-collision block 3 times. + // Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: + // The best collision attacks against SHA-1 have complexity about 2^60, + // thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. + // An attacker would be better off using a generic birthday search of complexity 2^80. + rehash: a, b, c, d, e := h0, h1, h2, h3, h4 // Each of the four 20-iteration rounds // differs only in the computation of f and - // the choice of K (_K0, _K1, etc). + // the choice of K (K0, K1, etc). i := 0 + + // Store pre-step compression state for the collision detection. + cs[0] = [shared.WordBuffers]uint32{a, b, c, d, e} + for ; i < 16; i++ { - // Store pre-step compression state for the collision detection. - dig.cs[i] = [5]uint32{a, b, c, d, e} + // load step + j := i * 4 + w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) f := b&c | (^b)&d - t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K0 + t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0 a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d // Store compression state for the collision detection. m1[i] = w[i&0xf] } for ; i < 20; i++ { - // Store pre-step compression state for the collision detection. - dig.cs[i] = [5]uint32{a, b, c, d, e} - tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] w[i&0xf] = tmp<<1 | tmp>>(32-1) f := b&c | (^b)&d - t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K0 + t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0 a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d // Store compression state for the collision detection. m1[i] = w[i&0xf] } for ; i < 40; i++ { - // Store pre-step compression state for the collision detection. - dig.cs[i] = [5]uint32{a, b, c, d, e} - tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] w[i&0xf] = tmp<<1 | tmp>>(32-1) f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K1 + t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K1 a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d // Store compression state for the collision detection. m1[i] = w[i&0xf] } for ; i < 60; i++ { - // Store pre-step compression state for the collision detection. - dig.cs[i] = [5]uint32{a, b, c, d, e} + if i == 58 { + // Store pre-step compression state for the collision detection. + cs[1] = [shared.WordBuffers]uint32{a, b, c, d, e} + } tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] w[i&0xf] = tmp<<1 | tmp>>(32-1) f := ((b | c) & d) | (b & c) - t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K2 + t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K2 a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d // Store compression state for the collision detection. m1[i] = w[i&0xf] } for ; i < 80; i++ { - // Store pre-step compression state for the collision detection. - dig.cs[i] = [5]uint32{a, b, c, d, e} + if i == 65 { + // Store pre-step compression state for the collision detection. + cs[2] = [shared.WordBuffers]uint32{a, b, c, d, e} + } tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] w[i&0xf] = tmp<<1 | tmp>>(32-1) f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K3 + t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K3 a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d // Store compression state for the collision detection. @@ -122,113 +119,104 @@ func blockGeneric(dig *digest, p []byte) { h3 += d h4 += e - bcol := false - if mask := ubc.CalculateDvMask(m1); mask != 0 { - dvs := ubc.SHA1_dvs() - for i := 0; dvs[i].DvType != 0; i++ { - if (mask & ((uint32)(1) << uint32(dvs[i].MaskB))) != 0 { - for j := 0; j < msize; j++ { - dig.m2[j] = m1[j] ^ dvs[i].Dm[j] - } - - recompressionStep(dvs[i].TestT, &dig.ihv2, &dig.ihvtmp, dig.m2, dig.cs[dvs[i].TestT]) - - if 0 == ((dig.ihvtmp[0] ^ h0) | (dig.ihvtmp[1] ^ h1) | - (dig.ihvtmp[2] ^ h2) | (dig.ihvtmp[3] ^ h3) | (dig.ihvtmp[4] ^ h4)) { - dig.col = true - bcol = true - } - } - } + if hi == 2 { + hi++ + goto rehash } - // Collision attacks are thwarted by hashing a detected near-collision block 3 times. - // Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: - // The best collision attacks against SHA-1 have complexity about 2^60, - // thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. - // An attacker would be better off using a generic birthday search of complexity 2^80. - if bcol { - for j := 0; j < 2; j++ { - a, b, c, d, e := h0, h1, h2, h3, h4 - - i := 0 - for ; i < 20; i++ { - f := b&c | (^b)&d - t := bits.RotateLeft32(a, 5) + f + e + m1[i] + _K0 - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d - } - for ; i < 40; i++ { - f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + m1[i] + _K1 - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d - } - for ; i < 60; i++ { - f := ((b | c) & d) | (b & c) - t := bits.RotateLeft32(a, 5) + f + e + m1[i] + _K2 - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d - } - for ; i < 80; i++ { - f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + m1[i] + _K3 - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d + if hi == 1 { + if mask := ubc.CalculateDvMask(m1); mask != 0 { + dvs := ubc.SHA1_dvs() + + for i := 0; dvs[i].DvType != 0; i++ { + if (mask & ((uint32)(1) << uint32(dvs[i].MaskB))) != 0 { + var state [5]uint32 + switch dvs[i].TestT { + case 58: + state = cs[1] + case 65: + state = cs[2] + case 0: + state = cs[0] + default: + panic(fmt.Sprintf("dvs data is trying to use a testT that isn't available: %d", dvs[i].TestT)) + } + + col := hasCollided( + dvs[i].TestT, // testT is the step number + // m2 is a secondary message created XORing with + // ubc's DM prior to the SHA recompression step. + m1, dvs[i].Dm, + state, + [5]uint32{h0, h1, h2, h3, h4}) + + if col { + dig.col = true + + hi++ + goto rehash + } + } } - - h0 += a - h1 += b - h2 += c - h3 += d - h4 += e } } - p = p[chunk:] + p = p[shared.Chunk:] } dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4 } -func recompressionStep(step int, ihvin, ihvout *[5]uint32, m2 [msize]uint32, state [5]uint32) { +func hasCollided(step uint32, m1, dm [shared.Rounds]uint32, + state [shared.WordBuffers]uint32, h [shared.WordBuffers]uint32) bool { + // Intermediary Hash Value. + ihv := [shared.WordBuffers]uint32{} + a, b, c, d, e := state[0], state[1], state[2], state[3], state[4] // Walk backwards from current step to undo previous compression. - for i := 79; i >= 60; i-- { + // The existing collision detection does not have dvs higher than 65, + // start value of i accordingly. + for i := uint32(64); i >= 60; i-- { a, b, c, d, e = b, c, d, e, a if step > i { b = bits.RotateLeft32(b, -30) f := b ^ c ^ d - e -= bits.RotateLeft32(a, 5) + f + _K3 + m2[i] + e -= bits.RotateLeft32(a, 5) + f + shared.K3 + (m1[i] ^ dm[i]) } } - for i := 59; i >= 40; i-- { + for i := uint32(59); i >= 40; i-- { a, b, c, d, e = b, c, d, e, a if step > i { b = bits.RotateLeft32(b, -30) f := ((b | c) & d) | (b & c) - e -= bits.RotateLeft32(a, 5) + f + _K2 + m2[i] + e -= bits.RotateLeft32(a, 5) + f + shared.K2 + (m1[i] ^ dm[i]) } } - for i := 39; i >= 20; i-- { + for i := uint32(39); i >= 20; i-- { a, b, c, d, e = b, c, d, e, a if step > i { b = bits.RotateLeft32(b, -30) f := b ^ c ^ d - e -= bits.RotateLeft32(a, 5) + f + _K1 + m2[i] + e -= bits.RotateLeft32(a, 5) + f + shared.K1 + (m1[i] ^ dm[i]) } } - for i := 19; i >= 0; i-- { + for i := uint32(20); i > 0; i-- { + j := i - 1 a, b, c, d, e = b, c, d, e, a - if step > i { - b = bits.RotateLeft32(b, -30) + if step > j { + b = bits.RotateLeft32(b, -30) // undo the rotate left f := b&c | (^b)&d - e -= bits.RotateLeft32(a, 5) + f + _K0 + m2[i] + // subtract from e + e -= bits.RotateLeft32(a, 5) + f + shared.K0 + (m1[j] ^ dm[j]) } } - ihvin[0] = a - ihvin[1] = b - ihvin[2] = c - ihvin[3] = d - ihvin[4] = e + ihv[0] = a + ihv[1] = b + ihv[2] = c + ihv[3] = d + ihv[4] = e a = state[0] b = state[1] c = state[2] @@ -236,38 +224,34 @@ func recompressionStep(step int, ihvin, ihvout *[5]uint32, m2 [msize]uint32, sta e = state[4] // Recompress blocks based on the current step. - for i := 0; i < 20; i++ { - if step <= i { - f := b&c | (^b)&d - t := bits.RotateLeft32(a, 5) + f + e + _K0 + m2[i] - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d - } - } - for i := 20; i < 40; i++ { - if step <= i { - f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + _K1 + m2[i] - a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d - } - } - for i := 40; i < 60; i++ { + // The existing collision detection does not have dvs below 58, so they have been removed + // from the source code. If new dvs are added which target rounds below 40, that logic + // will need to be readded here. + for i := uint32(40); i < 60; i++ { if step <= i { f := ((b | c) & d) | (b & c) - t := bits.RotateLeft32(a, 5) + f + e + _K2 + m2[i] + t := bits.RotateLeft32(a, 5) + f + e + shared.K2 + (m1[i] ^ dm[i]) a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d } } - for i := 60; i < 80; i++ { + for i := uint32(60); i < 80; i++ { if step <= i { f := b ^ c ^ d - t := bits.RotateLeft32(a, 5) + f + e + _K3 + m2[i] + t := bits.RotateLeft32(a, 5) + f + e + shared.K3 + (m1[i] ^ dm[i]) a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d } } - ihvout[0] = ihvin[0] + a - ihvout[1] = ihvin[1] + b - ihvout[2] = ihvin[2] + c - ihvout[3] = ihvin[3] + d - ihvout[4] = ihvin[4] + e + ihv[0] += a + ihv[1] += b + ihv[2] += c + ihv[3] += d + ihv[4] += e + + if ((ihv[0] ^ h[0]) | (ihv[1] ^ h[1]) | + (ihv[2] ^ h[2]) | (ihv[3] ^ h[3]) | (ihv[4] ^ h[4])) == 0 { + return true + } + + return false } diff --git a/sha1cd.go b/sha1cd.go index 48933ad..d6f983b 100644 --- a/sha1cd.go +++ b/sha1cd.go @@ -16,6 +16,8 @@ import ( "encoding/binary" "errors" "hash" + + shared "github.com/pjbgf/sha1cd/internal" ) func init() { @@ -23,47 +25,25 @@ func init() { } // The size of a SHA-1 checksum in bytes. -const Size = 20 +const Size = shared.Size // The blocksize of SHA-1 in bytes. -const BlockSize = 64 - -const ( - chunk = 64 - init0 = 0x67452301 - init1 = 0xEFCDAB89 - init2 = 0x98BADCFE - init3 = 0x10325476 - init4 = 0xC3D2E1F0 -) +const BlockSize = shared.Chunk // digest represents the partial evaluation of a checksum. type digest struct { - h [5]uint32 - x [chunk]byte + h [shared.WordBuffers]uint32 + x [shared.Chunk]byte nx int len uint64 // col defines whether a collision has been found. col bool - // cs stores the compression state for each of the SHA1's 80 steps. - cs [80][5]uint32 - // m2 is a secondary message created XORing with ubc's DM prior to the SHA recompression step. - m2 [msize]uint32 - // ihv2 is an Intermediary Hash Value created during the SHA recompression step. - ihv2 [5]uint32 - // ihvtmp is an Intermediary Hash Value created during the SHA recompression step. - ihvtmp [5]uint32 } -const ( - magic = "shacd\x01" - marshaledSize = len(magic) + 5*4 + chunk + 8 -) - func (d *digest) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, marshaledSize) - b = append(b, magic...) + b := make([]byte, 0, shared.MarshaledSize) + b = append(b, shared.Magic...) b = appendUint32(b, d.h[0]) b = appendUint32(b, d.h[1]) b = appendUint32(b, d.h[2]) @@ -98,13 +78,13 @@ func appendUint64(b []byte, v uint64) []byte { } func (d *digest) UnmarshalBinary(b []byte) error { - if len(b) < len(magic) || string(b[:len(magic)]) != magic { + if len(b) < len(shared.Magic) || string(b[:len(shared.Magic)]) != shared.Magic { return errors.New("crypto/sha1: invalid hash state identifier") } - if len(b) != marshaledSize { + if len(b) != shared.MarshaledSize { return errors.New("crypto/sha1: invalid hash state size") } - b = b[len(magic):] + b = b[len(shared.Magic):] b, d.h[0] = consumeUint32(b) b, d.h[1] = consumeUint32(b) b, d.h[2] = consumeUint32(b) @@ -112,13 +92,13 @@ func (d *digest) UnmarshalBinary(b []byte) error { b, d.h[4] = consumeUint32(b) b = b[copy(d.x[:], b):] b, d.len = consumeUint64(b) - d.nx = int(d.len % chunk) + d.nx = int(d.len % shared.Chunk) return nil } func consumeUint64(b []byte) ([]byte, uint64) { _ = b[7] - x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[shared.WordBuffers])<<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 } @@ -130,36 +110,15 @@ func consumeUint32(b []byte) ([]byte, uint32) { } func (d *digest) Reset() { - d.h[0] = init0 - d.h[1] = init1 - d.h[2] = init2 - d.h[3] = init3 - d.h[4] = init4 + d.h[0] = shared.Init0 + d.h[1] = shared.Init1 + d.h[2] = shared.Init2 + d.h[3] = shared.Init3 + d.h[4] = shared.Init4 d.nx = 0 d.len = 0 d.col = false - d.ihv2[0] = 0x0 - d.ihv2[1] = 0x0 - d.ihv2[2] = 0x0 - d.ihv2[3] = 0x0 - d.ihv2[4] = 0x0 - - d.ihvtmp[0] = 0xD5 - d.ihvtmp[1] = 0x394 - d.ihvtmp[2] = 0x8152A8 - d.ihvtmp[3] = 0x0 - d.ihvtmp[4] = 0xA7ECE0 - - for i := range d.m2 { - d.m2[i] = 0x0 - } - - for i := range d.cs { - for j := range d.cs[i] { - d.cs[i][j] = 0x0 - } - } } // New returns a new hash.Hash computing the SHA1 checksum. The Hash also @@ -186,14 +145,14 @@ func (d *digest) Write(p []byte) (nn int, err error) { if d.nx > 0 { n := copy(d.x[d.nx:], p) d.nx += n - if d.nx == chunk { + if d.nx == shared.Chunk { block(d, d.x[:]) d.nx = 0 } p = p[n:] } - if len(p) >= chunk { - n := len(p) &^ (chunk - 1) + if len(p) >= shared.Chunk { + n := len(p) &^ (shared.Chunk - 1) block(d, p[:n]) p = p[n:] } diff --git a/test/bench_test.go b/test/bench_test.go index c069fd8..6d93f4b 100644 --- a/test/bench_test.go +++ b/test/bench_test.go @@ -3,16 +3,16 @@ package test import ( "crypto/sha1" "hash" + "os" "testing" "github.com/pjbgf/sha1cd" "github.com/pjbgf/sha1cd/cgo" - "github.com/pjbgf/sha1cd/testdata" "github.com/pjbgf/sha1cd/ubc" ) func BenchmarkCalculateDvMask(b *testing.B) { - data := testdata.Shattered1M1s[0] + data := shattered1M1s[0] b.Run("go", func(b *testing.B) { b.ReportAllocs() @@ -41,6 +41,18 @@ func benchmarkSize(b *testing.B, n string, d hash.Hash, size int) { }) } +func benchmarkContent(b *testing.B, n string, d hash.Hash, data []byte) { + b.Run(n, func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(int64(len(data))) + for i := 0; i < b.N; i++ { + d.Reset() + d.Write(data) + d.Sum(data[:0]) + } + }) +} + func BenchmarkHash8Bytes(b *testing.B) { benchmarkSize(b, "sha1", sha1.New(), 8) benchmarkSize(b, "sha1cd", sha1cd.New(), 8) @@ -64,3 +76,12 @@ func BenchmarkHash8K(b *testing.B) { benchmarkSize(b, "sha1cd", sha1cd.New(), 8192) benchmarkSize(b, "sha1cd_cgo", cgo.New(), 8192) } + +func BenchmarkHashWithCollision(b *testing.B) { + shambles, err := os.ReadFile("testdata/files/sha-mbles-1.bin") + if err != nil { + b.Fatal(err) + } + benchmarkContent(b, "sha1cd", sha1cd.New(), shambles) + benchmarkContent(b, "sha1cd_cgo", cgo.New(), shambles) +} diff --git a/test/collisiondetection_test.go b/test/collisiondetection_test.go index 2f808a0..7a2921a 100644 --- a/test/collisiondetection_test.go +++ b/test/collisiondetection_test.go @@ -8,7 +8,6 @@ import ( "github.com/pjbgf/sha1cd" "github.com/pjbgf/sha1cd/cgo" - "github.com/pjbgf/sha1cd/testdata" "github.com/pjbgf/sha1cd/ubc" ) @@ -27,35 +26,35 @@ func TestCollisionDetection(t *testing.T) { }{ { name: "shattered-1 ", - inputFile: "../testdata/files/shattered-1.pdf", + inputFile: "testdata/files/shattered-1.pdf", wantCollision: true, wantHash: "16e96b70000dd1e7c85b8368ee197754400e58ec", hashers: defaultHashers, }, { name: "shattered-2", - inputFile: "../testdata/files/shattered-2.pdf", + inputFile: "testdata/files/shattered-2.pdf", wantCollision: true, wantHash: "e1761773e6a35916d99f891b77663e6405313587", hashers: defaultHashers, }, { name: "sha-mbles-1", - inputFile: "../testdata/files/sha-mbles-1.bin", + inputFile: "testdata/files/sha-mbles-1.bin", wantCollision: true, wantHash: "4f3d9be4a472c4dae83c6314aa6c36a064c1fd14", hashers: defaultHashers, }, { name: "sha-mbles-2", - inputFile: "../testdata/files/sha-mbles-2.bin", + inputFile: "testdata/files/sha-mbles-2.bin", wantCollision: true, wantHash: "9ed5d77a4f48be1dbf3e9e15650733eb850897f2", hashers: defaultHashers, }, { name: "Valid File", - inputFile: "../testdata/files/valid-file.txt", + inputFile: "testdata/files/valid-file.txt", wantHash: "2b915da50f163514d390c9d87a4f3e23eb663f8a", hashers: defaultHashers, }, @@ -85,10 +84,10 @@ func TestCollisionDetection(t *testing.T) { } func TestCalculateDvMask_Shattered1(t *testing.T) { - for i := range testdata.Shattered1M1s { + for i := range shattered1M1s { t.Run(fmt.Sprintf("m1[%d]", i), func(t *testing.T) { - got := ubc.CalculateDvMask(testdata.Shattered1M1s[i]) - want := cgo.CalculateDvMask(testdata.Shattered1M1s[i]) + got := ubc.CalculateDvMask(shattered1M1s[i]) + want := cgo.CalculateDvMask(shattered1M1s[i]) if want != got { t.Fatalf("dvmask: %d\nwant %d", got, want) diff --git a/testdata/shattered1_m1s.go b/test/shattered1_m1s.go similarity index 99% rename from testdata/shattered1_m1s.go rename to test/shattered1_m1s.go index ff79617..c0418c8 100644 --- a/testdata/shattered1_m1s.go +++ b/test/shattered1_m1s.go @@ -1,8 +1,8 @@ -package testdata +package test -// Shattered1M1s contains all m1 messages generated by the original sha1.c code +// shattered1M1s contains all m1 messages generated by the original sha1.c code // when calling ubc_check.c. Used to attest correctness of the Go implementation. -var Shattered1M1s = [][80]uint32{ +var shattered1M1s = [][80]uint32{ { 0x25504446, 0x2D312E33, 0xA25E2E3, 0xCFD30A0A, 0xA312030, 0x206F626A, 0xA3C3C2F, 0x57696474, 0x68203220, 0x3020522F, 0x48656967, 0x68742033, 0x20302052, 0x2F547970, 0x65203420, 0x3020522F, 0xD003DBEA, 0x6FC4846D, 0xF0A3F336, 0xAF972772, diff --git a/testdata/files/sha-mbles-1.bin b/test/testdata/files/sha-mbles-1.bin similarity index 100% rename from testdata/files/sha-mbles-1.bin rename to test/testdata/files/sha-mbles-1.bin diff --git a/testdata/files/sha-mbles-2.bin b/test/testdata/files/sha-mbles-2.bin similarity index 100% rename from testdata/files/sha-mbles-2.bin rename to test/testdata/files/sha-mbles-2.bin diff --git a/testdata/files/shattered-1.pdf b/test/testdata/files/shattered-1.pdf similarity index 100% rename from testdata/files/shattered-1.pdf rename to test/testdata/files/shattered-1.pdf diff --git a/testdata/files/shattered-2.pdf b/test/testdata/files/shattered-2.pdf similarity index 100% rename from testdata/files/shattered-2.pdf rename to test/testdata/files/shattered-2.pdf diff --git a/testdata/files/valid-file.txt b/test/testdata/files/valid-file.txt similarity index 100% rename from testdata/files/valid-file.txt rename to test/testdata/files/valid-file.txt diff --git a/ubc/check.go b/ubc/check.go index 4552918..167a555 100644 --- a/ubc/check.go +++ b/ubc/check.go @@ -6,22 +6,22 @@ package ubc type DvInfo struct { // DvType, DvK and DvB define the DV: I(K,B) or II(K,B) (see the paper). // https://marc-stevens.nl/research/papers/C13-S.pdf - DvType int - DvK int - DvB int + DvType uint32 + DvK uint32 + DvB uint32 // TestT is the step to do the recompression from for collision detection. - TestT int + TestT uint32 // MaskI and MaskB define the bit to check for each DV in the dvmask returned by ubc_check. - MaskI int - MaskB int + MaskI uint32 + MaskB uint32 // Dm is the expanded message block XOR-difference defined by the DV. Dm [80]uint32 } -// Check takes as input an expanded message block and verifies the unavoidable bitconditions +// CalculateDvMask takes as input an expanded message block and verifies the unavoidable bitconditions // for all listed DVs. It returns a dvmask where each bit belonging to a DV is set if all // unavoidable bitconditions for that DV have been met. // Thus, one needs to do the recompression check for each DV that has its bit set.