1. 引言
时间攻击是侧信道攻击的一种,利用执行时间的变化来提取秘密信息。
与密码分析不同,密码分析旨在发现弱点并突破密码学协议的理论安全保证,而时间攻击则利用特定协议中的实现缺陷。虽然某些特定的密码学构造(如非对称加密)可能更容易受到时间攻击的影响,但这一攻击向量可以影响任何密码学实现。
为了减轻时间攻击,最佳实践是确保实现是常数时间的,即密码学函数的执行时间应保持不变,不论输入是什么。在实践中,应该确保代码路径和任何内存访问与秘密数据无关。 并非所有的时间差异都能被利用,但消除所有差异可以确保实现的安全性。为了确保实现是常数时间的,密码学从业者开发了各种工具来检测非常数时间代码。
本文分为两个部分:
- 第一部分为背景信息:介绍了时间攻击的基本概念和一个具体的示例
- 第二部分着重介绍了不同的工具,供从业者检查实现是否是常数时间的,并将其分类为四个不同的类别,每个类别都有其优点和限制。
2. 背景
密码学实现的时间攻击最早由Kocher在其1996年论文《Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems》 中提出。多年来,各种研究人员对这些攻击进行了扩展。特别是,Schindler 2000年论文《A Timing Attack against RSA with the Chinese Remainder Theorem》演示了针对RSA实现的攻击,该实现采用了特定的优化改进,2005年,Brumley和Boneh发布了《Remote Timing Attacks are Practical》,成功从OpenSSL中提取了私钥。同时,像AES这样的对称密码也可能受到时间攻击的影响,正如在2005年论文 《Cache-timing attacks on AES》中所示。
最近,后量子算法Kyber在其官方实现中发现了时间漏洞,称为KyberSlash(详情见2024年论文KyberSlash: Exploiting secret-dependent division timings in Kyber implementations)。CWE-385: Covert Timing Channel目录跟踪了在实现中发现的时间漏洞。
通常,要利用时间攻击,必须满足两个关键先决条件:
- 1)可以访问一个允许进行足够查询的oracle。
- 2)秘密数据与攻击者控制的数据之间存在时间依赖性。
所需的查询次数取决于时间泄漏的严重性。如果时间泄漏信号相对于所有其他指令或网络延迟引入的噪声较强,可能只需要少量的测量;否则,可能需要数百万次。时间依赖性是由于执行踪迹或指令时序的差异而产生的。
2.1 常见的常数时间违反模式
四种最常见的违反常数时间属性的模式,都是依赖于秘密数据的:
- 1)条件跳转:条件跳转会导致执行不同的指令,并通常导致四种模式中最显著的时间差异。使程序的执行流程依赖于秘密数据,将导致巨大的时间差异,具体取决于两个分支之间的差异有多大。
- 2)数组访问:依赖于秘密数据的数组访问和更一般的内存访问,可能会通过访问内存位置时的时间差异来提取索引值。这些时间差异主要源于缓存的使用,以及某个给定值是否位于缓存中。像AES这样的密码算法,它们使用依赖于秘密数据的替代表,即使在网络中,也很容易受到这种攻击,如2005年论文《Cache-timing attacks on AES》中所示。
- 3)整数除法(依赖于处理器):如果除数数量依赖于秘密数据,可能会泄露秘密。这些操作可能会根据使用的CPU架构或编译器泄漏秘密数据。
- 4)移位操作(依赖于处理器):如果移位的数量依赖于秘密数据,可能会泄露秘密。这些操作可能会根据使用的CPU架构或编译器泄漏秘密数据。
c
// 1. 条件跳转
if(secret == 1):
{
...
}
while(secret > 0)
{
...
}
// 2. 数组访问
lookup_table[secret];
// 3. 整数除法(依赖于处理器)
data = secret / m;
// 4. 移位操作(依赖于处理器)
data = a << secret;
在编写使用秘密数据进行操作的代码时,应考虑这四种模式,并尽量避免它们。
如果因为密码学算法的要求而无法避免这些模式,则应该采用掩码技术(见2013年论文《Masking against Side-Channel Attacks: A Formal Security Proof》),以消除或减少执行时间与秘密数据之间的相关性。
下一节将说明如何在模幂运算中利用条件跳转来实现时间攻击。
2.2 示例:模幂运算时间攻击
[Kocher 1996年论文《Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems》展示了用于计算模幂运算的算法容易受到时间攻击。像RSA和Diffie-Hellman这样的流行密码学系统都使用模幂运算,因此这种漏洞构成了一个重大的安全风险。如,在RSA中,解密涉及将密文 c t ct ct 提升到秘密指数 d d d,并对公共模数 N N N 取模:
c t d m o d N ct^{d} \mod{N} ctdmodN
如果攻击者可以在相同的秘密指数 d d d下,使用不同的密文值 c t ct ct查询解密函数,他们可能会根据计算所花费的时间推断出秘密指数 d d d值。由于模幂运算计算量大且广泛使用,优化此操作可以显著提高性能。这样的一种优化方法被称为二次幂运算 或从右到左的二进制方法 ,可将乘法次数减少为 log d \log d logd。
Input: base y , exponent d = { d n , ⋯ , d 0 } 2 , modulus N r = 1 for i = ∣ n ∣ downto 0 : if d i = = 1 : r = r ∗ y m o d N y = y ∗ y m o d N return r \begin{align} & \textbf{Input: } \text{base }y,\text{exponent } d=\{d_n,\cdots,d_0\}_2,\text{modulus } N \\ &r = 1 \\ &\textbf{for } i=|n| \text{ downto } 0: \\ &\quad\textbf{if } d_i == 1: \\ &\quad\quad r = r * y \mod{N} \\ &\quad y = y * y \mod{N} \\ &\textbf{return }r \end{align} Input: base y,exponent d={dn,⋯,d0}2,modulus Nr=1for i=∣n∣ downto 0:if di==1:r=r∗ymodNy=y∗ymodNreturn r
生成的代码会根据指数位 d i d_i di进行分支,违反了前面提到的条件跳转原则。
- 如果指数位为 d i = 1 d_i=1 di=1,则执行额外的乘法操作 r = r ∗ y r=r*y r=r∗y,导致更长的执行时间,从而泄漏出指数 d d d 中 1 1 1和 0 0 0的位数。
此外,一种常用的模乘技术叫做Montgomery蒙哥马利乘法 ,它不是常数时间的,并且根据模数和乘法结果执行额外的计算。如果乘法的中间值超过模数 N N N,则需要执行归约步骤。额外的归约步骤会导致可观察到的时间差异。
为了利用这些变化,攻击者可以构造两个输入 y y y 和 y ′ y' y′,使得:
y 2 < y 3 < N y ′ 2 < N ≤ y ′ 3 \begin{align*} y^2 < y^3 < N \\ y'^2 < N \leq y'^3 \end{align*} y2<y3<Ny′2<N≤y′3
- 没有归约步骤的模乘法可能需要 t 1 t_1 t1 时间,而需要归约步骤的则需要 t 2 t_2 t2 时间。
- 对于输入 y y y,两个蒙哥马利乘法都不需要归约步骤,因此执行时间为 t 1 + t 1 t_1+t_1 t1+t1。
- 对于输入 y ′ y' y′,第一个乘法 r = r ∗ r r=r*r r=r∗r 不需要归约,但第二个乘法 r = r ∗ y r=r*y r=r∗y 可能需要,导致额外的时间 t 2 t_2 t2。
Input: base y , exponent d = { d n , ⋯ , d 0 } 2 , modulus N r = 1 for i = ∣ n ∣ downto 0 : if d i = = 1 : r = r ∗ y m o d N { t 1 , if r ∗ y < N t 2 , if r ∗ y ≥ N y = y ∗ y m o d N { t 1 , if r ∗ r < N return r \begin{align} & \color{gray} \textbf{Input: } \text{base }y,\text{exponent } d=\{d_n,\cdots,d_0\}_2,\text{modulus } N \\ &\color{gray}r = 1 \\ &\color{gray}\textbf{for } i=|n| \text{ downto } 0: \\ &\color{gray}\quad\textbf{if } d_i == 1: \\ &\quad\quad r = r * y \mod{N} \begin{cases} t_1,& \text{if } r * y < N\\ t_2,& \text{if } r * y \geq N \end{cases} \\ &\quad y = y * y \mod{N} \begin{cases} t_1,& \text{if } r * r < N\end{cases} \\ &\color{gray}\textbf{return }r \end{align} Input: base y,exponent d={dn,⋯,d0}2,modulus Nr=1for i=∣n∣ downto 0:if di==1:r=r∗ymodN{t1,t2,if r∗y<Nif r∗y≥Ny=y∗ymodN{t1,if r∗r<Nreturn r
分支行为揭示了时间差异:
| 输入 | d i = 0 d_i=0 di=0 | d i = 1 d_i=1 di=1 |
|---|---|---|
| y y y | t 1 t_1 t1 | t 1 + t 1 t_1+t_1 t1+t1 |
| y ′ y' y′ | t 1 t_1 t1 | t 1 + t 2 t_1+t_2 t1+t2 |
通过分析这些时间差异,攻击者可以推断出特定位 d i d_i di 是否为0或1。虽然单次执行可能无法提供足够的信息,但结合多次测量和统计分析,可以帮助恢复私钥 d d d。
3. 常数时间工具
为了减轻时间攻击的风险,最佳实践是以常数时间的方式实现密码学算法,这意味着执行时间不受输入影响而保持一致。为了确保不存在这种时间差异,密码学领域已经创建了多种检测潜在时间差异的工具。这些工具通过它们所针对的编程语言(最常见的是C)和时间分析的基本方法进行区分。可以将这些不同的方法分为四个类别:
- 形式化常数时间工具
- 符号化常数时间工具
- 动态分析常数时间工具
- 统计分析常数时间工具
每种方法与其他方法相比,具有不同的优点和缺点。
更多信息可参看:
- 2024年论文《"These results must be false": A usability evaluation of constant-time analysis tools》
- 常数时间工具列表
3.1 形式化常数时间工具
形式化常数时间工具旨在数学上证明给定模型遵循指定的时间泄漏属性。为此,这些工具首先创建源代码或二进制文件的抽象表示,称为模型。接下来的步骤是指定该模型必须遵循的属性,通常通过注释应保持秘密的变量或内存区域。这些工具被分类为静态工具,因为它们不执行底层代码。
常见的形式化工具包括:
| 优点 | 缺点 |
|---|---|
| 保证: 形式化验证证明了在分析的模型下没有时间泄漏。 | 复杂性: 这些工具通常需要密码学和形式化方法方面的更多专业知识,因此不太容易为普通开发者所用。 |
| 灵活性: 许多工具使用LLVM字节码,可以与多种语言一起使用。 | 建模和限制: 形式化过程中做出的假设可能无法完全反映现实,导致验证不完整。如,编译阶段引入的任何更改都可能导致生成的二进制文件与分析的模型不同。 |
3.2 符号化常数时间工具
符号化常数时间工具使用符号执行来查找时间泄漏,通过分析不同执行路径和内存访问如何依赖于符号变量,尤其是秘密数据。符号执行还可以提供违反某一属性的具体值,这有助于理解潜在问题。大多数符号执行工具关注缓存时间攻击,使它们在涉及攻击者共享相同缓存的威胁模型中非常有用。
常见的符号化工具包括:
| 优点 | 缺点 |
|---|---|
| 反例: 符号执行可以提供具体的反例或测试用例,展示漏洞的存在,从而更容易理解和重现问题。 | 时间密集型: 符号执行会探索所有可能的路径,导致长时间的执行。 |
3.3 动态常数时间工具
动态常数时间工具与形式化常数时间工具一起,是确保常数时间执行最常用的方法之一。这些方法通常涉及标记特定内存区域为敏感,确保它们不会泄漏时间信息,或者跟踪执行流以检测不同输入的执行路径差异。
常见的动态工具包括:
| 优点 | 缺点 |
|---|---|
| 细粒度分析: 允许指定敏感内存区域,使其在目标分析中非常有效。 | 覆盖范围有限: 只能跟踪测试期间遇到的执行路径,可能遗漏一些漏洞。 |
| 灵活性: 通过注释不同的代码部分,可以适应各种上下文。 |
3.4 统计常数时间工具
统计常数时间工具通过执行程序并测量不同输入的耗时来分析程序。如果所有输入的时间测量结果一致,则表明没有时间漏洞。由于这些工具测量的是实际执行的代码,因此它们接近实际实现并考虑潜在的变量,使其成为检测潜在时间问题的初步检查工具。
常见的统计工具包括:
| 优点 | 缺点 |
|---|---|
| 设置简单: 设置统计测试相对简单,在某些情况下可以在不需要访问源代码的情况下完成。 | 调试困难: 当检测到时间差异时,统计工具通常不会提供关于问题原因或代码中位置的信息。 |
| 实用性: 统计工具测量实际的实时泄漏,提供包括所有潜在变量(如编译器优化、架构差异)的现实结果。 | 噪声: 记录的测量质量仅与测试环境的精度相关。如果代码的其他部分耗时显著,可能会导致时间漏洞未被发现。 |