stillson / rdrand

python interface to intel hardware RNG
Other
26 stars 10 forks source link

Adding RdSeed #5

Closed dj-on-github closed 6 years ago

dj-on-github commented 7 years ago

I am the DRNG architect at Intel. I've tried to add RdSeed capability.

My problem is that the CPUID routine in rdrand.c doesn't seem to work for leaf 7, which is necessary to get the RdSeed capability bit. EBX is on leaf 7 is being returned as 0x00000000.

My guess is that while the leaf field is being set, the subfunc field is not, in the following define for 64 bit builds. # define __cpuid(x,y) asm("cpuid":"=a"(x[0]),"=b"(x[1]),"=c"(x[2]),"=d"(x[3]):"a"(y))

I think this because the decision code is invoking cpuid twice and there's nothing clearing the subfunc in ECX to 0 in between. For the extended featureset bits, CPUID is called with EAX=7 and ECX=0.

int
RdSeed_cpuid(void)
{
    unsigned int info[4] = {-1, -1, -1, -1};

    /* Are we on an Intel or AMD processor? */
    cpuid(0, info);

    if (!(( memcmp((void *) &info[1], (void *) "Genu", 4) == 0 &&
        memcmp((void *) &info[3], (void *) "ineI", 4) == 0 &&
        memcmp((void *) &info[2], (void *) "ntel", 4) == 0 )
        ||
        ( memcmp((void *) &info[1], (void *) "Auth", 4) == 0 &&
        memcmp((void *) &info[3], (void *) "enti", 4) == 0 &&
        memcmp((void *) &info[2], (void *) "cAMD", 4) == 0 )))
        return 0;

    /* Do we have RDSEED? */
    cpuid(7, info);
    int ebx = info[1];  /* Always comes back as 0 */
    if ((ebx & RDSEED_MASK) == RDSEED_MASK)
        return 1;
    else
        return info[1];
}

I hacked in my usual CPUID code which takes both parameters and works fine.

void get_cpuid_linux(unsigned int *info, const unsigned int func, const unsigned int subfunc)
{
asm(".intel_syntax noprefix;\n\
mov r8, rdi;\n\
mov r9, rsi;\n\
mov r10, rdx;\n\
push rax;\n\
push rbx;\n\
push rcx;\n\
push rdx;\n\
mov eax, r9d;\n\
mov ecx, r10d;\n\
cpuid;\n\
mov DWORD PTR [r8], eax;\n\
mov DWORD PTR [r8+4], ebx;\n\
mov DWORD PTR [r8+8], ecx;\n\
mov DWORD PTR [r8+12], edx;\n\
pop rdx;\n\
pop rcx;\n\
pop rbx;\n\
pop rax;\n\
.att_syntax prefix\n");
}

However this is Linux/macos (GCC and Clang) only. The minGW GCC on windows variant in my programs is:

void get_cpuid_windows(int leaf, unsigned int *info) {
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;

asm("\n\
    mov %4, %%eax\n\
    cpuid\n\
    mov %%eax,%0\n\
    mov %%ebx,%1\n\
    mov %%ecx,%2\n\
    mov %%edx,%3":"=r"(a),"=r"(b),"=r"(c),"=r"(d):"r"(leaf):"%eax","%ebx","%ecx","%edx");
    info[0] = a;
    info[1] = b;
    info[2] = c;
    info[3] = d;
}

So.. Not wanting to hack up the code too much I tried this, which seemed to work, but I don't have the means immediately available to test on Windows or 32 bit systems.

In rdrand.c I tried changing this # define __cpuid(x,y) asm("cpuid":"=a"(x[0]),"=b"(x[1]),"=c"(x[2]),"=d"(x[3]):"a"(y))

to this # define __cpuid(x,y,s) asm("cpuid":"=a"(x[0]),"=b"(x[1]),"=c"(x[2]),"=d"(x[3]):"a"(y),"c"(s))

and this __cpuid(reg, op); to this __cpuid(reg, op, 0);

And it worked fine on my Linux 64 bit build.

For 32 bit, I speculatively changed the 32 bit CPUID code by adding subfunc like this.

void
cpuid(unsigned int op, unsigned int subfunc, unsigned int reg[4])
{

#if USING_GCC && IS64BIT
    __cpuid(reg, op, subfunc);
#else
    asm volatile("pushl %%ebx      \n\t" /* save %ebx */
                 "cpuid            \n\t"
                 "movl %%ebx, %1   \n\t" /* save what cpuid just put in %ebx */
                 "popl %%ebx       \n\t" /* restore the old %ebx */
                 : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
                 : "a"(op), "c"(subfunc)
                 : "cc");
#endif
}

and fixed up all the calls to cpuid to add the subfunc as 0 and it worked..

Python 2.7.10 (default, Sep 24 2015, 17:50:09)
[GCC 5.1.1 20150618 (Red Hat 5.1.1-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from rdrand import RdRandom
>>> from rdseed import RdSeedom
>>> r = RdRandom()
>>> s = RdSeedom()
>>> s.getrandbits(256)
56996184683593646573755105708587638525003637942488626051424656260238268697195L
>>> r.getrandbits(256)
11692978054180914393575981539136110327427631718494460059352733267822546623322L
>>> quit()

I'd like to put in a push request for the other stuff I've added (AMD support, RdSeed) but I'm reluctant to do that until the CPUID problem is fixed in the codebase and tested on Windows and 32 bits systems.

stillson commented 6 years ago

Done!