mirror of
https://github.com/golang/go
synced 2024-11-26 08:27:56 -07:00
crypto/subtle: add XORBytes
Export cipher.xorBytes as subtle.XORBytes, for proposal #53021, to provide fast XOR to cryptography libraries outside crypto/cipher. Along with the move, implement the alignment check TODO in xor_generic.go, so that systems with neither unaligned accesses nor custom assembly can still XOR a word at a time in word-based algorithms like GCM. This removes the need for the separate cipher.xorWords. Fixes #53021. Change-Id: I58f80a922f1cff671b5ebc6168eb046e702b5a4c Reviewed-on: https://go-review.googlesource.com/c/go/+/421435 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
parent
90466e1ddf
commit
57d05512fe
1
api/next/53021.txt
Normal file
1
api/next/53021.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pkg crypto/subtle, func XORBytes([]uint8, []uint8, []uint8) int #53021
|
@ -11,7 +11,10 @@
|
|||||||
|
|
||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import "crypto/internal/alias"
|
import (
|
||||||
|
"crypto/internal/alias"
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
type cbc struct {
|
type cbc struct {
|
||||||
b Block
|
b Block
|
||||||
@ -80,7 +83,7 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
|
|||||||
|
|
||||||
for len(src) > 0 {
|
for len(src) > 0 {
|
||||||
// Write the xor to dst, then encrypt in place.
|
// Write the xor to dst, then encrypt in place.
|
||||||
xorBytes(dst[:x.blockSize], src[:x.blockSize], iv)
|
subtle.XORBytes(dst[:x.blockSize], src[:x.blockSize], iv)
|
||||||
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
|
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
|
||||||
|
|
||||||
// Move to the next block with this block as the next iv.
|
// Move to the next block with this block as the next iv.
|
||||||
@ -162,7 +165,7 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
|
|||||||
// Loop over all but the first block.
|
// Loop over all but the first block.
|
||||||
for start > 0 {
|
for start > 0 {
|
||||||
x.b.Decrypt(dst[start:end], src[start:end])
|
x.b.Decrypt(dst[start:end], src[start:end])
|
||||||
xorBytes(dst[start:end], dst[start:end], src[prev:start])
|
subtle.XORBytes(dst[start:end], dst[start:end], src[prev:start])
|
||||||
|
|
||||||
end = start
|
end = start
|
||||||
start = prev
|
start = prev
|
||||||
@ -171,7 +174,7 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
|
|||||||
|
|
||||||
// The first block is special because it uses the saved iv.
|
// The first block is special because it uses the saved iv.
|
||||||
x.b.Decrypt(dst[start:end], src[start:end])
|
x.b.Decrypt(dst[start:end], src[start:end])
|
||||||
xorBytes(dst[start:end], dst[start:end], x.iv)
|
subtle.XORBytes(dst[start:end], dst[start:end], x.iv)
|
||||||
|
|
||||||
// Set the new iv to the first block we copied earlier.
|
// Set the new iv to the first block we copied earlier.
|
||||||
x.iv, x.tmp = x.tmp, x.iv
|
x.iv, x.tmp = x.tmp, x.iv
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import "crypto/internal/alias"
|
import (
|
||||||
|
"crypto/internal/alias"
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
type cfb struct {
|
type cfb struct {
|
||||||
b Block
|
b Block
|
||||||
@ -37,7 +40,7 @@ func (x *cfb) XORKeyStream(dst, src []byte) {
|
|||||||
// able to match CTR/OFB performance.
|
// able to match CTR/OFB performance.
|
||||||
copy(x.next[x.outUsed:], src)
|
copy(x.next[x.outUsed:], src)
|
||||||
}
|
}
|
||||||
n := xorBytes(dst, src, x.out[x.outUsed:])
|
n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
|
||||||
if !x.decrypt {
|
if !x.decrypt {
|
||||||
copy(x.next[x.outUsed:], dst)
|
copy(x.next[x.outUsed:], dst)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,10 @@
|
|||||||
|
|
||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import "crypto/internal/alias"
|
import (
|
||||||
|
"crypto/internal/alias"
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
type ctr struct {
|
type ctr struct {
|
||||||
b Block
|
b Block
|
||||||
@ -83,7 +86,7 @@ func (x *ctr) XORKeyStream(dst, src []byte) {
|
|||||||
if x.outUsed >= len(x.out)-x.b.BlockSize() {
|
if x.outUsed >= len(x.out)-x.b.BlockSize() {
|
||||||
x.refill()
|
x.refill()
|
||||||
}
|
}
|
||||||
n := xorBytes(dst, src, x.out[x.outUsed:])
|
n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
|
||||||
dst = dst[n:]
|
dst = dst[n:]
|
||||||
src = src[n:]
|
src = src[n:]
|
||||||
x.outUsed += n
|
x.outUsed += n
|
||||||
|
@ -5,6 +5,5 @@
|
|||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
// Export internal functions for testing.
|
// Export internal functions for testing.
|
||||||
var XorBytes = xorBytes
|
|
||||||
var NewCBCGenericEncrypter = newCBCGenericEncrypter
|
var NewCBCGenericEncrypter = newCBCGenericEncrypter
|
||||||
var NewCBCGenericDecrypter = newCBCGenericDecrypter
|
var NewCBCGenericDecrypter = newCBCGenericDecrypter
|
||||||
|
@ -373,7 +373,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
|
|||||||
g.cipher.Encrypt(mask[:], counter[:])
|
g.cipher.Encrypt(mask[:], counter[:])
|
||||||
gcmInc32(counter)
|
gcmInc32(counter)
|
||||||
|
|
||||||
xorWords(out, in, mask[:])
|
subtle.XORBytes(out, in, mask[:])
|
||||||
out = out[gcmBlockSize:]
|
out = out[gcmBlockSize:]
|
||||||
in = in[gcmBlockSize:]
|
in = in[gcmBlockSize:]
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
|
|||||||
if len(in) > 0 {
|
if len(in) > 0 {
|
||||||
g.cipher.Encrypt(mask[:], counter[:])
|
g.cipher.Encrypt(mask[:], counter[:])
|
||||||
gcmInc32(counter)
|
gcmInc32(counter)
|
||||||
xorBytes(out, in, mask[:])
|
subtle.XORBytes(out, in, mask[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,5 +423,5 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]
|
|||||||
binary.BigEndian.PutUint64(out, y.low)
|
binary.BigEndian.PutUint64(out, y.low)
|
||||||
binary.BigEndian.PutUint64(out[8:], y.high)
|
binary.BigEndian.PutUint64(out[8:], y.high)
|
||||||
|
|
||||||
xorWords(out, out, tagMask[:])
|
subtle.XORBytes(out, out, tagMask[:])
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
package cipher
|
package cipher
|
||||||
|
|
||||||
import "crypto/internal/alias"
|
import (
|
||||||
|
"crypto/internal/alias"
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
type ofb struct {
|
type ofb struct {
|
||||||
b Block
|
b Block
|
||||||
@ -66,7 +69,7 @@ func (x *ofb) XORKeyStream(dst, src []byte) {
|
|||||||
if x.outUsed >= len(x.out)-x.b.BlockSize() {
|
if x.outUsed >= len(x.out)-x.b.BlockSize() {
|
||||||
x.refill()
|
x.refill()
|
||||||
}
|
}
|
||||||
n := xorBytes(dst, src, x.out[x.outUsed:])
|
n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
|
||||||
dst = dst[n:]
|
dst = dst[n:]
|
||||||
src = src[n:]
|
src = src[n:]
|
||||||
x.outUsed += n
|
x.outUsed += n
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
package cipher
|
|
||||||
|
|
||||||
// xorBytes xors the bytes in a and b. The destination should have enough
|
|
||||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
|
||||||
func xorBytes(dst, a, b []byte) int {
|
|
||||||
n := len(a)
|
|
||||||
if len(b) < n {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
_ = dst[n-1]
|
|
||||||
xorBytesSSE2(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func xorWords(dst, a, b []byte) {
|
|
||||||
xorBytes(dst, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func xorBytesSSE2(dst, a, b *byte, n int)
|
|
@ -1,29 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
package cipher
|
|
||||||
|
|
||||||
// xorBytes xors the bytes in a and b. The destination should have enough
|
|
||||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
|
||||||
func xorBytes(dst, a, b []byte) int {
|
|
||||||
n := len(a)
|
|
||||||
if len(b) < n {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
// make sure dst has enough space
|
|
||||||
_ = dst[n-1]
|
|
||||||
|
|
||||||
xorBytesARM64(&dst[0], &a[0], &b[0], n)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func xorWords(dst, a, b []byte) {
|
|
||||||
xorBytes(dst, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func xorBytesARM64(dst, a, b *byte, n int)
|
|
@ -1,91 +0,0 @@
|
|||||||
// 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 && !ppc64 && !ppc64le && !arm64
|
|
||||||
|
|
||||||
package cipher
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// xorBytes xors the bytes in a and b. The destination should have enough
|
|
||||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
|
||||||
func xorBytes(dst, a, b []byte) int {
|
|
||||||
n := len(a)
|
|
||||||
if len(b) < n {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case supportsUnaligned:
|
|
||||||
fastXORBytes(dst, a, b, n)
|
|
||||||
default:
|
|
||||||
// TODO(hanwen): if (dst, a, b) have common alignment
|
|
||||||
// we could still try fastXORBytes. It is not clear
|
|
||||||
// how often this happens, and it's only worth it if
|
|
||||||
// the block encryption itself is hardware
|
|
||||||
// accelerated.
|
|
||||||
safeXORBytes(dst, a, b, n)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
|
||||||
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
|
|
||||||
|
|
||||||
// fastXORBytes xors in bulk. It only works on architectures that
|
|
||||||
// support unaligned read/writes.
|
|
||||||
// n needs to be smaller or equal than the length of a and b.
|
|
||||||
func fastXORBytes(dst, a, b []byte, n int) {
|
|
||||||
// Assert dst has enough space
|
|
||||||
_ = dst[n-1]
|
|
||||||
|
|
||||||
w := n / wordSize
|
|
||||||
if w > 0 {
|
|
||||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
|
||||||
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
|
||||||
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
|
||||||
for i := 0; i < w; i++ {
|
|
||||||
dw[i] = aw[i] ^ bw[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := (n - n%wordSize); i < n; i++ {
|
|
||||||
dst[i] = a[i] ^ b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// n needs to be smaller or equal than the length of a and b.
|
|
||||||
func safeXORBytes(dst, a, b []byte, n int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
dst[i] = a[i] ^ b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
|
|
||||||
// The arguments are assumed to be of equal length.
|
|
||||||
func fastXORWords(dst, a, b []byte) {
|
|
||||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
|
||||||
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
|
||||||
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
|
||||||
n := len(b) / wordSize
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
dw[i] = aw[i] ^ bw[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
|
|
||||||
// The slice arguments a and b are assumed to be of equal length.
|
|
||||||
func xorWords(dst, a, b []byte) {
|
|
||||||
if supportsUnaligned {
|
|
||||||
fastXORWords(dst, a, b)
|
|
||||||
} else {
|
|
||||||
safeXORBytes(dst, a, b, len(b))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
package cipher
|
|
||||||
|
|
||||||
// xorBytes xors the bytes in a and b. The destination should have enough
|
|
||||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
|
||||||
func xorBytes(dst, a, b []byte) int {
|
|
||||||
n := len(a)
|
|
||||||
if len(b) < n {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
_ = dst[n-1]
|
|
||||||
xorBytesVSX(&dst[0], &a[0], &b[0], n)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func xorWords(dst, a, b []byte) {
|
|
||||||
xorBytes(dst, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func xorBytesVSX(dst, a, b *byte, n int)
|
|
@ -1,75 +0,0 @@
|
|||||||
// 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 cipher_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestXOR(t *testing.T) {
|
|
||||||
for j := 1; j <= 1024; j++ {
|
|
||||||
if testing.Short() && j > 16 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for alignP := 0; alignP < 2; alignP++ {
|
|
||||||
for alignQ := 0; alignQ < 2; alignQ++ {
|
|
||||||
for alignD := 0; alignD < 2; alignD++ {
|
|
||||||
p := make([]byte, j)[alignP:]
|
|
||||||
q := make([]byte, j)[alignQ:]
|
|
||||||
d1 := make([]byte, j+alignD)[alignD:]
|
|
||||||
d2 := make([]byte, j+alignD)[alignD:]
|
|
||||||
if _, err := io.ReadFull(rand.Reader, p); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(rand.Reader, q); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cipher.XorBytes(d1, p, q)
|
|
||||||
n := min(p, q)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
d2[i] = p[i] ^ q[i]
|
|
||||||
}
|
|
||||||
if !bytes.Equal(d1, d2) {
|
|
||||||
t.Logf("p: %#v", p)
|
|
||||||
t.Logf("q: %#v", q)
|
|
||||||
t.Logf("expect: %#v", d2)
|
|
||||||
t.Logf("result: %#v", d1)
|
|
||||||
t.Fatal("not equal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(a, b []byte) int {
|
|
||||||
n := len(a)
|
|
||||||
if len(b) < n {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
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++ {
|
|
||||||
cipher.XorBytes(dst, s0, s1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
24
src/crypto/subtle/xor.go
Normal file
24
src/crypto/subtle/xor.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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 := len(x)
|
||||||
|
if len(y) < n {
|
||||||
|
n = 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
|
||||||
|
}
|
10
src/crypto/subtle/xor_amd64.go
Normal file
10
src/crypto/subtle/xor_amd64.go
Normal file
@ -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)
|
@ -2,10 +2,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// func xorBytesSSE2(dst, a, b *byte, n int)
|
// func xorBytes(dst, a, b *byte, n int)
|
||||||
TEXT ·xorBytesSSE2(SB), NOSPLIT, $0
|
TEXT ·xorBytes(SB), NOSPLIT, $0
|
||||||
MOVQ dst+0(FP), BX
|
MOVQ dst+0(FP), BX
|
||||||
MOVQ a+8(FP), SI
|
MOVQ a+8(FP), SI
|
||||||
MOVQ b+16(FP), CX
|
MOVQ b+16(FP), CX
|
10
src/crypto/subtle/xor_arm64.go
Normal file
10
src/crypto/subtle/xor_arm64.go
Normal file
@ -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)
|
@ -2,10 +2,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// func xorBytesARM64(dst, a, b *byte, n int)
|
// func xorBytes(dst, a, b *byte, n int)
|
||||||
TEXT ·xorBytesARM64(SB), NOSPLIT|NOFRAME, $0
|
TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0
|
||||||
MOVD dst+0(FP), R0
|
MOVD dst+0(FP), R0
|
||||||
MOVD a+8(FP), R1
|
MOVD a+8(FP), R1
|
||||||
MOVD b+16(FP), R2
|
MOVD b+16(FP), R2
|
58
src/crypto/subtle/xor_generic.go
Normal file
58
src/crypto/subtle/xor_generic.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// 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 && !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 {
|
||||||
|
return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), uintptr(len(x))/wordSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
10
src/crypto/subtle/xor_ppc64x.go
Normal file
10
src/crypto/subtle/xor_ppc64x.go
Normal file
@ -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)
|
@ -2,12 +2,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build ppc64 || ppc64le
|
//go:build (ppc64 || ppc64le) && !purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// func xorBytesVSX(dst, a, b *byte, n int)
|
// func xorBytes(dst, a, b *byte, n int)
|
||||||
TEXT ·xorBytesVSX(SB), NOSPLIT, $0
|
TEXT ·xorBytes(SB), NOSPLIT, $0
|
||||||
MOVD dst+0(FP), R3 // R3 = dst
|
MOVD dst+0(FP), R3 // R3 = dst
|
||||||
MOVD a+8(FP), R4 // R4 = a
|
MOVD a+8(FP), R4 // R4 = a
|
||||||
MOVD b+16(FP), R5 // R5 = b
|
MOVD b+16(FP), R5 // R5 = b
|
106
src/crypto/subtle/xor_test.go
Normal file
106
src/crypto/subtle/xor_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// 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/rand"
|
||||||
|
. "crypto/subtle"
|
||||||
|
"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 min(a, b []byte) int {
|
||||||
|
n := len(a)
|
||||||
|
if len(b) < n {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user