Skip to content

Conversation

@rbqvq
Copy link
Contributor

@rbqvq rbqvq commented Dec 10, 2025

  • 允许传入 cipher.AEAD 作为加密方式

CFB 加密格式

encrypt( [Nonce] [CRC32] [FEC] [KCP])

AEAD 加密格式

[Nonce] encrypt([FEC] [KCP]) [AEAD Tag]


要点如下:

Header 长度变更 (仅 Nonce), 尾部留空 (已经有了)

如果把 CRC32 看作 AEAD Tag 的话, CRC在前, AEAD 在后

MTU 计算变更 Header + (AEAD Overhead)

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

补一条:

如果传入的不符合 BlockCrypt 或 cipher.AEAD

比如 nil 或者 其他内容

那就是无加密 (

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

@xtaci 注释和文档变更就交给你了,你先看,我去 rebase 一下

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

这个我得慢慢看了

@rbqvq rbqvq force-pushed the aead branch 2 times, most recently from fc90173 to bcf7a48 Compare December 10, 2025 05:55
@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

行了,少了个 return 补上了

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

好的,这个我主要考虑必要性,因为AEAD一般是在上层封装消息的时候,对于UDP数据包的必要性,一个4B的crc32应该就够了,AEAD的tag在12-16,这个会增加overhead.

行了,少了个 return 补上了

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

确实是会增加overhead, 对于 chacha20 的话
加密开销 nonce 12 + authTag 16 = 28
比现在多8个

AES GCM 的 authTag 可以在12-16之间
也就是 32 个,多 12 字节

不考虑 XChacha20 这个还是能接受的

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

GHASH 现在也有 CPU 硬解加速什么的, 可能会比 CRC32 快一些?

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

这点速度是有GHASH的硬件看不上,没GHASH的硬件GCM更慢。

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

我先看看,暂时不合并这个,先留着。

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

这点速度是有GHASH的硬件看不上,没GHASH的硬件GCM更慢。

是这样,可以考虑 Chacha20Poly1035 什么的
用 CFB 还是 AEAD 取决于用户咯

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

https://github.com/xtaci/kcptun/releases/tag/v20251210
先测试测试kcptun,tag了。

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

晚点我加几个 Benchmark 看看吞吐

AEAD的 NonceSize 也能调
8字节的 Nonce 配 12 字节authTag 就没有多余开销了倒是, 灵活性这一块 (

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

$ go.exe test -benchmem -run=^$ -bench "^Benchmark(CFB|AEAD)" .
2025/12/10 15:10:40 beginning tests, encryption:salsa20, fec:10/3
goos: windows
goarch: amd64
pkg: github.com/xtaci/kcp-go/v5
cpu: Intel(R) Xeon(R) CPU E5-2683 v4 @ 2.10GHz
BenchmarkCFB_AES_128_CRC32-32             320488              3453 ns/op         405.39 MB/s           0 B/op          0 allocs/op
BenchmarkAEAD_AES_128_GCM-32              602001              1994 ns/op         702.28 MB/s        3072 B/op          1 allocs/op
BenchmarkCFB_Salsa20_CRC32-32             554316              2113 ns/op         662.70 MB/s           0 B/op          0 allocs/op
BenchmarkAEAD_Chacha20_Poly1035-32        488388              2397 ns/op         584.07 MB/s        3072 B/op          1 allocs/op
PASS
ok      github.com/xtaci/kcp-go/v5      4.788s

有硬解的情况下 GCM 比 CFB 快多了

不过 Chacha20Poly1035 就不比 Salsa20 快了 (包里没有 Chacha20 的 BlockCrypt)


重用 AEAD 内存以后的结果

2025/12/10 15:16:23 beginning tests, encryption:salsa20, fec:10/3
goos: windows
goarch: amd64
pkg: github.com/xtaci/kcp-go/v5
cpu: Intel(R) Xeon(R) CPU E5-2683 v4 @ 2.10GHz
BenchmarkCFB_AES_128_CRC32-32             299253              3448 ns/op         406.06 MB/s           0 B/op          0 allocs/op
BenchmarkAEAD_AES_128_GCM-32             1555551               772.4 ns/op      1812.60 MB/s           0 B/op          0 allocs/op
BenchmarkCFB_Salsa20_CRC32-32             565623              2117 ns/op         661.20 MB/s           0 B/op          0 allocs/op
BenchmarkAEAD_Chacha20_Poly1035-32        959094              1254 ns/op        1116.54 MB/s           0 B/op          0 allocs/op
PASS
ok      github.com/xtaci/kcp-go/v5      4.771s

这下吊打了

@rbqvq rbqvq force-pushed the aead branch 2 times, most recently from 88cca5e to f36e133 Compare December 10, 2025 07:42
@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

image

后面的这一堆的构造函数,需要做类型检查,只允许BlockCrypt和AEAD类型。

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

或者说,有没有办法直接扩展BlockCrypt,让AEAD 是其中一种类型。这样改动最小。

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

我本来是想封装成aead的,但是吧authTag (CRC32) 写起来真的很难受(

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

要么就是加个结构体,内部是any,调用必须通过函数构造私有结构体

但是不管是扩展还是改函数签名都会破坏兼容性 :(

可以考虑未知的cipher直接panic

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

不过可以考虑这样子

type aeadCrypt struct {
    cipher.AEAD
}

func (aeadCrypt) Encrypt(_, _ []byte) {
    panic("called Encrypt on AEAD crypt")
}

func (aeadCrypt) Decrypt(_, _ []byte) {
    panic("called Decrypt on AEAD crypt")
}

然后内部断言

if block ok := l.block.(*aeadCrypt); ok {
    block.Seal(...)
}

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

可以尝试下

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

OK

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

好了,我跑个测试去

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

看上去他工作,数据正常传输

@xtaci
Copy link
Owner

xtaci commented Dec 10, 2025

好的,非加密情况的长度,就是case nil那里是确保是0对吧,测试过没

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 10, 2025

AEAD的工作,nil 的话 sess_test.go 里面应该有?
go test 是没问题的

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

OK,你要不直接PR

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

我在外面 (

对于 Open 的话

if dst != nil && cap(dst) < len(ciphertext) - aead.Overhead {
    panic("new buffer allocated")
}

理论上 Open 不用检查, 因为 dst 重用 ciphertext 肯定是比 plaintext 大的

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

OK,我来

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

编辑了一下,Open不用检查, Seal 检查就行了

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

检查扔到seal前面,检查不通过连seal都不用
cap(dst) - len(dst) 取可用长度即可

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

其实我的想法是直接在newUDPSession里面检查 NonceSize 和 Overhead

直接在 newUDPSession 就抛 (

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

其实我的想法是直接在newUDPSession里面检查 NonceSize 和 Overhead

直接在 newUDPSession 就抛 (

这个也需要的,提前检查。但内部的assert也需要的,一个保险丝。

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

371609f

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

这个assert感觉永远碰不到倒是

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

这个assert感觉永远碰不到倒是

避免其他的对kcp的改动碰到,当然正常碰不到,但需要。

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

这样的话 hmmmm

我想应该改成这样子

# Seal

if dst == nil || cap(dst)-len(dst) < len(plaintext) + s.aead.Overhead() {
   panic("a new buffer allocated!")
}

# Open
if dst == nil || cap(dst)-len(dst) < len(ciphertext) - s.aead.Overhead() {
   panic("unreachable")
}

没有 dst 也会生成新 Buffer
Open 看你要不要加,Open永远重用 ciphertext[:0] 不会造成新的分配

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

都加上吧,这样

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

最好 UDPSession 里面调用一下 SetMTU 或者确保 headerSize + IKCP_MTU_DEF 不会超过 1500

我的倾向是调用一次 SetMTU(IKCP_MTU_DEF)

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

image 都加上吧,这样

please那个删掉吧, 不是 MTU 的问题

他应该永远到不了这个地方

panic("unreachable")

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

if !sess.SetMTU(IKCP_MTU_DEF) {
    panic("Overhead too large")
}

修正一次 MTU

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

image

确实够了, panic 消息改成 unreachable 或许更好

所以 Open 里面你想好加不加了吗 (

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

image

SetMTU 在 NewKCP 之后调用

kcp 对象没填充直接 SetMTU 肯定空指针

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

Open可以不用,因为Open没有从buffer从拿数据。

image

确实够了, panic 消息改成 unreachable 或许更好

所以 Open 里面你想好加不加了吗 (

Open可以不用,因为Open没有从buffer从拿数据。

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

image

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

应该没问题

你什么时候开一个 dev 分支或者临时分支的,
改动定稿前 rebase + force push
不至于在 master 里面反复横跳 (

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

应该没问题

你什么时候开一个 dev 分支或者临时分支的, 改动定稿前 rebase + force push 不至于在 master 里面反复横跳 (

OK,平时改动少。这次改动多了。

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

以后就用dev分支吧,我push上去了。

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

应该没啥问题了, Seal 那个应该也永远碰不到

我在想要不要把 Seal 那个检查扔到测试里面
主分支就不留这个检查了

@xtaci
Copy link
Owner

xtaci commented Dec 11, 2025

应该没啥问题了, Seal 那个应该也永远碰不到

我在想要不要把 Seal 那个检查扔到测试里面 主分支就不留这个检查了

你先想想,我先测试几天aesgcm的kcptun

@rbqvq
Copy link
Contributor Author

rbqvq commented Dec 11, 2025

应该没啥问题了, Seal 那个应该也永远碰不到
我在想要不要把 Seal 那个检查扔到测试里面 主分支就不留这个检查了

你先想想,我先测试几天aesgcm的kcptun

行,我回去加几个测试
毕竟修正 MTU 以后是不存在新 alloc 的
容量永远够

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants