摘要

  • RFC 3078 的 12 位一致性计数器可以指出 MPPE 包发生丢失或乱序,却不能重建丢失的密文,也不能证明后续明文已经交给应用。
  • 无状态模式可根据计数差推进密钥;有状态模式则必须丢弃当前及后续数据、发出 CCP Reset-Request,并等到发送端带 FLUSHED 的包之后才能重新相信密码状态。

最先说明问题的包,恰恰不能使用

接收端原本等待一个计数值,却收到更大的值。这个包清楚表明中间少了东西,但它的密文可能已经建立在接收端没有经历过的密钥变化之上。知道序列断裂,不会自动给出正确的 RC4 状态。

RFC 3078 对有状态模式的处理没有留下猜测空间。接收端必须丢弃这个包,发送不带数据的 CCP Reset-Request,并静默丢弃后续包,直到收到 FLUSHED。发送端收到请求后重新初始化自己的 RC4 表,在下一包上设置 FLUSHED;接收端据此重建同一边界。协议不需要另一个 Reset-Ack,真正的回执是发送端行为已经改变。

这段流程把常被混为一谈的三件事拆开了。计数差证明本地历史不完整;FLUSHED 让双方重新拥有一致状态;应用是否补回缺失信息则要由更高层另行证明。

加密之前已经有多道门

RFC 3078 于 2001 年 3 月以 Informational RFC 发布,描述 Microsoft Point-to-Point Encryption 如何保护 PPP 封装数据,并更新了与 RFC 2118 的 MPPC 共用的选项空间。它没有成为 Internet Standard。

MPPE 通过 PPP Compression Control Protocol 的 18 号选项协商。发起方应报出支持的选择,响应方通常以一个选择作出 NAK:128、56 或 40 位,另有一个无状态模式位。如果根本没有尝试 MPPE 协商,默认就是不加密;如果开始协商却失败,规范建议终止链路。

发送 MPPE 数据以前,PPP 必须进入 Network-Layer Protocol 阶段,CCP 必须达到 Opened。身份认证成功并不等于 CCP 已打开;拿到会话密钥不等于选中了某种强度;报出 128 位能力不等于双方最终使用 128 位;策略中写着“要求加密”也不会让第一个用户包自动获得保护。

相关 RFC 有意把责任拆开。RFC 2548 说明 RADIUS 如何按方向携带 MPPE 密钥;RFC 3079 说明密钥如何从认证材料派生;RFC 1962 给出 CCP 的协商与复位框架;RFC 1661 定义 PPP 阶段。RFC 3078 处理的是这些步骤之后、密文包已经开始流动时的状态。任何单层日志都不能替代整条链。

十二位数字保存位置,不保存内容

一致性计数器每发送一包增加一,达到 4095 后回到零。接收端把观察值与本地预期比较,借此识别跳变、乱序,并决定需要推进多少次密钥状态。

这个数字不包含丢失包的副本,也没有证明跳变的原因。队列丢弃、物理损失、过滤、乱序或主动篡改都可能造成异常。即便计数连续,也只说明观察到的序列符合预期,不能证明每份明文都已进入应用。

RFC 3078 说 MPPE 不要求可靠链路,并指出为同步而加入 RFC 1663 的 PPP Reliable Transmission 往往只是增加开销。这不是“丢包无害”。它只表示密码状态机定义了如何越过损失;丢失的数据仍需上层协议承担。

并非所有 PPP 协议号都会经过 MPPE。加密数据位也只是影响处理的头字段,不是一份密码学证明。若审计只记录“看见 MPPE 包”,它还没有回答哪些流量应受保护、CCP 是否已打开、密文能否被对端解开。

无状态模式用计算跨过缺口

无状态模式在每次计数变化时改变会话密钥,通常就是每包一次。发送端先改密钥再加密;接收端读到计数后,先把密钥推进到相同位置再解密。每个加密包都必须带 FLUSHED。

若上次收到计数 2,这次收到 5,接收端就执行三次密钥变化,再尝试解开计数 5 的包。它无需向对端请求复位,也能追上发送端。但计数 3 和 4 的内容并没有因此回来。

因此,“无状态”并非没有共同状态。双方仍必须共享起始密钥、密钥演化函数、计数含义与所选模式。它只是让每个到达包携带足够的边界,使接收端可以本地重建当前位置。

有状态模式把反向路径纳入了解密

有状态模式只在计数低八位等于 0xFF 的标志包前改变密钥。若这个包丢失,发送端已经进入新密钥,接收端却停在旧密钥。后来到达的计数可以揭露偏差,却未必能提供可安全解密的内容。

Reset-Request 于是使反向控制路径成为正向数据恢复的一部分。只观察发送端到接收端的监控点也许能看到缺口和之后的 FLUSHED,却无法证明是哪一个接收端发出了哪次请求。只看到 Reset-Request 也不能证明发送端作出响应,更不能证明首个复位后明文正确。两个方向的时间线和第一份成功解密的上层数据都应进入回执。

规范还警告,有状态复位可能让两个包使用相同密钥,因此不应在容易丢包的环境中采用,例如 Internet 上的二层隧道。这个警告针对明确的状态转换,并不是对所有部署的统计,也不是一个具名故障报告。

最初的协商本身没有完整性保护

MPPE 协商不受完整性保护。主动攻击者可以更改 Supported Bits,影响看起来被选中的强度。严格的本地配置可以拒绝不允许的模式,缩小风险,却不会把原始协商变成已认证事实。

攻击者还可能修改一致性计数器,使双方失步;或反转加密数据位,造成拒绝服务。头字段是状态机输入,不是可信审计证书。系统必须同时保存线上的观察、双方本地接受的配置,以及真正的解密结果。

历史叙述也不能把“128 位”当成现代安全背书。RFC 3078 使用 RC4;RFC 4345 后来为 SSH 描述改进的 Arcfour 模式,那是另一种协议构造。后者提醒我们密钥长度不足以概括安全性,却不能证明 MPPE 已迁移、仍广泛部署或获得了后来的性质。

正确的回执是一连串状态转移

需要保存 PPP 会话与对端身份、认证结果、初始密钥来源和代次(不记录密钥本身)、CCP 请求/拒绝/确认、最终强度与模式,以及 CCP 进入 Opened 的时间。

对每个方向,还要保存配置代次、预期与观察到的计数、4095 回绕、缺口大小、FLUSHED 与加密数据位、0xFF 边界、丢弃的密文数、Reset-Request 的发送与接收、等待期间的丢弃、RC4 重新初始化结果,以及复位后第一包成功明文。之后还要继续记录上层序列、重传、确认与业务结果。

用 Lu Heng 的现实层方法看,认证、派生密钥、传递密钥、协商模式、收到包、发现丢失、恢复密码状态、得到明文和完成业务都是不同记录。运行中的代码必须逐一连接它们。计数器的价值正是诚实地只证明其中一个边界。

来源