diff options
| author | Filippo Valsorda <filippo@golang.org> | 2024-09-30 17:08:12 +0200 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-10-23 15:21:50 +0000 |
| commit | cb69354de3d3d2a4e9ba1afa481a3174039bbe96 (patch) | |
| tree | d2b183a84076f6966bf6bc9eceeda985d6c12af6 /src/crypto/internal | |
| parent | fdf6605109167e8093ee27d246c8b35678146eb6 (diff) | |
| download | go-cb69354de3d3d2a4e9ba1afa481a3174039bbe96.tar.xz | |
crypto/internal/fips/subtle: provide XORBytes
This is needed from inside the module, and we generally don't want to
import the crypto tree from it.
For #69536
Change-Id: I69e91e4df89ecac0016c671ccd28e733a7131533
Reviewed-on: https://go-review.googlesource.com/c/go/+/616716
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Diffstat (limited to 'src/crypto/internal')
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor.go | 21 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_amd64.go | 10 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_amd64.s | 58 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_arm64.go | 10 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_arm64.s | 69 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_generic.go | 64 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_loong64.go | 10 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_loong64.s | 166 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_ppc64x.go | 10 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_ppc64x.s | 142 | ||||
| -rw-r--r-- | src/crypto/internal/fips/subtle/xor_test.go | 98 |
11 files changed, 658 insertions, 0 deletions
diff --git a/src/crypto/internal/fips/subtle/xor.go b/src/crypto/internal/fips/subtle/xor.go new file mode 100644 index 0000000000..158dbcede9 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor.go @@ -0,0 +1,21 @@ +// Copyright 2022 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 subtle + +// XORBytes sets dst[i] = x[i] ^ y[i] for all i < n = min(len(x), len(y)), +// returning n, the number of bytes written to dst. +// If dst does not have length at least n, +// XORBytes panics without writing anything to dst. +func XORBytes(dst, x, y []byte) int { + n := min(len(x), len(y)) + if n == 0 { + return 0 + } + if n > len(dst) { + panic("subtle.XORBytes: dst too short") + } + xorBytes(&dst[0], &x[0], &y[0], n) // arch-specific + return n +} diff --git a/src/crypto/internal/fips/subtle/xor_amd64.go b/src/crypto/internal/fips/subtle/xor_amd64.go new file mode 100644 index 0000000000..3bb2f08b7c --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_amd64.go @@ -0,0 +1,10 @@ +// Copyright 2018 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. + +//go:build !purego + +package subtle + +//go:noescape +func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips/subtle/xor_amd64.s b/src/crypto/internal/fips/subtle/xor_amd64.s new file mode 100644 index 0000000000..949424f87a --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_amd64.s @@ -0,0 +1,58 @@ +// Copyright 2018 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. + +//go:build !purego + +#include "textflag.h" + +// func xorBytes(dst, a, b *byte, n int) +TEXT ·xorBytes(SB), NOSPLIT, $0 + MOVQ dst+0(FP), BX + MOVQ a+8(FP), SI + MOVQ b+16(FP), CX + MOVQ n+24(FP), DX + TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned. + JNZ not_aligned + +aligned: + MOVQ $0, AX // position in slices + + PCALIGN $16 +loop16b: + MOVOU (SI)(AX*1), X0 // XOR 16byte forwards. + MOVOU (CX)(AX*1), X1 + PXOR X1, X0 + MOVOU X0, (BX)(AX*1) + ADDQ $16, AX + CMPQ DX, AX + JNE loop16b + RET + + PCALIGN $16 +loop_1b: + SUBQ $1, DX // XOR 1byte backwards. + MOVB (SI)(DX*1), DI + MOVB (CX)(DX*1), AX + XORB AX, DI + MOVB DI, (BX)(DX*1) + TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b. + JNZ loop_1b + CMPQ DX, $0 // if len is 0, ret. + JE ret + TESTQ $15, DX // AND 15 & len, if zero jump to aligned. + JZ aligned + +not_aligned: + TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b. + JNE loop_1b + SUBQ $8, DX // XOR 8bytes backwards. + MOVQ (SI)(DX*1), DI + MOVQ (CX)(DX*1), AX + XORQ AX, DI + MOVQ DI, (BX)(DX*1) + CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned. + JGE aligned + +ret: + RET diff --git a/src/crypto/internal/fips/subtle/xor_arm64.go b/src/crypto/internal/fips/subtle/xor_arm64.go new file mode 100644 index 0000000000..65bab4c657 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_arm64.go @@ -0,0 +1,10 @@ +// Copyright 2020 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. + +//go:build !purego + +package subtle + +//go:noescape +func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips/subtle/xor_arm64.s b/src/crypto/internal/fips/subtle/xor_arm64.s new file mode 100644 index 0000000000..76321645d7 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_arm64.s @@ -0,0 +1,69 @@ +// Copyright 2020 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. + +//go:build !purego + +#include "textflag.h" + +// func xorBytes(dst, a, b *byte, n int) +TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0 + MOVD dst+0(FP), R0 + MOVD a+8(FP), R1 + MOVD b+16(FP), R2 + MOVD n+24(FP), R3 + CMP $64, R3 + BLT tail +loop_64: + VLD1.P 64(R1), [V0.B16, V1.B16, V2.B16, V3.B16] + VLD1.P 64(R2), [V4.B16, V5.B16, V6.B16, V7.B16] + VEOR V0.B16, V4.B16, V4.B16 + VEOR V1.B16, V5.B16, V5.B16 + VEOR V2.B16, V6.B16, V6.B16 + VEOR V3.B16, V7.B16, V7.B16 + VST1.P [V4.B16, V5.B16, V6.B16, V7.B16], 64(R0) + SUBS $64, R3 + CMP $64, R3 + BGE loop_64 +tail: + // quick end + CBZ R3, end + TBZ $5, R3, less_than32 + VLD1.P 32(R1), [V0.B16, V1.B16] + VLD1.P 32(R2), [V2.B16, V3.B16] + VEOR V0.B16, V2.B16, V2.B16 + VEOR V1.B16, V3.B16, V3.B16 + VST1.P [V2.B16, V3.B16], 32(R0) +less_than32: + TBZ $4, R3, less_than16 + LDP.P 16(R1), (R11, R12) + LDP.P 16(R2), (R13, R14) + EOR R11, R13, R13 + EOR R12, R14, R14 + STP.P (R13, R14), 16(R0) +less_than16: + TBZ $3, R3, less_than8 + MOVD.P 8(R1), R11 + MOVD.P 8(R2), R12 + EOR R11, R12, R12 + MOVD.P R12, 8(R0) +less_than8: + TBZ $2, R3, less_than4 + MOVWU.P 4(R1), R13 + MOVWU.P 4(R2), R14 + EORW R13, R14, R14 + MOVWU.P R14, 4(R0) +less_than4: + TBZ $1, R3, less_than2 + MOVHU.P 2(R1), R15 + MOVHU.P 2(R2), R16 + EORW R15, R16, R16 + MOVHU.P R16, 2(R0) +less_than2: + TBZ $0, R3, end + MOVBU (R1), R17 + MOVBU (R2), R19 + EORW R17, R19, R19 + MOVBU R19, (R0) +end: + RET diff --git a/src/crypto/internal/fips/subtle/xor_generic.go b/src/crypto/internal/fips/subtle/xor_generic.go new file mode 100644 index 0000000000..e575c35696 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_generic.go @@ -0,0 +1,64 @@ +// Copyright 2013 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. + +//go:build (!amd64 && !arm64 && !loong64 && !ppc64 && !ppc64le) || purego + +package subtle + +import ( + "runtime" + "unsafe" +) + +const wordSize = unsafe.Sizeof(uintptr(0)) + +const supportsUnaligned = runtime.GOARCH == "386" || + runtime.GOARCH == "amd64" || + runtime.GOARCH == "ppc64" || + runtime.GOARCH == "ppc64le" || + runtime.GOARCH == "s390x" + +func xorBytes(dstb, xb, yb *byte, n int) { + // xorBytes assembly is written using pointers and n. Back to slices. + dst := unsafe.Slice(dstb, n) + x := unsafe.Slice(xb, n) + y := unsafe.Slice(yb, n) + + if supportsUnaligned || aligned(dstb, xb, yb) { + xorLoop(words(dst), words(x), words(y)) + if uintptr(n)%wordSize == 0 { + return + } + done := n &^ int(wordSize-1) + dst = dst[done:] + x = x[done:] + y = y[done:] + } + xorLoop(dst, x, y) +} + +// aligned reports whether dst, x, and y are all word-aligned pointers. +func aligned(dst, x, y *byte) bool { + return (uintptr(unsafe.Pointer(dst))|uintptr(unsafe.Pointer(x))|uintptr(unsafe.Pointer(y)))&(wordSize-1) == 0 +} + +// words returns a []uintptr pointing at the same data as x, +// with any trailing partial word removed. +func words(x []byte) []uintptr { + n := uintptr(len(x)) / wordSize + if n == 0 { + // Avoid creating a *uintptr that refers to data smaller than a uintptr; + // see issue 59334. + return nil + } + return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), n) +} + +func xorLoop[T byte | uintptr](dst, x, y []T) { + x = x[:len(dst)] // remove bounds check in loop + y = y[:len(dst)] // remove bounds check in loop + for i := range dst { + dst[i] = x[i] ^ y[i] + } +} diff --git a/src/crypto/internal/fips/subtle/xor_loong64.go b/src/crypto/internal/fips/subtle/xor_loong64.go new file mode 100644 index 0000000000..e49f0fc9e3 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_loong64.go @@ -0,0 +1,10 @@ +// Copyright 2024 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. + +//go:build !purego + +package subtle + +//go:noescape +func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips/subtle/xor_loong64.s b/src/crypto/internal/fips/subtle/xor_loong64.s new file mode 100644 index 0000000000..09dc80eb93 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_loong64.s @@ -0,0 +1,166 @@ +// Copyright 2024 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. + +//go:build !purego + +#include "textflag.h" + +// func xorBytes(dst, a, b *byte, n int) +TEXT ·xorBytes(SB), NOSPLIT, $0 + MOVV dst+0(FP), R4 + MOVV a+8(FP), R5 + MOVV b+16(FP), R6 + MOVV n+24(FP), R7 + + MOVV $64, R9 + BGEU R7, R9, loop64 // n >= 64 +tail: + SRLV $1, R9 + BGEU R7, R9, xor_32 // n >= 32 && n < 64 + SRLV $1, R9 + BGEU R7, R9, xor_16 // n >= 16 && n < 32 + SRLV $1, R9 + BGEU R7, R9, xor_8 // n >= 8 && n < 16 + SRLV $1, R9 + BGEU R7, R9, xor_4 // n >= 4 && n < 8 + SRLV $1, R9 + BGEU R7, R9, xor_2 // n >= 2 && n < 4 + SRLV $1, R9 + BGEU R7, R9, xor_1 // n = 1 + +loop64: + MOVV (R5), R10 + MOVV 8(R5), R11 + MOVV 16(R5), R12 + MOVV 24(R5), R13 + MOVV (R6), R14 + MOVV 8(R6), R15 + MOVV 16(R6), R16 + MOVV 24(R6), R17 + XOR R10, R14 + XOR R11, R15 + XOR R12, R16 + XOR R13, R17 + MOVV R14, (R4) + MOVV R15, 8(R4) + MOVV R16, 16(R4) + MOVV R17, 24(R4) + MOVV 32(R5), R10 + MOVV 40(R5), R11 + MOVV 48(R5), R12 + MOVV 56(R5), R13 + MOVV 32(R6), R14 + MOVV 40(R6), R15 + MOVV 48(R6), R16 + MOVV 56(R6), R17 + XOR R10, R14 + XOR R11, R15 + XOR R12, R16 + XOR R13, R17 + MOVV R14, 32(R4) + MOVV R15, 40(R4) + MOVV R16, 48(R4) + MOVV R17, 56(R4) + ADDV $64, R5 + ADDV $64, R6 + ADDV $64, R4 + SUBV $64, R7 + // 64 in R9 + BGEU R7, R9, loop64 + BEQ R7, R0, end + +xor_32_check: + SRLV $1, R9 + BLT R7, R9, xor_16_check +xor_32: + MOVV (R5), R10 + MOVV 8(R5), R11 + MOVV 16(R5), R12 + MOVV 24(R5), R13 + MOVV (R6), R14 + MOVV 8(R6), R15 + MOVV 16(R6), R16 + MOVV 24(R6), R17 + XOR R10, R14 + XOR R11, R15 + XOR R12, R16 + XOR R13, R17 + MOVV R14, (R4) + MOVV R15, 8(R4) + MOVV R16, 16(R4) + MOVV R17, 24(R4) + ADDV $32, R5 + ADDV $32, R6 + ADDV $32, R4 + SUBV $32, R7 + BEQ R7, R0, end + +xor_16_check: + SRLV $1, R9 + BLT R7, R9, xor_8_check +xor_16: + MOVV (R5), R10 + MOVV 8(R5), R11 + MOVV (R6), R12 + MOVV 8(R6), R13 + XOR R10, R12 + XOR R11, R13 + MOVV R12, (R4) + MOVV R13, 8(R4) + ADDV $16, R5 + ADDV $16, R6 + ADDV $16, R4 + SUBV $16, R7 + BEQ R7, R0, end + +xor_8_check: + SRLV $1, R9 + BLT R7, R9, xor_4_check +xor_8: + MOVV (R5), R10 + MOVV (R6), R11 + XOR R10, R11 + MOVV R11, (R4) + ADDV $8, R5 + ADDV $8, R6 + ADDV $8, R4 + SUBV $8, R7 + BEQ R7, R0, end + +xor_4_check: + SRLV $1, R9 + BLT R7, R9, xor_2_check +xor_4: + MOVW (R5), R10 + MOVW (R6), R11 + XOR R10, R11 + MOVW R11, (R4) + ADDV $4, R5 + ADDV $4, R6 + ADDV $4, R4 + SUBV $4, R7 + BEQ R7, R0, end + +xor_2_check: + SRLV $1, R9 + BLT R7, R9, xor_1 +xor_2: + MOVH (R5), R10 + MOVH (R6), R11 + XOR R10, R11 + MOVH R11, (R4) + ADDV $2, R5 + ADDV $2, R6 + ADDV $2, R4 + SUBV $2, R7 + BEQ R7, R0, end + +xor_1: + MOVB (R5), R10 + MOVB (R6), R11 + XOR R10, R11 + MOVB R11, (R4) + +end: + RET diff --git a/src/crypto/internal/fips/subtle/xor_ppc64x.go b/src/crypto/internal/fips/subtle/xor_ppc64x.go new file mode 100644 index 0000000000..760463c7e5 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_ppc64x.go @@ -0,0 +1,10 @@ +// Copyright 2018 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. + +//go:build (ppc64 || ppc64le) && !purego + +package subtle + +//go:noescape +func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips/subtle/xor_ppc64x.s b/src/crypto/internal/fips/subtle/xor_ppc64x.s new file mode 100644 index 0000000000..c1f72c5ced --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_ppc64x.s @@ -0,0 +1,142 @@ +// Copyright 2018 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. + +//go:build (ppc64 || ppc64le) && !purego + +#include "textflag.h" + +// func xorBytes(dst, a, b *byte, n int) +TEXT ·xorBytes(SB), NOSPLIT, $0 + MOVD dst+0(FP), R3 // R3 = dst + MOVD a+8(FP), R4 // R4 = a + MOVD b+16(FP), R5 // R5 = b + MOVD n+24(FP), R6 // R6 = n + + CMPU R6, $64, CR7 // Check if n ≥ 64 bytes + MOVD R0, R8 // R8 = index + CMPU R6, $8, CR6 // Check if 8 ≤ n < 64 bytes + BLE CR6, small // <= 8 + BLT CR7, xor32 // Case for 32 ≤ n < 64 bytes + + // Case for n ≥ 64 bytes +preloop64: + SRD $6, R6, R7 // Set up loop counter + MOVD R7, CTR + MOVD $16, R10 + MOVD $32, R14 + MOVD $48, R15 + ANDCC $63, R6, R9 // Check for tailing bytes for later + PCALIGN $16 + // Case for >= 64 bytes + // Process 64 bytes per iteration + // Load 4 vectors of a and b + // XOR the corresponding vectors + // from a and b and store the result +loop64: + LXVD2X (R4)(R8), VS32 + LXVD2X (R4)(R10), VS34 + LXVD2X (R4)(R14), VS36 + LXVD2X (R4)(R15), VS38 + LXVD2X (R5)(R8), VS33 + LXVD2X (R5)(R10), VS35 + LXVD2X (R5)(R14), VS37 + LXVD2X (R5)(R15), VS39 + XXLXOR VS32, VS33, VS32 + XXLXOR VS34, VS35, VS34 + XXLXOR VS36, VS37, VS36 + XXLXOR VS38, VS39, VS38 + STXVD2X VS32, (R3)(R8) + STXVD2X VS34, (R3)(R10) + STXVD2X VS36, (R3)(R14) + STXVD2X VS38, (R3)(R15) + ADD $64, R8 + ADD $64, R10 + ADD $64, R14 + ADD $64, R15 + BDNZ loop64 + BC 12,2,LR // BEQLR + MOVD R9, R6 + CMP R6, $8 + BLE small + // Case for 8 <= n < 64 bytes + // Process 32 bytes if available +xor32: + CMP R6, $32 + BLT xor16 + ADD $16, R8, R9 + LXVD2X (R4)(R8), VS32 + LXVD2X (R4)(R9), VS33 + LXVD2X (R5)(R8), VS34 + LXVD2X (R5)(R9), VS35 + XXLXOR VS32, VS34, VS32 + XXLXOR VS33, VS35, VS33 + STXVD2X VS32, (R3)(R8) + STXVD2X VS33, (R3)(R9) + ADD $32, R8 + ADD $-32, R6 + CMP R6, $8 + BLE small + // Case for 8 <= n < 32 bytes + // Process 16 bytes if available +xor16: + CMP R6, $16 + BLT xor8 + LXVD2X (R4)(R8), VS32 + LXVD2X (R5)(R8), VS33 + XXLXOR VS32, VS33, VS32 + STXVD2X VS32, (R3)(R8) + ADD $16, R8 + ADD $-16, R6 +small: + CMP R6, $0 + BC 12,2,LR // BEQLR +xor8: +#ifdef GOPPC64_power10 + SLD $56,R6,R17 + ADD R4,R8,R18 + ADD R5,R8,R19 + ADD R3,R8,R20 + LXVL R18,R17,V0 + LXVL R19,R17,V1 + VXOR V0,V1,V1 + STXVL V1,R20,R17 + RET +#else + CMP R6, $8 + BLT xor4 + // Case for 8 ≤ n < 16 bytes + MOVD (R4)(R8), R14 // R14 = a[i,...,i+7] + MOVD (R5)(R8), R15 // R15 = b[i,...,i+7] + XOR R14, R15, R16 // R16 = a[] ^ b[] + SUB $8, R6 // n = n - 8 + MOVD R16, (R3)(R8) // Store to dst + ADD $8, R8 +xor4: + CMP R6, $4 + BLT xor2 + MOVWZ (R4)(R8), R14 + MOVWZ (R5)(R8), R15 + XOR R14, R15, R16 + MOVW R16, (R3)(R8) + ADD $4,R8 + ADD $-4,R6 +xor2: + CMP R6, $2 + BLT xor1 + MOVHZ (R4)(R8), R14 + MOVHZ (R5)(R8), R15 + XOR R14, R15, R16 + MOVH R16, (R3)(R8) + ADD $2,R8 + ADD $-2,R6 +xor1: + CMP R6, $0 + BC 12,2,LR // BEQLR + MOVBZ (R4)(R8), R14 // R14 = a[i] + MOVBZ (R5)(R8), R15 // R15 = b[i] + XOR R14, R15, R16 // R16 = a[i] ^ b[i] + MOVB R16, (R3)(R8) // Store to dst +#endif +done: + RET diff --git a/src/crypto/internal/fips/subtle/xor_test.go b/src/crypto/internal/fips/subtle/xor_test.go new file mode 100644 index 0000000000..3f5ef980a3 --- /dev/null +++ b/src/crypto/internal/fips/subtle/xor_test.go @@ -0,0 +1,98 @@ +// Copyright 2013 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 subtle_test + +import ( + "bytes" + . "crypto/internal/fips/subtle" + "crypto/rand" + "fmt" + "io" + "testing" +) + +func TestXORBytes(t *testing.T) { + for n := 1; n <= 1024; n++ { + if n > 16 && testing.Short() { + n += n >> 3 + } + for alignP := 0; alignP < 8; alignP++ { + for alignQ := 0; alignQ < 8; alignQ++ { + for alignD := 0; alignD < 8; alignD++ { + p := make([]byte, alignP+n, alignP+n+10)[alignP:] + q := make([]byte, alignQ+n, alignQ+n+10)[alignQ:] + if n&1 != 0 { + p = p[:n] + } else { + q = q[:n] + } + if _, err := io.ReadFull(rand.Reader, p); err != nil { + t.Fatal(err) + } + if _, err := io.ReadFull(rand.Reader, q); err != nil { + t.Fatal(err) + } + + d := make([]byte, alignD+n, alignD+n+10) + for i := range d { + d[i] = 0xdd + } + want := make([]byte, len(d), cap(d)) + copy(want[:cap(want)], d[:cap(d)]) + for i := 0; i < n; i++ { + want[alignD+i] = p[i] ^ q[i] + } + + if XORBytes(d[alignD:], p, q); !bytes.Equal(d, want) { + t.Fatalf("n=%d alignP=%d alignQ=%d alignD=%d:\n\tp = %x\n\tq = %x\n\td = %x\n\twant %x\n", n, alignP, alignQ, alignD, p, q, d, want) + } + } + } + } + } +} + +func TestXorBytesPanic(t *testing.T) { + mustPanic(t, "subtle.XORBytes: dst too short", func() { + XORBytes(nil, make([]byte, 1), make([]byte, 1)) + }) + mustPanic(t, "subtle.XORBytes: dst too short", func() { + XORBytes(make([]byte, 1), make([]byte, 2), make([]byte, 3)) + }) +} + +func BenchmarkXORBytes(b *testing.B) { + dst := make([]byte, 1<<15) + data0 := make([]byte, 1<<15) + data1 := make([]byte, 1<<15) + sizes := []int64{1 << 3, 1 << 7, 1 << 11, 1 << 15} + for _, size := range sizes { + b.Run(fmt.Sprintf("%dBytes", size), func(b *testing.B) { + s0 := data0[:size] + s1 := data1[:size] + b.SetBytes(int64(size)) + for i := 0; i < b.N; i++ { + XORBytes(dst, s0, s1) + } + }) + } +} + +func mustPanic(t *testing.T, expected string, f func()) { + t.Helper() + defer func() { + switch msg := recover().(type) { + case nil: + t.Errorf("expected panic(%q), but did not panic", expected) + case string: + if msg != expected { + t.Errorf("expected panic(%q), but got panic(%q)", expected, msg) + } + default: + t.Errorf("expected panic(%q), but got panic(%T%v)", expected, msg, msg) + } + }() + f() +} |
