密码攻击——无分支的代码,执行时间是常量

密码攻击——无分支的代码,执行时间是常量,第1张

基于时间的密码攻击

考虑下边的代码

int memcmp(const void *s1, const void *s2, size_t n) {
    if (n != 0) {
        const unsigned char *p1 = s1, *p2 = s2;
        do {
            if (*p1++ != *p2++)
                return ((*--p1) - (*--p2));
        } while (--n != 0);
    }
    return (0);
}

如果这是进行密码验证的程序,那么黑客可以根据返回时间的不同,判断出输入的密码是在哪一位开始出错的。

因此在密码领域,引出了常量时间编程。

常量时间编程——绝对值

取绝对值,直觉上,正数是其本身,负数是自身减1再取反。

是需要if进行判断的。


这样程序执行的时间,就不是一个常量了。


引出常量时间的编码:

#include 
int32_t abs(int32_t x) {
    int32_t mask = (x >> 31);
    return (x + mask) ^ mask;
}

注:
C语言定义,无符号数右移,是逻辑右移。

有符号数右移,是算术右移。


左移的话,有没有符号的数,都一样。

当有符号数x是正数时,
mask = 0,(x + 0)^ 0 = x
当有符号数x是负数时,
mask = 0xFFFF_FFFF,即是 -1
而且和 0xFFFF_FFFF 进行异或 *** 作,就是在取反。


(x - 1)^ 0xFFFF_FFFF 就是进行了减1再取反的 *** 作。

注:
C语言取绝对值当中,有一个未定义的情况。


abs(INT_MIN) 的值是未定义的,由编译器厂商进行安排。


INT_MIN = 0x8000_0000 = -2147483648
这个值带入到上述代码,得到的值还是 -2147483648,这是错的。

取绝对值的代码再来一个:

int32_t abs(int32_t x) {
    int32_t mask = (x >> 31);
    return (x ^ mask) - mask;
}

正数就不说了,因为mask=0。


对于负数,乍一看,是先取反,再加一。

怎么又搞了一次取反加一啊,啥?这样再搞一次就取到负数对应的绝对值了???
证明如下:
x + x逆 = 0
x + x反 = 0xFFFF_FFFF
上述代码结果为 abs(x) = x反 + 1
两边同时加上x
x + abs(x) = x + x反 + 1 = 0xFFFF_FFFF + 1 = 0(最高位的1会溢出)
得到
当x是负数时,abs(x) = -x
这是成立的。

这个代码改为x86汇编时,将变成:

cdq
xor eax, edx
sub eax, edx

cdq是将eax的最高位填充到edx当中。

eax是正数时,edx是0。

负数时,edx填充全1.
然后运算 eax = eax ^ edx
只考虑负数情况,此时eax等于自身取反了。


再看 eax = eax - edx
edx是-1,此时eax又加了一个1
因此这个汇编代码,就是上边的C语言代码。

再来两个执行时间是常量的例子,自行思考。

取出较小的数
int32_t min(int32_t a, int32_t b) {
    int32_t diff = (a - b);
    return b + (diff & (diff >> 31));
}


如果a大于b,则x+=c
x += ((b - a) >> 31) & c;

 
 
 
完。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/674665.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存