Skip to content

[go1.20] Override cypto/subtle XORBytes #1284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions compiler/natives/src/crypto/subtle/xor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//go:build js
// +build js

package subtle

import "github.com/gopherjs/gopherjs/js"

const wordSize = 4 // bytes for a Uint32Array

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")
}

// The original uses unsafe and uintptr for specific architecture
// to pack registers full instead of doing one byte at a time.
// We can't do the unsafe conversions from []byte to []uintptr
// but we can convert a Uint8Array into a Uint32Array,
// so we'll simply do it four bytes at a time plus any remainder.
// The following is similar to xorBytes from xor_generic.go

dst = dst[:n]
x = x[:n]
y = y[:n]
if wordCount := n / wordSize; wordCount > 0 &&
aligned(dst) && aligned(x) && aligned(y) {
dstWords := words(dst)
xWords := words(x)
yWords := words(y)
for i := range dstWords {
dstWords[i] = xWords[i] ^ yWords[i]
}
done := n &^ int(wordSize-1)
dst = dst[done:]
x = x[done:]
y = y[done:]
}
for i := range dst {
dst[i] = x[i] ^ y[i]
}
return n
}

// aligned determines whether the slice is word-aligned since
// Uint32Array's require the offset to be multiples of 4.
func aligned(b []byte) bool {
slice := js.InternalObject(b)
offset := slice.Get(`$offset`).Int()
return offset%wordSize == 0
}

// words returns a []uint pointing at the same data as b,
// with any trailing partial word removed.
// The given b must have a word aligned offset.
func words(b []byte) []uint {
slice := js.InternalObject(b)
offset := slice.Get(`$offset`).Int()
length := slice.Get(`$length`).Int()
byteBuffer := slice.Get(`$array`).Get(`buffer`)
wordBuffer := js.Global.Get(`Uint32Array`).New(byteBuffer, offset, length/wordSize)
return wordBuffer.Interface().([]uint)
}

//gopherjs:purge
const supportsUnaligned = false

//gopherjs:purge
func xorBytes(dstb, xb, yb *byte, n int)

//gopherjs:purge
func xorLoop[T byte | uintptr](dst, x, y []T) {}