aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/internal
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2024-09-30 17:08:12 +0200
committerGopher Robot <gobot@golang.org>2024-10-23 15:21:50 +0000
commitcb69354de3d3d2a4e9ba1afa481a3174039bbe96 (patch)
treed2b183a84076f6966bf6bc9eceeda985d6c12af6 /src/crypto/internal
parentfdf6605109167e8093ee27d246c8b35678146eb6 (diff)
downloadgo-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.go21
-rw-r--r--src/crypto/internal/fips/subtle/xor_amd64.go10
-rw-r--r--src/crypto/internal/fips/subtle/xor_amd64.s58
-rw-r--r--src/crypto/internal/fips/subtle/xor_arm64.go10
-rw-r--r--src/crypto/internal/fips/subtle/xor_arm64.s69
-rw-r--r--src/crypto/internal/fips/subtle/xor_generic.go64
-rw-r--r--src/crypto/internal/fips/subtle/xor_loong64.go10
-rw-r--r--src/crypto/internal/fips/subtle/xor_loong64.s166
-rw-r--r--src/crypto/internal/fips/subtle/xor_ppc64x.go10
-rw-r--r--src/crypto/internal/fips/subtle/xor_ppc64x.s142
-rw-r--r--src/crypto/internal/fips/subtle/xor_test.go98
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()
+}