diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 31aed21..5bb6b28 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -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 diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 1a2fe57..a33b60b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -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 diff --git a/README.md b/README.md index 7717ab3..de29dd1 100644 --- a/README.md +++ b/README.md @@ -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客户端或服务器。 diff --git a/go.mod b/go.mod index a9f43b0..386714f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Tnze/go-mc -go 1.19 +go 1.20 require ( github.com/google/uuid v1.3.0 diff --git a/net/CFB8/cfb8.go b/net/CFB8/cfb8.go index 5714a2b..26e0cbf 100644 --- a/net/CFB8/cfb8.go +++ b/net/CFB8/cfb8.go @@ -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 diff --git a/net/CFB8/cfb8_test.go b/net/CFB8/cfb8_test.go index c0b59e4..9d70f9c 100644 --- a/net/CFB8/cfb8_test.go +++ b/net/CFB8/cfb8_test.go @@ -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) +}