perf: sync dispatch hot path optimizations

This commit is contained in:
A 2026-07-06 14:27:03 +08:00
parent 03b785ebf4
commit 7e64d9c30e
13 changed files with 824 additions and 215 deletions

View file

@ -0,0 +1,181 @@
package mtprotoedge
import (
"bytes"
"crypto/aes"
"crypto/rand"
"encoding/binary"
"testing"
"github.com/gotd/ige"
"github.com/gotd/td/bin"
"github.com/gotd/td/crypto"
)
func newTestAuthKey(t *testing.T) crypto.AuthKey {
t.Helper()
var key crypto.Key
if _, err := rand.Read(key[:]); err != nil {
t.Fatalf("rand: %v", err)
}
return key.WithID()
}
// encryptRawClientPlaintext 用 client side(x=0)把一段已含 32 字节头与 padding 的
// 原始明文加密成完整入站帧,用于构造 gotd Cipher.Encrypt 不允许生成的畸形明文。
func encryptRawClientPlaintext(t *testing.T, key crypto.AuthKey, plaintext []byte) *bin.Buffer {
t.Helper()
if len(plaintext)%16 != 0 {
t.Fatalf("plaintext must be 16-aligned, got %d", len(plaintext))
}
msgKey := crypto.MessageKey(key.Value, plaintext, crypto.Client)
aesKey, iv := crypto.Keys(key.Value, msgKey, crypto.Client)
blk, err := aes.NewCipher(aesKey[:])
if err != nil {
t.Fatalf("aes: %v", err)
}
encrypted := make([]byte, len(plaintext))
ige.EncryptBlocks(blk, iv[:], encrypted, plaintext)
var b bin.Buffer
b.Put(key.ID[:])
b.Put(msgKey[:])
b.Put(encrypted)
return &b
}
// buildRawPlaintext 构造 salt/session/msg_id/seq_no + dataLen 头与指定 data/padding 的明文。
// dataLen 允许与真实 data 长度不一致,用于打边界。
func buildRawPlaintext(salt, sessionID, msgID int64, seqNo, dataLen int32, data, padding []byte) []byte {
out := make([]byte, 0, 32+len(data)+len(padding))
var hdr [32]byte
binary.LittleEndian.PutUint64(hdr[0:8], uint64(salt))
binary.LittleEndian.PutUint64(hdr[8:16], uint64(sessionID))
binary.LittleEndian.PutUint64(hdr[16:24], uint64(msgID))
binary.LittleEndian.PutUint32(hdr[24:28], uint32(seqNo))
binary.LittleEndian.PutUint32(hdr[28:32], uint32(dataLen))
out = append(out, hdr[:]...)
out = append(out, data...)
return append(out, padding...)
}
// TestDecryptClientFrameParityWithGotd 逐字节对照 telesrv 自建解密与 gotd server cipher:
// 同一帧要么双方都接受且字段/数据一致,要么双方都拒绝。覆盖正常帧、畸形长度、
// 超限 padding、篡改 msg_key/auth_key_id、截断帧。
func TestDecryptClientFrameParityWithGotd(t *testing.T) {
key := newTestAuthKey(t)
serverCipher := crypto.NewServerCipher(rand.Reader)
pad := func(n int) []byte {
p := make([]byte, n)
if _, err := rand.Read(p); err != nil {
t.Fatalf("rand: %v", err)
}
return p
}
data16 := pad(16)
cases := []struct {
name string
frame *bin.Buffer
}{
{"valid_small", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7000, 1, 16, data16, pad(16)))},
{"valid_large", encryptRawClientPlaintext(t, key, buildRawPlaintext(9, 8, 7002, 3, 4096, pad(4096), pad(16)))},
{"zero_len_data", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7004, 5, 0, nil, pad(16)))},
{"data_len_negative", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7006, 7, -4, data16, pad(16)))},
{"data_len_unaligned", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7008, 9, 6, data16, pad(16)))},
{"data_len_overflow", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7010, 11, 64, data16, pad(16)))},
{"padding_too_big", encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7012, 13, 16, data16, pad(1040)))},
{"plaintext_only_header_block", encryptRawClientPlaintext(t, key, pad(16))},
}
// 篡改 msg_key。
tampered := encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7014, 15, 16, data16, pad(16)))
tampered.Buf[8] ^= 0xff
cases = append(cases, struct {
name string
frame *bin.Buffer
}{"tampered_msg_key", tampered})
// 错误 auth_key_id。
wrongKey := encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7016, 17, 16, data16, pad(16)))
wrongKey.Buf[0] ^= 0xff
cases = append(cases, struct {
name string
frame *bin.Buffer
}{"wrong_auth_key_id", wrongKey})
// 截断帧。
cases = append(cases,
struct {
name string
frame *bin.Buffer
}{"truncated_header", &bin.Buffer{Buf: pad(16)}},
struct {
name string
frame *bin.Buffer
}{"unaligned_ciphertext", &bin.Buffer{Buf: pad(24 + 15)}},
)
var plain bin.Buffer
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
gotdData, gotdErr := serverCipher.DecryptFromBuffer(key, &bin.Buffer{Buf: append([]byte(nil), tc.frame.Buf...)})
frame, ourErr := decryptClientFrame(key, &bin.Buffer{Buf: append([]byte(nil), tc.frame.Buf...)}, &plain)
if (gotdErr == nil) != (ourErr == nil) {
t.Fatalf("accept/reject mismatch: gotd err=%v, ours err=%v", gotdErr, ourErr)
}
if gotdErr != nil {
return
}
if frame.salt != gotdData.Salt || frame.sessionID != gotdData.SessionID ||
frame.messageID != gotdData.MessageID || frame.seqNo != gotdData.SeqNo {
t.Fatalf("header mismatch: ours=%+v gotd salt=%d session=%d msg=%d seq=%d",
frame, gotdData.Salt, gotdData.SessionID, gotdData.MessageID, gotdData.SeqNo)
}
if !bytes.Equal(frame.data, gotdData.Data()) {
t.Fatalf("data mismatch: ours %d bytes, gotd %d bytes", len(frame.data), len(gotdData.Data()))
}
})
}
}
// TestDecryptClientFrameReusesPlainBuffer 验证同一 plain 缓冲跨帧复用:先大帧后小帧,
// 解密结果仍正确且不受前一帧残留字节影响。
func TestDecryptClientFrameReusesPlainBuffer(t *testing.T) {
key := newTestAuthKey(t)
big := make([]byte, 2048)
for i := range big {
big[i] = byte(i)
}
small := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
pad := make([]byte, 16)
frame1 := encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7100, 1, int32(len(big)), big, pad))
frame2 := encryptRawClientPlaintext(t, key, buildRawPlaintext(1, 2, 7102, 3, int32(len(small)), small, pad))
var plain bin.Buffer
f1, err := decryptClientFrame(key, frame1, &plain)
if err != nil {
t.Fatalf("decrypt big frame: %v", err)
}
if !bytes.Equal(f1.data, big) {
t.Fatal("big frame data mismatch")
}
f2, err := decryptClientFrame(key, frame2, &plain)
if err != nil {
t.Fatalf("decrypt small frame: %v", err)
}
if !bytes.Equal(f2.data, small) {
t.Fatal("small frame data mismatch after buffer reuse")
}
if f2.messageID != 7102 || f2.seqNo != 3 || f2.salt != 1 || f2.sessionID != 2 {
t.Fatalf("small frame header mismatch: %+v", f2)
}
if len(f2.plaintext) != 32+len(small)+len(pad) {
t.Fatalf("plaintext length not shrunk on reuse: %d", len(f2.plaintext))
}
}