Rust in Everything
基于 rustls 0.23.46

rustls 源码解析

rustls 是一个用 Rust 写的 TLS 库,实现 TLS 1.2 和 TLS 1.3 的客户端与服务端。它不开套接字:ClientConnection / ServerConnection 只是一段「加密管道」,调用方用 read_tls 把从网络读到的字节交给它,用 write_tls 取走要发出去的字节,再用 process_new_packets 让它处理。README 对自己的定位是「aims to provide a good level of cryptographic security, requires no configuration to achieve that security, and provides no unsafe features or obsolete cryptography by default」。握手是一串 Box<dyn State>,每个 Expect* 结构只接受它等的那条消息;TLS 1.3 的密钥日程是一串 KeySchedule* 类型,上一步被消耗才能得到下一步;所有密码学原语都来自一个可替换的 CryptoProvider(默认 aws-lc-rs,可选 ring);证书路径校验交给单独的 crate rustls-webpki。这些说法落在哪些代码上、和 OpenSSL 比能测出什么,见rustls 解决什么问题。

锁定的版本

项本卷说明
rustls0.23.46,tag v/0.23.46 → f04804fa(2026-10-10,「Delay saving KX hint until handshake confirmation」)tag 名里带斜杠;crates.io 上 rustls-0.23.46 包的 .cargo_vcs_info.json 记的就是这个 commit
分支0.23.x 在 rel-0.23 分支上发版tag 不是 main 的祖先,两者的分叉点是 4e0b5fed(2025-06-30,「Bump version to 0.23.29」);main 是 0.24 的开发线,本卷不讲
历史首个提交 2016-05-02,tag 上可达 4134 个提交仓库根目录的 CHANGELOG.md 只有一句话,指向 GitHub releases 页
rustls-webpki0.103.16,tag v/0.103.16 → 8ecdcc7(2026-10-09)证书路径校验,0.23.46 实际解析到的版本
rustls-pki-types1.15.1,tag v/1.15.1 → e29ad15(2026-07-22)CertificateDer、PrivateKeyDer、ServerName、UnixTime、PEM 解析
密码学后端ring 0.17.14、aws-lc-rs 1.19.0(aws-lc-sys 0.47.0)只讲 rustls 怎么接它们,不讲它们的内部

源码链接一律指向 tag,形如 KeyScheduleHandshakeStart;webpki 和 pki-types 的链接指向各自仓库的 tag。

讲什么

  • 连接与 sans-IO 接口:ConnectionCommon、CommonState、read_tls / write_tls / process_new_packets、Stream / StreamOwned、不缓冲的 unbuffered 接口。
  • 记录层:编解码(Codec)、去帧与输入缓冲、加密与序列号、分片与发送缓冲。
  • 握手:State trait 与 Expect* 状态、客户端与服务端的 TLS 1.3、密钥日程、会话恢复与 0-RTT;TLS 1.2、ECH、QUIC、kTLS 各一节。
  • 证书校验:WebPkiServerVerifier 怎么调 rustls-webpki,路径构建和它的预算,客户端证书与自定义校验器。
  • 密码学提供者:CryptoProvider 的字段、进程级默认值、两个内置 provider 的套件与密钥交换组。
  • 只提一句:bogo / openssl-tests / fuzz 等测试设施、FIPS 认证本身(只讲 fips feature 改了什么)、rustls-post-quantum 实验 crate。

三条主线

全卷沿三条主线展开,每页开头标出自己在哪条线上:

  • 握手主线(一次 TLS 1.3 完整握手):ClientConnection::new 发出 ClientHello → ServerConnection 选参数,回 ServerHello 和一条加密记录(EncryptedExtensions、Certificate、CertificateVerify、Finished)→ 客户端逐个 Expect* 状态校验,推进密钥日程,发 Finished → 双方进入应用数据阶段 → 服务端发两张会话票据。
  • 记录主线(一段应用数据的往返):writer().write_all → 分片 → 加密(序列号与 nonce)→ write_tls 交给调用方 → 对端 read_tls 进缓冲 → process_new_packets 去帧、解密 → reader().read。
  • 校验主线(一张服务器证书怎么被信任):WebPkiServerVerifier::verify_server_cert → rustls-webpki 的路径构建(签发者匹配、约束、有效期、预算上限)→ 名字匹配 → CertificateVerify 的签名 → 吊销检查(可选)。

每条线的每一步落在哪个函数、哪一页讲,见怎么读这一卷。握手主线和记录主线在本机上的逐条记录实测见一次握手。

flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> records[02 记录层]
  records --> hs[03 握手]
  hs --> verify[04 证书校验]
  verify --> crypto["05 密码学提供者"]
  crypto --> design[06 设计取舍]
  design --> pit[07 常见坑]
  pit --> site[08 在本站中]
  site --> hands[09 动手]

实验环境

关于行为的说法(握手几轮、每条记录多长、用哪个密钥加密、报什么错、耗时多少)都配一个小程序和它的真实输出。平台是 M2 Max、macOS 27、rustc 1.99.0(aarch64)。实验程序依赖 crates.io 上的 rustls = "=0.23.46",同时打开 ring 和 aws_lc_rs 两个 feature,所以每个配置都用 builder_with_provider 显式指定 provider。所有握手都在进程内存里或 127.0.0.1 上完成,不连外网;对照 OpenSSL 用本机的 openssl 命令(Homebrew 的 OpenSSL 4.1.0,不是 macOS 自带的 LibreSSL)。

各部分

  1. 00 导读:锁定版本;三条主线每一步落在哪;feature 表和它们互相牵连的地方;manual/ 模块怎么当资料读;rustls/src 各目录分工、依赖方向与工作区里的其他 crate。
  2. 01 全景:rustls 不做什么(实测:CA 证书不能直接当服务器证书);两个 provider 的握手耗时与二进制大小,和 OpenSSL 在 127.0.0.1 上的对照;sans-IO 的驱动循环和漏掉一步时的症状;一次握手的 9 条记录,每条用哪个密钥加密。
  3. 02 记录层:编解码、去帧与缓冲、加密与序列号、发送路径。
  4. 03 握手:状态机、客户端 TLS 1.3、服务端 TLS 1.3、密钥日程、会话恢复与 0-RTT、TLS 1.2 与 ECH / QUIC / kTLS。
  5. 04 证书校验:服务器证书校验器、rustls-webpki 的路径构建、客户端证书与自定义校验。
  6. 05 密码学提供者:CryptoProvider、套件与密钥交换组。
  7. 06 设计取舍:sans-IO、类型驱动的状态机与密钥日程、不实现旧协议、可替换的密码学后端、校验放在独立 crate。
  8. 07 常见坑:十八个坑,报错原文做标题,每个都先跑出最小复现。
  9. 08 在本站中:本站的 sqlx、lettre 经 ring provider 用 rustls 0.23.46;reqwest 同时编进了两种 TLS。
  10. 09 动手:不依赖 rustls,按 RFC 8446 写 TLS 1.3 的密钥日程,用 RFC 8448 的测试向量核对,再解开本卷抓到的一条加密记录。

适合谁读

  • 用过 rustls(或 tokio-rustls、reqwest、sqlx 的 rustls feature)、想知道一次 connect 背后发生了什么的读者:先读 01 全景 的四页,再读 03 握手。
  • 熟悉 OpenSSL 的读者:rustls 解决什么问题 列出了它不做的事和能测的差别,sans-IO 讲清楚为什么没有 SSL_read / SSL_write 那样的接口。
  • 需要的前置知识:TLS 1.3 握手的大致流程(ClientHello、ServerHello、Finished);对称加密、Diffie-Hellman 密钥交换、数字签名各是做什么的;Rust 的 trait 对象和 std::io::{Read, Write}。不需要 async:rustls 本身是同步的,异步运行时由 tokio-rustls 这样的外层 crate 对接。

💬 关联讨论

发起讨论
加载中...