如何使用汇编语言从英特尔处理器中获取随机数?

8

我需要从处理器(Intel Core i3)的随机生成器中获取随机数。我不想使用任何库,而是希望使用内嵌汇编在C++中实现,但我不知道应该使用哪些寄存器和指令。

2个回答

11
在支持的CPU上(目前只有Ivy Bridge和Haswell Intel CPU),调用RDRAND指令将随机值放入指定的寄存器中。例如,这将给您一个随机的64位值:
RDRAND %rax

成功时,进位标志将被设置。有关更多详细信息,请参阅英特尔的Bull Mountain软件实现指南。(“Bull Mountain”是英特尔硬件RNG的代号。)


您能进一步阐述它是PRNG还是熵生成器吗? - Mark B
点击链接。简而言之:这是一个真正的随机数生成器。 - Gunther Piez
https://www.felixcloutier.com/x86/rdrand 是汇编手册:可用于16位或32位操作数大小。在C++中,通常使用内置函数,例如 int success = _rdrand64_step(&output)while( !_rdrand64_step(&output) ){}。 (定义在<immintrin.h>中。RDRAND和RDSEED内置函数GCC和Intel C ++ - Peter Cordes

6

...但我不知道应该使用哪些寄存器和指令。

下面是我在Linux机器上使用GCC的内联汇编。我相信我从英特尔手册中抄袭了很大一部分。它可能是由David Johnston编写的,他对问题提供了令人惊叹的技术答案。他也是英特尔硬件设计的人。


int RDRAND_bytes(byte* buff, size_t bsize)
{
    if (!HasRDRAND())
        return -1;

    size_t idx = 0, rem = bsize;
    size_t safety = bsize / sizeof(unsigned int) + 4;

    unsigned int val;
    while (rem > 0 && safety > 0)
    {
        char rc;    
        __asm__ volatile(
                "rdrand %0 ; setc %1"
                : "=r" (val), "=qm" (rc)
        );

        // 1 = success, 0 = underflow
        if (rc)
        {
            size_t cnt = (rem < sizeof(val) ? rem : sizeof(val));
            memcpy(buff + idx, &val, cnt);

            rem -= cnt;
            idx += cnt;
        }
        else
        {
            safety--;
        }
    }

    // Wipe temp on exit
    *((volatile unsigned int*)&val) = 0;

    // 0 = success; non-0 = failure (possibly partial failure).
    return (int)(bsize - rem);
}

以下代码用于检测 HasRDRAND。它可以检测 AMD 和 Intel CPU(我认为这是提供 RDRAND 的所有 CPU)。

struct CPUIDinfo {
    unsigned int EAX;
    unsigned int EBX;
    unsigned int ECX;
    unsigned int EDX;
};

// Be careful below. EBX/RBX needs to be preserved depending on the memory model and use of PIC.
void cpuid_info(CPUIDinfo *info, const unsigned int func, const unsigned int subfunc) {
    __asm__ __volatile__ (
            "cpuid"
            : "=a"(info->EAX), "=b"(info->EBX), "=c"(info->ECX), "=d"(info->EDX)
            : "a"(func), "c"(subfunc)
    );
}

int HasAmdCpu() {
    CPUIDinfo info;
    cpuid_info(&info, 0, 0);
    if (memcmp((char *) (&info.EBX), "htuA", 4) == 0
            && memcmp((char *) (&info.EDX), "itne", 4) == 0
            && memcmp((char *) (&info.ECX), "DMAc", 4) == 0) {

        return 1;
    }


int HasIntelCpu() {
    CPUIDinfo info;
    cpuid_info(&info, 0, 0);
    if (memcmp((char *) (&info.EBX), "Genu", 4) == 0
            && memcmp((char *) (&info.EDX), "ineI", 4) == 0
            && memcmp((char *) (&info.ECX), "ntel", 4) == 0) {

        return 1;
    }

    return 0;
}

int HasRDRAND() {    
    if (!HasAmdCpu() || !HasIntelCpu())
        return 0;

    CPUIDinfo info;
    cpuid_info(&info, 1, 0);

    static const unsigned int RDRAND_FLAG = (1 << 30);
    if ((info.ECX & RDRAND_FLAG) == RDRAND_FLAG)
        return 1;

    return 0;
}

同时请参考如何使用RDRAND内嵌函数?。虽然它不能直接回答你的问题,但它可能是一个替代方案,并且可以帮助其他人。


3
起初这是我的工作,但是英特尔的低级软件专家们对此进行了清理和改进,他们在交叉编译器和交叉字宽兼容性方面付出了努力。 - David Johnston
有关 RDRAND 在 AMD 上的可用性,请参阅 AMD64 架构程序员手册第三卷:通用和系统指令 - jww

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接