为什么我们需要一个恒定时间*单字节*比较函数?

jus*_*nas 14 comparison cryptography equality go

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

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

    return int(z)
}
Run Code Online (Sandbox Code Playgroud)

现在,我理解需要恒定时间(数组等)比较,因为常规算法可能会在第一个不等元素之后短路.但在这种情况下,是不是定期比较两个固定大小的整数已经在CPU级别进行恒定时间操作?

Gus*_*yer 11

除了将结果设置为1或0而不是真或假(允许后续按位操作)之外,该点可能会避免分支错误预测.

比较这个编译的方式:

var a, b, c, d byte
_ =  a == b && c == d
Run Code Online (Sandbox Code Playgroud)

=>

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
Run Code Online (Sandbox Code Playgroud)

有了这个:

var a, b, c, d byte
_ =  subtle.ConstantTimeByteEq(a, b) & subtle.ConstantTimeByteEq(c, d)
Run Code Online (Sandbox Code Playgroud)

=>

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
Run Code Online (Sandbox Code Playgroud)

虽然后一版本较长,但它也是线性的 - 没有分支.


Vol*_*ker 6

不必要.在进行优化后,很难说出编译器会发出什么.您最终可能会得到高级"比较一个字节"的不同机器代码.在侧面通道中泄漏一点点可能会使您的加密从"基本不可破解"变为"希望不值得破解所需的资金".