Rust in Everything
基于 rustls 0.23.46

握手

这一部分讲握手主线的中段:记录层把一条握手消息解出来之后,谁接住它、检查它、根据它算出下一组密钥。rustls 把握手写成一串 Expect* 结构,每个结构只认它在等的那几种消息,处理完把自己消费掉、返回下一个状态;TLS 1.3 的密钥推导也写成一串类型,每一步消耗上一步。两条链并排走:客户端从 ExpectServerHelloOrHelloRetryRequest 走到 ExpectTraffic 的同时,密钥日程从 KeySchedulePreHandshake 走到 KeyScheduleTraffic。

代码分布(按 tag v/0.23.46 统计 .rs 行数,含单元测试):client/hs.rs 1205 行、client/tls13.rs 1732 行、server/hs.rs 和 server/tls13.rs 是服务端的同一套写法,tls13/key_schedule.rs 1311 行(其中约 270 行是测试),hash_hs.rs 347 行,check.rs 只有 70 行。

  1. 状态机:State<Data> trait 只有一个必须实现的 handle,它按值拿走 Box<Self>、返回下一个 Box<dyn State>;ConnectionCore 每解出一条消息就调一次,出错时把错误存进状态槽,之后每次调用都返回同一个错误;不该来的消息由 check.rs 变成 InappropriateHandshakeMessage,统一换成 unexpected_message 告警。
  2. 客户端 TLS 1.3:ClientConnection::new 里就把 ClientHello 排好了队;ring 下 236 字节、一个 X25519 密钥共享,aws-lc-rs 默认 1466 字节、X25519MLKEM768 加一个「顺带」的 X25519;完整握手里客户端真正停留过的六个状态,HelloRetryRequest 怎么多出一轮,以及下次连同一个服务器时怎么记住它要的组。
  3. 服务端 TLS 1.3:ExpectClientHello 怎么选版本、套件、组和证书,Acceptor 先读 ClientHello 再定配置,什么时候发 HelloRetryRequest,票据怎么发。
  4. 密钥日程:RFC 8446 §7.1 的 HKDF 阶梯落成 KeyScheduleEarly → KeySchedulePreHandshake → KeyScheduleHandshakeStart → KeyScheduleHandshake → … → KeyScheduleTraffic;调错顺序在 crate 内部也编译不过;转录哈希 HandshakeHash 什么时候从缓冲变成增量哈希;对着 RFC 8448 和 NIST ACVP 的向量核对推导结果。
  5. 会话恢复与 0-RTT:票据、PSK 绑定值、早期数据和它的重放风险。
  6. TLS 1.2 与其他:TLS 1.2 的状态链,ECH 的两层 ClientHello,QUIC 只拿密钥,kTLS 把密钥交给内核。
flowchart TB
  rec["记录层解出一条消息"] --> core["ConnectionCore process_msg"]
  core --> st["当前 Box dyn State"]
  st --> chk["require_handshake_msg"]
  chk -->|类型不对| alert["unexpected_message"]
  chk -->|类型对| work["校验 推进转录哈希"]
  work --> ks["KeySchedule 下一步"]
  ks --> next["返回下一个状态"]
  next --> core

客户端和服务端共用 State<Data> 这个 trait 和 ConnectionCore 这套驱动,Data 分别是 ClientConnectionData 和 ServerConnectionData。消息怎么从字节变成 Message(去帧、解密、拼接跨记录的握手消息)在记录层一部分;一次完整握手每条记录的长度和密钥,见一次握手。

💬 关联讨论

发起讨论
加载中...