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

基于时间的密码攻击

考虑下边的代码

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 <stdint.h>
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;

 
 
 
完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值