如何在恒定时间内比较字符串?

时间:2017-06-22 06:06:19

标签: security rust

如何安全地比较两个有限长度的字符串,以便每次比较都需要相同的时间?不幸的是,Hashing有一个时间攻击漏洞。

有没有办法比较两个没有散列的字符串,不会受到时间攻击的影响?

3 个答案:

答案 0 :(得分:4)

TL; DR:使用汇编。

常量时间代码真的难以实现。要真正保持你需要的时间:

  • 恒定时间算法,
  • 所述算法的恒定时间实现。
  

"常数时间算法"意思?

字符串比较的例子很棒。大多数情况下,你希望比较尽可能少,这意味着在第一个差异时拯救:

fn simple_compare(a: &str, b: &str) -> bool {
    if a.len() != b.len() { return false; }

    for (a, b) in a.bytes().zip(b.bytes()) {
        if a != b { return false; }
    }

    true
}

然而,无论输入如何,恒定时间版本算法版本都应具有恒定时间:

  • 输入应始终具有相同的大小,
  • 无论差异位于何处(如果有),计算结果所用的时间应相同。

Lukas给出的算法几乎是正确的:

/// Prerequisite: a.len() == b.len()
fn ct_compare(a: &str, b: &str) -> bool {
    debug_assert!(a.len() == b.len());

    a.bytes().zip(b.bytes())
        .fold(0, |acc, (a, b)| acc | (a ^ b) ) == 0
}
  

"恒定时间实施"意思?

即使算法是恒定时间,实现也可能不是。

如果没有使用完全相同的CPU指令序列,那么在某些架构上,其中一条指令可能更快,而另一条指令则更慢,并且实现将丢失。

如果算法使用表查找,那么可能会有更多或更少的缓存未命中。

  

你能在Rust中写一个字符串比较的常量时间实现吗?

Rust语言可能适合该任务,但其工具链不是

  • LLVM优化器将对您的算法造成严重破坏,使其短路,消除不必要的读取,无论是现在还是将来,
  • LLVM后端将严重破坏您的实施,并采取不同的指示。

简而言之,今天,从Rust访问常量时间实现的唯一方法是在汇编中编写所述实现。

答案 1 :(得分:3)

在理论上,自己编写一个计时攻击安全字符串比较算法非常简单。在线有很多关于如何用其他语言进行操作的资源。重要的是欺骗优化器不以您不想要的方式优化代码。下面是一个示例Rust实现,它使用了here所述的算法:

fn ct_compare(a: &str, b: &str) -> bool {
    if a.len() != b.len() {
        return false;
    }

    a.bytes().zip(b.bytes())
        .fold(0, |acc, (a, b)| acc | (a ^ b) ) == 0
}

Playground

当然,这个算法可以很容易地推广到AsRef<[u8]>的所有内容。这留给读者一个练习; - )

看起来有一个箱子已经提供了这些比较:consistenttime。我还没有测试过,但文档看起来很不错。

答案 2 :(得分:1)

对于那些正在寻找提供此类实现的 crate 的人,您可以使用提供 rust-crypto 函数的 fixed_time_eq

该实现与 Lukas Kalbertodt 的实现非常相似。