【问题标题】:Why do we need a constant time *single byte* comparison function?为什么我们需要一个恒定时间*单字节*比较函数?
【发布时间】:2013-08-21 19:35:53
【问题描述】:

查看 Go 标准库,有一个 ConstantTimeByteEq 函数如下所示:

func ConstantTimeByteEq(x, y uint8) int {
    z := ^(x ^ y)
    z &= z >> 4
    z &= z >> 2
    z &= z >> 1

    return int(z)
}

现在,我明白了对恒定时间字符串(数组等)比较的必要性,因为常规算法可能会在第一个不相等元素后短路。但是在这种情况下,两个固定大小的整数的常规比较不是已经是 CPU 级别的恒定时间操作了吗?

【问题讨论】:

标签: cryptography comparison go equality


【解决方案1】:

这点很可能避免branch mispredictions,除了将结果作为1或0而不是真或假(允许作为按位运算进行跟进)。

比较它的编译方式:

var a, b, c, d byte
_ =  a == b && c == d

=>

0017 (foo.go:15) MOVQ    $0,BX
0018 (foo.go:15) MOVQ    $0,DX
0019 (foo.go:15) MOVQ    $0,CX
0020 (foo.go:15) MOVQ    $0,AX
0021 (foo.go:16) JMP     ,24
0022 (foo.go:16) MOVQ    $1,AX
0023 (foo.go:16) JMP     ,30
0024 (foo.go:16) CMPB    BX,DX
0025 (foo.go:16) JNE     ,29
0026 (foo.go:16) CMPB    CX,AX
0027 (foo.go:16) JNE     ,29
0028 (foo.go:16) JMP     ,22
0029 (foo.go:16) MOVQ    $0,AX

有了这个:

var a, b, c, d byte
_ =  subtle.ConstantTimeByteEq(a, b) & subtle.ConstantTimeByteEq(c, d)

=>

0018 (foo.go:15) MOVQ    $0,DX
0019 (foo.go:15) MOVQ    $0,AX
0020 (foo.go:15) MOVQ    $0,DI
0021 (foo.go:15) MOVQ    $0,SI
0022 (foo.go:16) XORQ    AX,DX
0023 (foo.go:16) XORQ    $-1,DX
0024 (foo.go:16) MOVQ    DX,BX
0025 (foo.go:16) SHRB    $4,BX
0026 (foo.go:16) ANDQ    BX,DX
0027 (foo.go:16) MOVQ    DX,BX
0028 (foo.go:16) SHRB    $2,BX
0029 (foo.go:16) ANDQ    BX,DX
0030 (foo.go:16) MOVQ    DX,AX
0031 (foo.go:16) SHRB    $1,DX
0032 (foo.go:16) ANDQ    DX,AX
0033 (foo.go:16) MOVBQZX AX,DX
0034 (foo.go:16) MOVQ    DI,BX
0035 (foo.go:16) XORQ    SI,BX
0036 (foo.go:16) XORQ    $-1,BX
0037 (foo.go:16) MOVQ    BX,AX
0038 (foo.go:16) SHRB    $4,BX
0039 (foo.go:16) ANDQ    BX,AX
0040 (foo.go:16) MOVQ    AX,BX
0041 (foo.go:16) SHRB    $2,BX
0042 (foo.go:16) ANDQ    BX,AX
0043 (foo.go:16) MOVQ    AX,BX
0044 (foo.go:16) SHRB    $1,BX
0045 (foo.go:16) ANDQ    BX,AX
0046 (foo.go:16) MOVBQZX AX,BX

虽然后一个版本更长,但它也是线性的——没有分支。

【讨论】:

  • 这是一个“什么”,但 OP 问题的答案在哪里 - “为什么”?
  • “为什么”的答案是here这个函数试图使所有调用都花费相同的时间,而不管其输入的值如何。这样,攻击者就无法使用基于时间的攻击。
  • 不,问题实际上是“不是两个固定大小的整数的常规比较已经是CPU级别的恒定时间操作”,字面意思。
  • @GustavoNiemeyer:您的回答也没有回答这个问题。无论如何,@ tomwilde 是正确的,因为 OP 问题是 “为什么我们需要一个恒定时间*单字节*比较函数?”.
【解决方案2】:

不一定。而且很难说编译器在进行优化后会发出什么。对于高级“比较一个字节”,您最终可能会得到不同的机器代码。在侧通道中泄漏一点点可能会将您的加密从“基本牢不可破”更改为“希望不值得破解所需的钱”。

【讨论】:

    【解决方案3】:

    如果调用该函数的代码要根据结果立即分支,则使用恒定时间方法不会提供太多额外的安全性。另一方面,如果要在一堆不同的字节对上调用函数,保持结果的总和,并且只根据最终结果进行分支,那么外部窥探者可能能够确定是否最后一个分支被采用,但不知道之前的哪个字节比较是造成它的原因。

    话虽如此,我不确定我是否在大多数使用案例中看到了一大堆优势 解决了将方法的输出提炼为零或一的麻烦;只需保持notEqual = (A0 ^ B0); notEqual |= (A1 ^ B1); notEqual |= (A2 ^ B2); ... 的运行计数即可达到相同的效果并且速度要快得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-01
      • 2021-03-17
      • 2011-09-20
      • 2014-03-13
      • 2023-04-09
      相关资源
      最近更新 更多