Rust in Everything
基于 rustls 0.23.46

源码地图

主线位置:三条主线的地图。握手主线走 client/、server/、tls13/;记录主线走 msgs/、record_layer.rs、conn.rs、common_state.rs、vecbuf.rs;校验主线从 webpki/ 出去,进入单独的 rustls-webpki 仓库。本页先用 cargo tree 看 rustls 依赖谁,再看 rustls/src 里每个目录管什么、互相怎么引用,最后说明工作区里其他 crate 的用途。

flowchart TB
  app[应用] --> conn["conn 与 common_state"]
  conn --> sm["client 与 server 状态机"]
  conn --> rl["record_layer"]
  sm --> ks["tls13 密钥日程"]
  sm --> msgs["msgs 线上格式"]
  rl --> msgs
  sm --> ver["webpki 适配层"]
  ver --> wp["rustls-webpki"]
  ks --> cr["crypto 接口"]
  rl --> cr
  cr --> prov["ring 或 aws-lc-rs"]
  wp --> prov

依赖图:cargo tree 核实

一个只开 ring、std、tls12(default-features = false)的小程序,cargo tree -e normal 的原样输出:

intro-size-ring v0.1.0 (~/exp-rustls/intro-size-ring)
`-- rustls v0.23.46
    |-- once_cell v1.21.4
    |-- ring v0.17.14
    |   |-- cfg-if v1.0.5
    |   |-- getrandom v0.2.17
    |   |   |-- cfg-if v1.0.5
    |   |   `-- libc v0.2.191
    |   |-- libc v0.2.191
    |   `-- untrusted v0.9.0
    |-- rustls-pki-types v1.15.1
    |   `-- zeroize v1.9.1
    |-- rustls-webpki v0.103.16
    |   |-- ring v0.17.14 (*)
    |   |-- rustls-pki-types v1.15.1 (*)
    |   `-- untrusted v0.9.0
    |-- subtle v2.6.1
    `-- zeroize v1.9.1

把 ring 换成 aws_lc_rs,ring 那一支变成 aws-lc-rs v1.19.0 → aws-lc-sys v0.47.0,rustls-webpki 下面也跟着换成 aws-lc-rs。这棵树说明三件事:

  • rustls 自己的直接依赖只有六个:两个兄弟 crate(webpki、pki-types),一个密码学后端,加上 subtle(常数时间比较)、zeroize(清零秘密)、once_cell(进程级默认 provider 的存放处)。开 logging 时再加 log。
  • rustls-webpki 也依赖同一个密码学后端。 rustls 的 feature ring 写的是 ["dep:ring", "webpki/ring"]:证书链上的签名由 webpki 用 ring(或 aws-lc-rs)验证,rustls 的 provider 只是把 webpki 提供的算法表交给校验器。
  • 没有 tokio,没有 std::net。连接对象不知道套接字是什么,见 sans-IO。

rustls/src 下的目录

按 tag 统计 rustls/src 的 .rs 行数(含文件内的 #[cfg(test)] 单元测试,不含 client/test.rs 这类独立测试文件和 verifybench.rs,这些一共 2898 行),合计 46310 行:

目录或文件行数管什么讲它的页
crypto/9461CryptoProvider 结构和一组 trait(cipher.rs、hash.rs、hmac.rs、tls13.rs、signer.rs、hpke.rs);两个内置实现 ring/(2967 行)和 aws_lc_rs/(4263 行,多出后量子 pq/ 和 HPKE)05 密码学提供者
msgs/7919线上格式:codec.rs(Codec、Reader)、handshake.rs(3266 行,所有握手消息和扩展)、deframer/(输入缓冲与去帧)、fragmenter.rs、message/、persist.rs(会话票据里存什么)02 记录层
client/7619客户端:client_conn.rs(ClientConfig、ClientConnection)、hs.rs(ClientHello 与 ServerHello)、tls13.rs、tls12.rs、ech.rs(1365 行)、handy.rs(会话缓存)、builder.rs03 握手
server/5228服务端:server_conn.rs(ServerConfig、ServerConnection、Acceptor)、hs.rs(ExpectClientHello)、tls13.rs、tls12.rs、handy.rs、builder.rs03 握手
webpki/1863和 rustls-webpki 的适配层:RootCertStore、WebPkiServerVerifier、WebPkiClientVerifier、verify.rs04 证书校验
tls13/1436key_schedule.rs(1311 行,KeySchedule* 类型链)、Tls13CipherSuite密钥日程
conn.rs + conn/1335 + 892ConnectionCommon、ConnectionCore、Reader / Writer;conn/unbuffered.rs(不缓冲的接口)、conn/kernel.rs(kTLS)sans-IO
common_state.rs1136CommonState:两个方向的记录层、发送缓冲、wants_read / wants_write、告警发送路径
error.rs1390Error 和它的子枚举(CertificateError、PeerMisbehaved 等)—
quic.rs1056QUIC:只导出密钥和握手消息,不碰记录层TLS 1.2 与其他
compress.rs725RFC 8879 证书压缩—
manual/671编进 rustdoc 的设计说明(全是文档注释)怎么读这一卷
record_layer.rs358RecordLayer:加解密器、序列号加密与序列号
hash_hs.rs347握手转录哈希密钥日程
vecbuf.rs377ChunkVecBuffer:发送缓冲和明文接收缓冲发送路径
stream.rs306Stream / StreamOwnedsans-IO
其余约 4200lib.rs(718 行,大半是文档)、builder.rs、enums.rs、tls12/、ticketer.rs、verify.rs、suites.rs、key_log*.rs、limited_cache.rs 等—

卷目录里的规模估计和实测有出入:crypto/ 约 9.5k 而不是 8k,两个 provider 也不是各一半(aws-lc-rs 多出后量子和 HPKE);client/ 约 7.6k,server/ 约 5.2k,都比估计大。

lib.rs 顶上有两行全 crate 的约束:#![forbid(unsafe_code, unused_must_use)] 和 #![no_std]。rustls 本身没有一行 unsafe,需要 unsafe 的地方都在密码学后端里;no_std 加上 extern crate alloc,让 std 成为可选 feature,注释说这样「forces one to explicitly import」std 才有的东西,防止不知不觉依赖 std。

模块之间的依赖方向

按 use crate::... 统计,方向大体是单向的:

  • msgs/ 是最底层,几乎只引用 msgs 自己、error、enums。例外是 msgs/persist.rs 引用了 client::ResolvesClientCert。
  • record_layer.rs 只引用 crypto、msgs、error;conn.rs 引用 common_state、msgs、record_layer、vecbuf,不引用 client 或 server:连接对象对两侧是泛型的,ConnectionCommon<Data> 里的 Data 是 ClientConnectionData 或 ServerConnectionData。
  • client/ 和 server/ 在最上面,引用 msgs(各 53 处和 34 处)、crypto、conn、common_state、tls13、hash_hs、verify。两者互不引用。
  • crypto/ 定义 trait,tls13/ 用它们派生密钥;例外是两个 provider 的 ticketer.rs 实现 server::ProducesTickets,crypto/signer.rs 引用两侧的证书解析 trait。
  • webpki/ 是到 rustls-webpki 的唯一出口。握手状态机只经由 verify.rs 里的 ServerCertVerifier / ClientCertVerifier trait 调证书校验;直接引用 webpki/ 的只有几处:client/builder.rs 建默认的 WebPkiServerVerifier,两个 provider 的 mod.rs 拿 WebPkiSupportedAlgorithms(签名算法表),server/handy.rs 按 SNI 选证书时检查名字。

工作区里的其他 crate

根目录 Cargo.toml 的 members 有十二个,default-members 只有 examples 和 rustls。除了 rustls 和 rustls-post-quantum(0.2.2),都写了 publish = false:

成员行数用途
rustls见上本卷的主体
examples307912 个可执行例子:simpleclient、simpleserver(用 Stream)、tlsclient-mio、tlsserver-mio(mio 事件循环)、unbuffered-client / -server / -async-client、server_acceptor、ech-client、limitedclient、0-RTT 的一对
rustls-test1900测试共用的代码(证书、握手辅助函数)
bogo2447让 BoringSSL 的 BoGo 测试套件驱动 rustls 的适配层
openssl-tests1064需要 OpenSSL 才能跑的互通测试
connect-tests407连真实网站的测试
ci-bench / rustls-bench1905 / 1536CI 里的基准测试和本地基准工具
provider-example1067用 RustCrypto 写的一个最小自定义 provider
rustls-provider-test123provider 的集成测试
rustls-fuzzing-provider652给模糊测试用的 provider
rustls-post-quantum262早期的后量子实验 crate,0.23.x 里 X25519MLKEM768 已并入 aws-lc-rs provider

fuzz/ 在 exclude 里(要 nightly)。另有 test-ca/(测试用的证书)、website/、audit/、admin/ 目录。

两个兄弟仓库

crate版本规模管什么从哪读起
rustls-webpki0.103.16src/ 10244 行X.509 证书解析与路径校验:verify_cert.rs(1523 行,路径构建与预算)、subject_name/(DNS 名和 IP 地址匹配)、crl/(吊销列表)、der.rs;ring_algs.rs / aws_lc_rs_algs.rs 是两个后端的签名算法表EndEntityCert::verify_for_usage,见 路径构建
rustls-pki-types1.15.1src/ 约 4100 行只有类型,不做密码学:CertificateDer、PrivateKeyDer、ServerName(server_name.rs)、UnixTime、PEM 解析(pem.rs)、算法标识(alg_id.rs)lib.rs;rustls::pki_types 原样转导出它

卷目录估计 webpki「约 9k」,按 tag 实数是 10244 行(含 648 行的 alg_tests.rs)。

小结

  • rustls 的直接依赖只有 webpki、pki-types、一个密码学后端和 subtle / zeroize / once_cell;webpki 用同一个后端验签名。
  • rustls/src 合计 46310 行:crypto/、msgs/、client/、server/ 四大块占四分之三。
  • 依赖方向:msgs 在底,record_layer / conn / common_state 居中,client / server 在顶且互不引用;证书校验只从 webpki/ 出去。
  • 全 crate forbid(unsafe_code) 并且是 no_std 加 alloc,std 是 feature。
  • 工作区十二个成员,只有 rustls 和 rustls-post-quantum 发布;examples/ 的 simpleclient 和 tlsclient-mio 是读接口用法的起点。

💬 关联讨论

发起讨论
加载中...