Mbed TLS 的 TLS 1.3 支持全解析:能力边界、配置项与握手实现架构(TEN-framework 第三方依赖深度解读)

原创2026-10-07 17:28:321,717 阅读
文章标签:人工智能AI AgentAgent 框架多模态语音音频后端

Mbed TLS 的 TLS 1.3 支持全解析:能力边界、配置项与握手实现架构(TEN-framework 第三方依赖深度解读)

本指南以 TEN-framework 仓库内第三方依赖 Mbed TLS 的架构文档 tls13-support.md 为主体,系统梳理其 TLS 1.3 实现的能力边界(支持的密码套件、ClientHello 扩展、签名算法与密钥交换方式)、编译与运行时配置方法,并深入剖析握手状态机、消息处理/写入手管线的代码组织方式与命名约定。读完本文,你既能快速判断 Mbed TLS 在当前构建中支持哪些 TLS 1.3 特性、如何开启,也能读懂 ssl_tls13_*.c 系列源码的演进结构与审查规范。

背景:Mbed TLS 的 TLS 1.3 实现处于什么阶段

TLS 1.3(RFC 8446)是现代传输安全的核心协议,其与 TLS 1.2 的最大差异在于:握手流程从两次往返压缩到一次(或 0-RTT)、移除了静态 RSA 与 CBC 模式、所有对称加密均采用 AEAD 且密钥派生统一基于 HKDF。Mbed TLS 在仓库中以 library/ssl_tls13_client.c、library/ssl_tls13_server.c、library/ssl_tls13_generic.c、library/ssl_tls13_keys.c 四个模块实现 TLS 1.3,并通过编译期宏 MBEDTLS_SSL_PROTO_TLS1_3 控制启停。

需要特别说明的是:这是一份部分实现(partial implementation),并非对 RFC 8446 的完整覆盖。Mbed TLS 官方将开发路线建立在 hannestschofenig/mbedtls 的 TLS 1.3 原型之上,并持续把开发分支的提交合并进原型、再反向 upstream,具体缺口见下文"上游化进度"一节。因此在当前仓库中启用 TLS 1.3 之前,务必先对照本文的能力清单确认你的使用场景是否在支持范围内。

编译期开关:如何开启与关闭 TLS 1.3

TLS 1.3 的使能完全由配置宏 MBEDTLS_SSL_PROTO_TLS1_3 控制,定义于 include/mbedtls/mbedtls_config.h:

/**
 * \def MBEDTLS_SSL_PROTO_TLS1_3
 *
 * Enable support for TLS 1.3.
 *
 * \note The support for TLS 1.3 is not comprehensive yet, in particular
 *       pre-shared keys are not supported.
 *       See docs/architecture/tls13-support.md for a description of the TLS
 *       1.3 support that this option enables.
 *
 * Requires: MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
 * Requires: MBEDTLS_PSA_CRYPTO_C
 * ...
 */
//#define MBEDTLS_SSL_PROTO_TLS1_3

该宏默认处于注释状态(未启用),启用前必须满足两个硬性依赖:

  • MBEDTLS_SSL_KEEP_PEER_CERTIFICATE:TLS 1.3 实现要求保留对端证书,必须保持默认的"启用"状态;
  • MBEDTLS_PSA_CRYPTO_C:TLS 1.3 的密钥派生与 AEAD 加密构建在 PSA Crypto 之上,同样必须保持启用。若希望所有密码学操作(含 X.509、与 TLS 1.2 共用的记录保护、握手哈希)都走 PSA,还需额外打开 MBEDTLS_USE_PSA_CRYPTO,否则 TLS 1.3 依赖的 PSA 之外的部分仍会走非 PSA 实现。

配置宏的合法性检查在 include/mbedtls/check_config.h 中完成,构建时若遗漏上述依赖会直接报错,而不是在运行期才暴露。

相关配置宏一览

配置宏 默认状态 说明
MBEDTLS_SSL_PROTO_TLS1_3 关闭 TLS 1.3 总开关
MBEDTLS_SSL_PROTO_TLS1_2 开启 TLS 1.2 开关,可与 TLS 1.3 相互独立编译
MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE 关闭 RFC 8446 附录 D.4 的中间盒兼容模式,开启会增加少量线上字节,但不影响与其他 TLS 1.3 实现的互通;除带宽极端敏感外官方建议开启
MBEDTLS_SSL_SESSION_TICKETS 开启 RFC 5077 会话票据,当前 TLS 1.3 实现不消费该特性

仓库提供了纯 TLS 1.3 的测试配置 tests/configs/tls13-only.h,它开启 MBEDTLS_SSL_PROTO_TLS1_3 与兼容模式、同时关闭 MBEDTLS_SSL_PROTO_TLS1_2、MBEDTLS_SSL_PROTO_DTLS 等宏,是验证"只跑 TLS 1.3"构建形态的现成参考:

#define MBEDTLS_SSL_PROTO_TLS1_3
#define MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE
#undef MBEDTLS_SSL_ENCRYPT_THEN_MAC
#undef MBEDTLS_SSL_EXTENDED_MASTER_SECRET
#undef MBEDTLS_SSL_PROTO_TLS1_2
#undef MBEDTLS_SSL_PROTO_DTLS

能力边界:TLS 1.3 到底支持到什么程度

客户端/服务端与密钥交换方式

  • Mbed TLS 同时实现了 TLS 1.3 的客户端与服务器端。
  • 密钥交换仅支持 ECDHE(椭圆曲线迪菲-赫尔曼临时密钥);
  • 不支持 DHE(有限域上的临时 DH);
  • 不支持预共享密钥(PSK),包括任何形式的会话恢复,因此不支持 0-RTT(early data)发送。

从源码结构看,TLS 1.3 侧统一经由 library/ssl_tls.c 分派:客户端握手步进 mbedtls_ssl_tls13_handshake_client_step() 与服务器端 mbedtls_ssl_tls13_handshake_server_step() 分别定义于 ssl_tls13_client.c 与 ssl_tls13_server.c。值得注意的是,虽然当前构建不支持 PSK,但运行时 API 层已经预留了密钥交换模式的概念,见 include/mbedtls/ssl.h:

#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK            ( 1u << 0 )
#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL      ( 1u << 1 )
#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL  ( 1u << 2 )

并通过 mbedtls_ssl_conf_tls13_key_exchange_modes()(ssl.h)在运行时配置允许的模式组合。这也印证了原文档中的判断:TLS 1.3 的密钥交换配置正沿着 PSK / PSK_EPHEMERAL / EPHEMERAL 这套运行时宏体系组织,PSK 相关能力属于"待上游化"部分。

支持的密码套件

TLS 1.3 的密码套件数量远少于 TLS 1.2(不再有"密钥交换 + 认证 + 批量加密"的自由组合),取决于库配置,以下五套理论上均可支持:

  • TLS_AES_128_GCM_SHA256
  • TLS_AES_256_GCM_SHA384
  • TLS_CHACHA20_POLY1305_SHA256
  • TLS_AES_128_CCM_SHA256
  • TLS_AES_128_CCM_8_SHA256

密码套件表在 library/ssl_ciphersuites.c 中维护(该文件同样引用了 MBEDTLS_SSL_PROTO_TLS1_3)。由于 TLS 1.3 全部使用 AEAD,TLS 1.2 时代的 MBEDTLS_SSL_ENCRYPT_THEN_MAC、MBEDTLS_SSL_EXTENDED_MASTER_SECRET 等选项对 TLS 1.3 而言是"不适用(n/a)"。

ClientHello 扩展支持矩阵

原文档给出了一张完整的 ClientHello 扩展支持表,直接继承如下:

扩展 支持
server_name YES
max_fragment_length no
status_request no
supported_groups YES
signature_algorithms YES
use_srtp no
heartbeat no
apln(ALPN) YES
signed_certificate_timestamp no
client_certificate_type no
server_certificate_type no
padding no
key_share YES
pre_shared_key no
psk_key_exchange_modes no
early_data no
cookie no
supported_versions YES
certificate_authorities no
post_handshake_auth no
signature_algorithms_cert no

(注:表中 apln 为原文拼写,对应实际扩展 ALPN,仓库中的 MBEDTLS_SSL_ALPN 宏默认开启。)

可以看到,TLS 1.3 握手所需的核心扩展——supported_versions、key_share、supported_groups、signature_algorithms、server_name、ALPN——均已实现;而 PSK/early data/会话恢复相关扩展全部未实现,与"不支持 PSK、0-RTT"的能力边界完全一致。

支持的椭圆曲线组与签名算法

  • 支持的组:取决于库配置,ECDHE 组理论上全部支持:secp256r1、x25519、secp384r1、x448、secp521r1;有限域组(DHE)不支持。
  • 支持的签名算法(证书与 CertificateVerify 均适用,取决于库配置):ecdsa_secp256r1_sha256、ecdsa_secp384r1_sha384、ecdsa_secp521r1_sha512、rsa_pkcs1_sha256、rsa_pkcs1_sha384、rsa_pkcs1_sha512、rsa_pss_rsae_sha256、rsa_pss_rsae_sha384、rsa_pss_rsae_sha512。
  • 强制算法提醒:在没有应用侧 profile 标准另行规定时,rsa_pkcs1_sha256、rsa_pss_rsae_sha256、ecdsa_secp256r1_sha256 三者为规范第 9.1 节规定的强制项,实现层面需保证默认可用。

签名算法与证书/密钥的匹配校验可在 ssl_tls13_generic.c 的 ssl_tls13_check_sig_alg_cert_key_match() 与 ssl_tls13_select_sig_alg_for_certificate_verify() 中看到对应实现。

版本支持与运行时协商约束

  • 客户端侧支持 TLS 1.2 与 TLS 1.3 的版本协商(通过 supported_versions 扩展向服务器表达能力);
  • 服务器端不参与版本协商;
  • TLS 1.2 与 TLS 1.3 可相互独立地编译启用;
  • 运行时约束:若同时编译启用了 TLS 1.2 与 TLS 1.3,对服务器端点而言,通过 mbedtls_ssl_conf_min_tls_version() / mbedtls_ssl_conf_max_tls_version()(ssl.h)只能把版本区间限定到二者之一;否则 mbedtls_ssl_setup() 会返回 MBEDTLS_ERR_SSL_BAD_CONFIG 错误。

与既有 TLS 1.2 构建选项的兼容性

原文档的核心结论是:TLS 1.3 实现与几乎所有 TLS 1.2 配置选项兼容——开启 TLS 1.3 时,极少需要改动原本为 TLS 1.2 准备的配置。仅有的两个例外是 MBEDTLS_PSA_CRYPTO_C 与 MBEDTLS_SSL_KEEP_PEER_CERTIFICATE(必须保持启用)。

其余 SSL/TLS 相关选项对 TLS 1.3 大多不支持或不适用,完整对照表如下:

Mbed TLS 配置选项 支持
MBEDTLS_SSL_ALL_ALERT_MESSAGES no
MBEDTLS_SSL_ASYNC_PRIVATE no
MBEDTLS_SSL_CONTEXT_SERIALIZATION no
MBEDTLS_SSL_DEBUG_ALL no
MBEDTLS_SSL_ENCRYPT_THEN_MAC n/a
MBEDTLS_SSL_EXTENDED_MASTER_SECRET n/a
MBEDTLS_SSL_KEEP_PEER_CERTIFICATE no (1)
MBEDTLS_SSL_RENEGOTIATION n/a
MBEDTLS_SSL_MAX_FRAGMENT_LENGTH no
MBEDTLS_SSL_SESSION_TICKETS no
MBEDTLS_SSL_SERVER_NAME_INDICATION yes
MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH no
MBEDTLS_ECP_RESTARTABLE no
MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED no
MBEDTLS_KEY_EXCHANGE_PSK_ENABLED n/a (2)
MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_RSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED n/a
MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED n/a
MBEDTLS_PSA_CRYPTO_C no (1)
MBEDTLS_USE_PSA_CRYPTO yes

注释说明:(1) 这些选项必须保持默认的"启用"状态;(2) TLS 1.3 的密钥交换配置选项未来将围绕密钥交换模式组织,即上文提到的 MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE/PSK/PSK_EPHEMERAL/EPHEMERAL 运行时宏体系。

值得注意的例外是 MBEDTLS_SSL_SERVER_NAME_INDICATION(SNI)与 MBEDTLS_USE_PSA_CRYPTO 在 TLS 1.3 下为 yes——SNI 对应 ClientHello 表中的 server_name 扩展。

原型上游化进度:尚未合并的部分

Mbed TLS 的 TLS 1.3 基于外部原型演进,以下部分尚未上游到当前仓库:

  1. 预共享密钥、会话恢复与 0-RTT 数据(客户端与服务器端两侧均未完成)——这是当前实现最大的能力缺口,与本仓库"不支持任何形式的会话恢复"的行为一致。
  2. 新的 TLS 消息处理栈(MPS,Message Processing Stack):原型开发伴随 TLS 消息层的重写,涵盖记录解析、握手重组、DTLS 重传状态机等底层细节。MPS 由四层加两个辅助组件构成:
    • Layer 1:数据报处理(Datagram handling)
    • Layer 2:记录处理(Record handling)
    • Layer 3:消息处理(Message handling)
    • Layer 4:重传状态机(Retransmission State Machine)
    • Reader:入站数据的指针运算与重组逻辑抽象
    • Writer:出站数据的指针运算与分片逻辑抽象

截至本文档版本,随 MBEDTLS_SSL_PROTO_TLS1_3 一起上游的 MPS 组件仅有 Reader,对应文件为 third_party/mbedtls/library/mps_reader.h(连同 mps_reader.c、mps_common.h、mps_error.h 等辅助文件)。测试方面,tests/suites/test_suite_mps.function 为 MPS 提供了专门的单元测试套件。

TLS 1.3 专用编码规则清单

TLS 1.3 上游化工作的代码审查成本较高,Mbed TLS 在文档中沉淀了一套专用编码规则,作为每次 PR 的自检清单(不替代项目总体编码规范,规范来源见文档中引用的 mbedtls-coding-standards 页面)。这些规则对阅读、贡献 TLS 1.3 代码的人来说是极好的"地图":

命名约定

  • TLS 1.3 专用的 C 模块、头文件、静态函数以 ssl_tls13_ 前缀命名,模块内部的结构体与类型同样如此;
  • 导出的函数、结构体与类型以 mbedtls_ssl_tls13_ 前缀命名;
  • TLS 1.3 专用宏中使用 TLS1_3 字样;
  • 与规范中字段/结构相关的宏与变量名应尽量携带规范字段名;若字段名过长需缩写,则全代码库统一使用同一缩写。

向量(length-prefixed vector)的指针命名惯例

  • <vector_name>_len:存放向量字节长度的变量;
  • p_<vector_name>_len:存放向量长度字段首字节地址的变量;
  • <vector_name>:存放向量值首字节地址的变量;
  • <vector_name>_end:存放向量值末尾后一字节地址的变量。

原文档以 ClientHello 的 cipher_suites 向量为例:

size_t cipher_suites_len;
unsigned char *p_cipher_suites_len;
unsigned char *cipher_suites;

该惯例降低了"拿错地址类型"的风险——例如误把长度字段地址当值地址使用,可能引发严重问题。

字节序与缓冲区安全宏 适用处应使用:

  • MBEDTLS_BYTE_{0-8}:从多字节整数提取单字节;
  • MBEDTLS_PUT_UINT{8|16|32|64}_BE:按大端序把多字节整数写入内存;
  • MBEDTLS_GET_UINT{8|16|32|64}_BE:按大端序从内存读出多字节整数;
  • MBEDTLS_SSL_CHK_BUF_PTR:写输出缓冲区前的空间检查;
  • MBEDTLS_SSL_CHK_BUF_READ_PTR:读输入缓冲区前的数据检查。

前两类宏提升可读性、降低字节序写错的风险;后两类宏把"读写不越界"的指针算术集中化,与上面的指针命名惯例配合,共同规避缓冲区越界。示例:

hs_hdr[1] = MBEDTLS_BYTE_2( total_hs_len );
MBEDTLS_PUT_UINT16_BE( MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS, p, 0 );
MBEDTLS_SSL_CHK_BUF_PTR( p, end, 7 );

其他通用规则

  • 解析/写入函数中一律使用局部变量 p 作为读写指针(该名字在既有 TLS 代码中已广泛使用,避免重蹈 PR #4882 讨论中的覆辙);
  • 读写 TLS 1.3 结构时,在函数注释中附上规范定义的结构;
  • 关联语句分组排列、不插入空行;跨函数复用的常量用宏(如 #define CLIENT_HELLO_RANDOM_LEN 32),仅函数内局部使用的常量无需建宏;
  • 指针声明时解引用符 * 贴近指针名(如 mbedtls_ssl_context *ssl;);
  • 行宽上限 80 字符,字符串字面量可超限以便搜索;重构导致超长时避免在同一次提交内重排(先提交超长行、再单独提交重排,降低审查难度);
  • 连续行中函数/宏参数垂直对齐;参数跨行时按语义分组(例如 mbedtls_ssl_start_handshake_msg( ssl, hs_type, buf, buf_len ); 优于把 buf 与 hs_type 拆开排列)。

握手代码组织:状态机与处理管线

顶层状态机

TLS 1.3 握手协议实现为一个状态机,顶层函数为 mbedtls_ssl_tls13_handshake_client_step()(ssl_tls13_client.c)与 mbedtls_ssl_tls13_handshake_server_step()(ssl_tls13_server.c),二者均为覆盖全部可能状态的 switch 语句。它们由通用的 mbedtls_ssl_handshake_step() 在 ssl_tls.c 中按协议版本分派调用。

状态机设计上有两个值得注意的原则:

  • 并非逐状态顺序遍历:若所有状态都顺序执行,会迫使每个状态处理器重复检查"是否要做某事",因此对可跳过的状态直接绕过。例如客户端侧,若握手基于预共享密钥(不含证书),则证书解析与校验相关状态被跳过。
  • 部分可跳过状态反而强制经过:当"强制经过"能最小化入站/出站密钥更新时机的不确定性时,即使可跳过也会走一遍。客户端侧的 MBEDTLS_SSL_CLIENT_CERTIFICATE 状态即为此类例子——从源码看,该状态处理器位于 ssl_tls13_client.c,且在 mbedtls_ssl_tls13_handshake_client_step() 的 switch 中被显式列出,同时还有 L1955 处的状态回退逻辑。

消息处理(process)管线的五阶段

处理握手消息的处理器命名统一为 mbedtls_ssl_tls13_process_*(如 mbedtls_ssl_tls13_process_certificate、mbedtls_ssl_tls13_process_certificate_verify、mbedtls_ssl_tls13_process_finished_message,见 ssl_tls13_generic.c)。每个处理器内部按阶段拆分,以压缩代码规模、降低出错概率:

  1. 协调阶段(coordination,可选):判断本状态是否应被绕过。判断依据要么仅读取 SSL 上下文的某些字段值,要么读取下一条消息的类型——后者用于尚不确定下一消息类型的情形(例如客户端侧不确定将收到 CertificateRequest 还是其他消息)。除读取下一条记录外,此阶段不应修改 SSL 上下文,因为它可能因下一条握手消息未到达而被重复执行。
  2. 获取阶段(fetching):此时已确定下一握手消息的类型并尝试获取。若未经过涉及"下一记录类型读取"的协调阶段,下一条消息可能尚未到达,处理器返回 MBEDTLS_ERR_SSL_WANT_READ 且不改变当前状态,稍后会被再次调用。仓库为此提供了公共封装 mbedtls_ssl_tls13_fetch_handshake_msg()(ssl_tls13_generic.c)。
  3. 预处理阶段(pre-processing,可选):为消息解析准备 SSL 上下文,或简化解析代码。此阶段可做少量简单解析,如 ServerHello 消息的预处理。
  4. 解析阶段(parsing):解析消息,并尽可能限制对 SSL 上下文的更新。预处理/解析/后处理的分层目的,就是让解析函数只专注解析,从而缩小函数体、简化逻辑。
  5. 后处理阶段(post-processing,可选):解析完成后进一步更新 SSL 上下文,为后续入站/出站消息做准备。例如秘密(secret)与密钥计算、握手消息校验和(transcript hash)更新均发生在这一阶段。
  6. 状态迁移(state change):统一在主状态处理器中完成,便于追踪状态机转移路径。

消息写入(write)管线的五阶段

写入手处理器命名统一为 mbedtls_ssl_tls13_write_*(如 mbedtls_ssl_tls13_write_certificate、mbedtls_ssl_tls13_write_certificate_verify、mbedtls_ssl_tls13_write_finished_message、mbedtls_ssl_tls13_write_change_cipher_spec,均见 ssl_tls13_generic.c)。管线阶段与处理侧对称:

  1. 协调阶段(可选):基于 SSL 上下文字段值判断是否跳过;
  2. 准备阶段(preparation,可选):为消息写入做前置处理或简化写入代码;
  3. 写入阶段(writing):写入消息,并尽可能限制对 SSL 上下文的更新(准备/写入/终结的分层让写入函数只专注写入);
  4. 终结阶段(finalization,可选):写入完成后更新 SSL 上下文,例如握手秘密与密钥计算(如 ServerHello 写入的终结)、服务端切换到出站方向的握手密钥,都在此阶段发生;
  5. 状态迁移(state change):同样统一在主状态处理器中完成。

出站数据刷新的关键约定

数据向对端的发送只发生在 mbedtls_ssl_handshake_step() 中、处理器调用之间,因此处理器无需处理 MBEDTLS_ERR_SSL_WANT_WRITE 错误码;且在调用下一个处理器前所有待发数据都已刷新完毕。这保证处理器在更换出站密钥时无需担心仍有未刷新的待发数据——这是整个写入管线能安全地"在终结阶段切换出站密钥"的前提。

测试与验证路径

当前仓库为 TLS 1.3 提供了多层验证手段,可作为实现能力的事实佐证:

小结:启用 TLS 1.3 前的决策清单

基于本仓库 Mbed TLS 的实际实现,若要启用 MBEDTLS_SSL_PROTO_TLS1_3,建议按以下清单核对:

  1. 确保 MBEDTLS_PSA_CRYPTO_C 与 MBEDTLS_SSL_KEEP_PEER_CERTIFICATE 保持启用;如需全链路 PSA,另开 MBEDTLS_USE_PSA_CRYPTO。
  2. 确认使用场景落在能力边界内:仅 ECDHE、无 PSK/会话恢复/0-RTT;服务端若同时编译了 TLS 1.2,运行时须把 min/max_tls_version 收敛到单一版本。
  3. 按需开启 MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE 以提升中间盒穿透性。
  4. 选择密码套件与曲线组时,以 MBEDTLS_SSL_PROTO_TLS1_3 相关配置为准,并保证 rsa_pkcs1_sha256、rsa_pss_rsae_sha256、ecdsa_secp256r1_sha256 三种强制签名算法可用。
  5. 使用 tests/ssl-opt.sh 的 tls13_kex_modes 用例或 ssl_client2/ssl_server2 对握手做端到端验证。

若需要同时追踪 Mbed TLS 中 TLS 1.3 的实现细节,可直接以 ssl_tls13_generic.c 的消息处理/写入管线为入口,配合本文的"阶段划分 + 命名约定"阅读,可以较快建立起对这套状态机实现的完整心智模型。

登录后查看全文
ten-framework