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:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@ -1,31 +1,39 @@
name: Go
on: [push, pull_request]
on: [ push, pull_request ]
jobs:
test:
name: Test
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:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.19
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go_version }}
check-latest: true
- name: Test
run: go test ./...
- name: Test
run: go test ./...
- run: mkdir -p ./bin/tools
- run: mkdir -p ./bin/tools
- name: Build tools
run: go build -o ./bin/tools ./examples/...
- name: Upload tools
uses: actions/upload-artifact@v1
with:
name: tools
path: ./bin/tools
- name: Build tools
run: go build -o ./bin/tools ./examples/...
- name: Upload tools
if: ${{ startsWith(matrix.go_version, '^') }}
uses: actions/upload-artifact@v3
with:
name: tools
path: ./bin/tools

View File

@ -7,7 +7,7 @@
### [教程 · 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.
这是一些Golang库用于帮助你编写自己的Minecraft客户端或服务器。

2
go.mod
View File

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

View File

@ -3,6 +3,7 @@ package CFB8
import (
"crypto/cipher"
"crypto/subtle"
"unsafe"
)
@ -48,7 +49,7 @@ func (cf *CFB8) XORKeyStream(dst, src []byte) {
// After this, the IV will come to the same as
// the last blockSize of ciphertext, so
// we can reuse them without copy.
cf.XORKeyStream(dst, src[:cf.blockSize])
cf.xorKeyStream(dst, src[:cf.blockSize])
var ciphertext []byte
if cf.de {
ciphertext = src
@ -63,16 +64,37 @@ func (cf *CFB8) XORKeyStream(dst, src []byte) {
i int
val byte
)
for i, val = range src {
cf.c.Encrypt(iv, ciphertext[i:])
dst[i] = val ^ iv[0]
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 {
cf.c.Encrypt(iv, ciphertext[i:])
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(iv, ciphertext[i+1:i+1+cf.blockSize])
copy(iv, ciphertext[i:i+cf.blockSize])
cf.ivPos = 0
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 {
posPlusBlockSize := cf.ivPos + cf.blockSize
// fast mod; 2*blockSize must be a non-negative integer power of 2

View File

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