Optimize non-overlapping CFB8 decryption using SIMD XOR (#265)

This commit is contained in:
B站贴吧蜡油
2023-11-24 06:49:31 +08:00
committed by GitHub
parent 75da769c25
commit bc3d77d784
6 changed files with 103 additions and 41 deletions

View File

@ -32,7 +32,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View File

@ -5,16 +5,23 @@ jobs:
test: test:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# match the minimum supported and the latest Go versions
go_version: [ '1.20', '^1.20' ]
steps: steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.19
id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v1 uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go_version }}
check-latest: true
- name: Test - name: Test
run: go test ./... run: go test ./...
@ -25,7 +32,8 @@ jobs:
run: go build -o ./bin/tools ./examples/... run: go build -o ./bin/tools ./examples/...
- name: Upload tools - name: Upload tools
uses: actions/upload-artifact@v1 if: ${{ startsWith(matrix.go_version, '^') }}
uses: actions/upload-artifact@v3
with: with:
name: tools name: tools
path: ./bin/tools path: ./bin/tools

View File

@ -7,7 +7,7 @@
### [教程 · Tutorial](https://go-mc.github.io/tutorial/) ### [教程 · Tutorial](https://go-mc.github.io/tutorial/)
Require Go version: 1.19 Require Go version: 1.20
There's some library in Go support you to create your Minecraft client or server. There's some library in Go support you to create your Minecraft client or server.
这是一些Golang库用于帮助你编写自己的Minecraft客户端或服务器。 这是一些Golang库用于帮助你编写自己的Minecraft客户端或服务器。

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/Tnze/go-mc module github.com/Tnze/go-mc
go 1.19 go 1.20
require ( require (
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0

View File

@ -3,6 +3,7 @@ package CFB8
import ( import (
"crypto/cipher" "crypto/cipher"
"crypto/subtle"
"unsafe" "unsafe"
) )
@ -48,7 +49,7 @@ func (cf *CFB8) XORKeyStream(dst, src []byte) {
// After this, the IV will come to the same as // After this, the IV will come to the same as
// the last blockSize of ciphertext, so // the last blockSize of ciphertext, so
// we can reuse them without copy. // we can reuse them without copy.
cf.XORKeyStream(dst, src[:cf.blockSize]) cf.xorKeyStream(dst, src[:cf.blockSize])
var ciphertext []byte var ciphertext []byte
if cf.de { if cf.de {
ciphertext = src ciphertext = src
@ -63,16 +64,37 @@ func (cf *CFB8) XORKeyStream(dst, src []byte) {
i int i int
val byte val byte
) )
dst = dst[:len(src)]
if cf.de {
for i = 0; i < len(src)-cf.blockSize; i += 1 {
cf.c.Encrypt(dst[i:], ciphertext[i:])
}
subtle.XORBytes(dst, src[:i], dst)
for ; i < len(src); i += 1 {
cf.c.Encrypt(iv, ciphertext[i:])
dst[i] = src[i] ^ iv[0]
}
} else {
_ = ciphertext[len(src)]
for i, val = range src { for i, val = range src {
cf.c.Encrypt(iv, ciphertext[i:]) cf.c.Encrypt(iv, ciphertext[i:])
dst[i] = val ^ iv[0] dst[i] = val ^ iv[0]
} }
// for-range does not increase i in the last loop,
// compared to the classic for clause
i += 1
}
// copy the current IV for next operation // copy the current IV for next operation
copy(iv, ciphertext[i+1:i+1+cf.blockSize]) copy(iv, ciphertext[i:i+cf.blockSize])
cf.ivPos = 0 cf.ivPos = 0
return return
} }
cf.xorKeyStream(dst, src)
}
func (cf *CFB8) xorKeyStream(dst, src []byte) {
dst = dst[:len(src)] // remove bounds check in loop
for i, val := range src { for i, val := range src {
posPlusBlockSize := cf.ivPos + cf.blockSize posPlusBlockSize := cf.ivPos + cf.blockSize
// fast mod; 2*blockSize must be a non-negative integer power of 2 // fast mod; 2*blockSize must be a non-negative integer power of 2

View File

@ -3,6 +3,7 @@ package CFB8
import ( import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"testing" "testing"
@ -150,14 +151,8 @@ func TestCFB8VectorsOverlapped(t *testing.T) {
} }
} }
func BenchmarkCFB8AES1KOverlapped(b *testing.B) { func benchmarkStreamOverlapped(b *testing.B, stream cipher.Stream) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
buf := make([]byte, 1024) buf := make([]byte, 1024)
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Encrypt(aes, iv[:])
b.SetBytes(int64(len(buf))) b.SetBytes(int64(len(buf)))
b.ReportAllocs() b.ReportAllocs()
@ -167,15 +162,9 @@ func BenchmarkCFB8AES1KOverlapped(b *testing.B) {
} }
} }
func BenchmarkCFB8AES1KNonOverlapping(b *testing.B) { func benchmarkStreamNonOverlapping(b *testing.B, stream cipher.Stream) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
buf := make([]byte, 1024) buf := make([]byte, 1024)
buf2 := make([]byte, 1024) buf2 := make([]byte, 1024)
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Encrypt(aes, iv[:])
b.SetBytes(int64(len(buf))) b.SetBytes(int64(len(buf)))
b.ReportAllocs() b.ReportAllocs()
@ -184,3 +173,46 @@ func BenchmarkCFB8AES1KNonOverlapping(b *testing.B) {
stream.XORKeyStream(buf2, buf) stream.XORKeyStream(buf2, buf)
} }
} }
func BenchmarkCFB8AES1KEncryptOverlapped(b *testing.B) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Encrypt(aes, iv[:])
benchmarkStreamOverlapped(b, stream)
}
func BenchmarkCFB8AES1KEncryptNonOverlapping(b *testing.B) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Encrypt(aes, iv[:])
benchmarkStreamNonOverlapping(b, stream)
}
func BenchmarkCFB8AES1KDecryptOverlapped(b *testing.B) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Decrypt(aes, iv[:])
benchmarkStreamOverlapped(b, stream)
}
func BenchmarkCFB8AES1KDecryptNonOverlapping(b *testing.B) {
var key [16]byte
var iv [16]byte
rand.Read(key[:])
rand.Read(iv[:])
aes, _ := aes.NewCipher(key[:])
stream := NewCFB8Decrypt(aes, iv[:])
benchmarkStreamNonOverlapping(b, stream)
}