Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ECH: Support TLS Encrypted Client Hello #3813

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

ECH: Support TLS Encrypted Client Hello #3813

wants to merge 11 commits into from

Conversation

Fangliding
Copy link
Member

@Fangliding Fangliding commented Sep 15, 2024

仅客户端
才发现go1.23官方已经支持了 之前写过的那堆复杂替换也不需要了 就正常写入就行了 ws和splithttp预计都能使用cloudflare的ECH
目前是写死的 稍后可以考虑从DNS中获取 已经测试可以和singbox与cloudflare建立ECH连接
写法

{
    "protocol": "trojan",
    "settings": {
        "servers": [
            {
                "address": "xxx",
                "port": 8080,
                "password": "password"
            }
        ]
    },
    "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
            "serverName": "xxx",
            "echConfig": "AFj+DQBUAAAgACAJSBrX4ZNnpgYFsaF+sUabAbsO+y2Bs61D6nmHEw7dRgAkAAEAAQABAAIAAQADAAIAAQACAAIAAgADAAMAAQADAAIAAwADAAV4LmNvbQAA",
            "echDohServer": "https://1.1.1.1/dns-query",
            "alpn": [
                "h2",
                "http/1.1"
            ]
        }
    },
    "tag": "proxy"
}

@Fangliding
Copy link
Member Author

有一个最大的问题是utls是go121的似乎 现在还不支持 遥遥落后了

@RPRX
Copy link
Member

RPRX commented Sep 15, 2024

有一个最大的问题是utls是go121的似乎 现在还不支持 遥遥落后了

Win7 编译也需要 go121,所以等 2025 再加这个吧,至少得 utls 支持,而且感觉无法通过本地 dns 拿到 echConfig 的话有点鸡肋

最大的问题还是 GFW 又会严控 DNS 了

@Fangliding
Copy link
Member Author

有一个最大的问题是utls是go121的似乎 现在还不支持 遥遥落后了

Win7 编译也需要 go121,所以等 2025 再加这个吧,至少得 utls 支持,而且感觉无法通过本地 dns 拿到 echConfig 的话有点鸡肋

这点我考虑过了 稍后可以加build tag绕过低版本go 就像隔壁一样

至于utls 等它支持了 核心改两行代码就能跟上了 目前这个实验性功能应该没有什么坏处

最大的问题还是 GFW 又会严控 DNS 了

在配置里加一个可选的doh服务器用于获取解析? 或者尝试写死 观察一段时间 不知道CF这个ECH config是不是轮动的 如果不是的话写死也能接受

@RPRX
Copy link
Member

RPRX commented Sep 15, 2024

先写一下通过 dns 获取 echConfig,然后等 utls 支持了再合这个 pr 吧,其实仅用 utls 的话可能 Win7 也能用?

@Fangliding
Copy link
Member Author

先写一下通过 dns 获取 echConfig,然后等 utls 支持了再合这个 pr 吧,其实仅用 utls 的话可能 Win7 也能用?

有一个小问题是内置DNS服务器只能处理A和AAAA 这个要再加就得外置
utls更新不知道猴年马月了 他们撮了个李鬼ech 这估计还得修改一部分才能兼容 而且开发好像没那么活跃 上次commit两个月前 这边合着测试一下大概问题不大?

@Fangliding
Copy link
Member Author

好了 现在支持 "echDohServer": "https://1.1.1.1/dns-query" 这样的方法代替echConfig了 设置了600秒TTL缓存 要求设置 serverName(不然去查谁呢)

@Fangliding
Copy link
Member Author

好了已经通过测试 websocket可以设置doh sever然后通过ECH连接到cloudflare
splithttp和grpc大概也是可以的

@yuhan6665
Copy link
Member

看了一下非常棒!建议及早合并

@yuhan6665 yuhan6665 changed the title 尝试支持 TLS Encrypted Client Hello ECH: client support TLS Encrypted Client Hello Sep 16, 2024
@dyhkwong
Copy link
Contributor

dyhkwong commented Sep 26, 2024

既然这个在 v2fly 和这里都开了并且不是 draft、没有进一步行动,那么:

  • mutex 用错
  • ServerName 用错,为什么不用 *tls.Config 的?(e.g. sni 不填,服务器地址是域名)
  • proto 里的 ech_config 类型可以为 bytes
  • 根据 RFC,DoH 的 message id “应该”为 0
  • 能用正常方法读取的为什么要用正则表达式去读取?
for _, answer := range msg.Answer {
	if https, ok := answer.(*dns.HTTPS); ok && https.Hdr.Name == dns.Fqdn(domain) {
		for _, v := range https.Value {
			if echConfig, ok := v.(*dns.SVCBECHConfig); ok {
				return echConfig.ECH, answer.Header().Ttl, nil
			}
		}
	}
}
  • 完全没有 context 管理

建议重写或转 draft

@Fangliding
Copy link
Member Author

@dyhkwong 感谢指正 有的问题我也知道不过没打算合我也没动了 等会改一下

不过有必要ctx吗 log里用Background的原因是GetTLSConfig这个函数就没有ctx 至于内部管理 我想这么简单大概没有必要?

@RPRX
Copy link
Member

RPRX commented Oct 2, 2024

隔壁咋已经合了,就是说现在没 uTLS 支持不会觉得很鸡肋吗,等待 uTLS 更新

@Fangliding
Copy link
Member Author

Fangliding commented Feb 8, 2025

稍微更新了一下 以便可能有人需要

区别 rebase到最新的commit 理论上可以支持XHTTP(3)

删除win7不支持的警告 由于使用魔改go而不是旧版本所以可以正常使用

proto文件让位到20 避免跟目前在开发的mitm功能撞车 顺便mitm功能可以扩展到接管所有被墙的CF网站(通过ECH)

@Fangliding
Copy link
Member Author

Fangliding commented Feb 8, 2025

稍微挂个简单点的测试
curl http://crypto.cloudflare.com:443/cdn-cgi/trace --resolv crypto.cloudflare.com:443:127.0.0.1 可以看到 sni=encrypted

{
    "log": {
        "loglevel": "debug"
    },
    "inbounds": [
        {
            "port": 443,
            "listen": "127.0.0.1",
            "tag": "in",
            "protocol": "dokodemo-door",
            "settings": {
                "address": "crypto.cloudflare.com",
                "port": 443,
                "network": "tcp"
            }
        }
    ],
    "outbounds": [
        {
            "tag": "tls-repack",
            "protocol": "freedom",
            "streamSettings": {
                "security": "tls",
                "tlsSettings": {
                    "serverName": "crypto.cloudflare.com",
                    "fingerprint": "unsafe",
                    "echConfigList": "fimtale.com+udp://1.1.1.1",
                    "alpn": [
                        "http/1.1"
                    ]
                }
            }
        }
    ]
}

@Fangliding
Copy link
Member Author

好了

@Fangliding
Copy link
Member Author

Fangliding commented Mar 9, 2025

我编写了一个分支 扩展了内部DNS系统让它支持更多DNS query类型并拿来处理ECH config yysy有够史的 5f50488
拿不准该不该支持 目前只写了一个DOH的支持 别的也够呛 就放在其他分支了

@RPRX
Copy link
Member

RPRX commented Mar 10, 2025

我确实觉得应该提供一个选择,允许客户端用自己的 DNS 去查

@Fangliding
Copy link
Member Author

Fangliding commented Mar 10, 2025

我确实觉得应该提供一个选择,允许客户端用自己的 DNS 去查

我已经提过了 golang没有提供这个查询函数 我已经提了估计还得等 甚至dnsmessage都还是草案 现在这个还有那个内置DNS分支还是用miekg/dns弄的

@Fangliding
Copy link
Member Author

这个弄好后要把 ECH 参数加进 GUI 和分享链接,话说现在 cf 的 ECH 还是只能 cloudflare-ech.com 吗,希望 GFW 晚点封

IETF 有一个还在讨论的扩展 允许这个外部SNI随机

自用的话可以自定义一个外部SNI加上解密失败自动回落到某个外部网站去 然后得到一个类似reality的东西( 不过人家可以光明正大放在扩展里

顺便如果以后把reality相关信息塞进ECH grease可以做到兼容quic(大概还需要处理一些quic帧)

@RPRX
Copy link
Member

RPRX commented Mar 13, 2025

自用的话可以自定义一个外部SNI加上解密失败自动回落到某个外部网站去 然后得到一个类似reality的东西( 不过人家可以光明正大放在扩展里

除此之外还要有一开始就默认双向对拷、server 消息的长度模仿,客户端 pin 以及 spider 机制,这一整套下来基本上就是 REALITY 了,只是验证信息从塞 session id 里改到了塞 ECH 扩展里,不过它缺少 AEAD 附带验证整个外部 Client Hello,可以被攻击者替换

@RPRX
Copy link
Member

RPRX commented Mar 13, 2025

顺便如果以后把reality相关信息塞进ECH grease可以做到兼容quic(大概还需要处理一些quic帧)

可以做到,但 QUIC 要装得像不太好搞,要么就偷 Caddy 要么就改 Nginx

但还有个致命问题是 QUIC 允许连接迁移能暴露这是端口转发,虽然 quic-go 服务端现在似乎还没支持它

虽然 REALITY 的目标本身就是对外表现为端口转发,看 GFW 怎么想了

@RPRX

This comment was marked as off-topic.

@RPRX

This comment was marked as off-topic.

@RPRX

This comment was marked as off-topic.

@RPRX
Copy link
Member

RPRX commented Mar 24, 2025

折叠了一些对纯傻逼输出情绪的内容,这一天天的太难了

前几天我在尝试把 REALITY 库升级到 Go 1.24,结果大量对 crypto/internal 的引用让我绷不住了,Golang 弄这么多 internal 干啥,还不给开 unsafe 绕过,我在想可能 REALITY 库不能把它们放 internal 里,VLESS encryption 才有希望,因为 uTLS 也是 internal

@seiceon

This comment was marked as spam.

@seiceon

This comment was marked as spam.

@RPRX

This comment was marked as off-topic.

@RPRX

This comment was marked as off-topic.

@dingai6230

This comment was marked as spam.

@RPRX

This comment was marked as off-topic.

@dingai6230

This comment was marked as spam.

@dingai6230

This comment was marked as spam.

@RPRX

This comment was marked as off-topic.

@RPRX

This comment was marked as off-topic.

@dingai6230

This comment was marked as spam.

@dingai6230

This comment was marked as spam.

@RPRX

This comment was marked as off-topic.

@RPRX
Copy link
Member

RPRX commented Mar 26, 2025

传统的公私钥用法,比如你用私钥签个消息,希望“任何人”都能拿你的公钥来验证它,或者“任何人”都能用公钥加密消息但只有你能用手里的私钥解密,此时公钥放互联网上当然没问题,因为目标就是“任何人”,或者 ECDHE,公钥本来就是在公网上传输的

但是 REALITY 的公钥就是连上 REALITY 服务端的许可,所以要求你只给“你的用户”并按 password 来保存,而不是“任何人”,对象不同,且它的 x25519 是两端本地直接进行的,而没有 plain text 传公钥这个环节,行为不同

@RPRX

This comment was marked as off-topic.

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.

7 participants