py-libp2p 里藏着个会骗过测试的 WebRTC 证书坑
/webrtc-direct/certhash/<hash>。拨号时 DTLS 握手拿到证书,算哈希,跟地址里对比,不用 CA,地址本身就是 pin。py-libp2p 要让 aiortc 用咱们自己生成的 libp2p 证书,代码写得挺直白:config = RTCConfiguration(certificates=[rtc_cert])
return RTCPeerConnection(configuration=config)aiortc 1.5 以后这写法直接报 TypeError,certificates= 参数被干掉了。网上一搜、一改,全是把证书挂到实例属性上:
pc = RTCPeerConnection(configuration=config)
pc._certificates = [rtc_cert] # 看着对劲,实则无效
return pc
单元测试全绿,loopback echo 测试也过——开 data channel、发 payload、收回 payload,啥毛病没有。我差点就这么合进 main 分支发版了。
真正的坑在 pc._certificates 这行。aiortc 内部根本不读这个属性,它读的是 self.__certificates。Python 对类体内的双下划线属性会做 name mangling,编译成 self._RTCPeerConnection__certificates。我在外面赋值的 _certificates 是个全新的、永远不会被读到的属性;真正参与 SDP 生成、写进 a=fingerprint 的,还是 aiortc 自己生成的那张证书。
后果很清楚:
- multiaddr 里广播的是 我们的 证书哈希
- DTLS 握手用的是 aiortc 自动生成 的证书
- 真实拨号全挂:
Remote DTLS fingerprint does not match certhash
loopback 测试为啥过?因为它根本不校验 fingerprint 对 multiaddr,只是把字节原路吐回来。唯一坏掉的安全属性,恰好是唯一没被测到的属性。
这 bug 卡在两个系统的缝隙里:Python 的语言规则(name mangling 防止子类误覆盖)跟 aiortc 的内部封装(靠 __certificates 实现私有)各自都没毛病,但你从类外部去摸私有状态时,只有 mangled name 能生效,直觉写的无 mangling 名字是静默无效的——没有任何报错、警告、异常,只是下游的安全属性挂了。
修法是显式写 mangled name,且要在任何 SDP 操作触发前完成:
pc = RTCPeerConnection(configuration=config)
pc._RTCPeerConnection__certificates = [rtc_cert] # 这才是真正的槽位
return pc顺便把循环回测里加一步:解析对端 SDP 里的 fingerprint,跟 multiaddr 里的 certhash 对比。没有这步,绿测就是谎言。
现在每次升级 aiortc 我都先跑一遍 grep -r "__certificates" site-packages/aiortc,确认内部字段名没变。库的私有实现细节随时可能变,但只要还用双下划线,mangling 规则就得记在脑子里——别信直觉写的属性名,信 dir(pc) 里真正带着类名前缀的那个。
