Jump to content

RdRand - calling Intel's on-chip RNG


RTFC
 Share

Recommended Posts

RdRand is Intel's on-chip hardware random number generator using entropy. This is just a quick response to this query; a faster solution would be to integrate some inline assembly (maybe using fasm?) The dll source is written by Stephen Higgins (blog here); there are several alternative codes available on GitHub as well. Usage for encryption is controversial (there are rumours of an NSA backdoor), but it may provide the closest approach to TRNG using PRNG. This is the 32-bit unsigned version, producing an integer in range: 0 to (4GB-1).

Example:

; for dll source, see: https://github.com/viathefalcon/rdrand_msvc_2010

; NB only some Intel CPUs support this feature
$RdRandsupported=_RdRandInit()
MsgBox(0,"Is RdRand supported on this CPU?",$RdRandsupported)
if $RdRandsupported=False then Exit

Global $lowerbound=0    ; NB: negative values are returned as 4GB - value (unsigned int)
Global $upperbound=1000

ConsoleWrite("RdRand lower bound: " & $lowerbound & @CRLF)
ConsoleWrite("RdRand upper bound: " & $upperbound & @CRLF & @CRLF)

For $rc=1 To 100
    ConsoleWrite("RdRand: " & @TAB & _RdRand($lowerbound, $upperbound) & @CRLF)
Next

_RdRandCleanUp()

;##################################################################################

Func _RdRandInit()
    Global $DllHandleRdRand = DllOpen(".\RdRandlib.dll")
    If @error Then Return SetError(1,0,False)

    $result=DllCall($DllHandleRdRand, "bool", "rdrand_supported" )
    If @error Then Return SetError(1,0,False)

    Return ($result[0]=1)
EndFunc


Func _RdRandCleanUp()

    If IsDeclared($DllHandleRdRand) Then DllClose($DllHandleRdRand)
EndFunc


Func _RdRand($lower, $upper)

    $result=DllCall($DllHandleRdRand, "uint", "rdrand_uniform_ex", "uint", $lower, "uint", $upper )
    if @error then Return SetError(@error,@extended,False)

    Return $result[0]
EndFunc

 

RdRand.7z

Link to comment
Share on other sites

@Tekk: Thanks, but as the title clearly states, this function is explicitly for Genu-ineI-ntel chips only, see source:

// Queries CPUID to see if the RDRAND instruction is supported
bool rdrand_cpuid(void) {

	// Check if we are on Intel hardware
	int info[4] = { -1, -1, -1, -1 };
	__cpuid( info, 0 );
	if (packcmp( info[1], "Genu" ) != 0 ||
		packcmp( info[3], "ineI" ) != 0 ||
		packcmp( info[2], "ntel" ) != 0 ){
		return false;
	}

	// Query the feature itself
	__cpuid( info, 1 );
	return ((info[2] & RDRAND_MASK) == RDRAND_MASK); // info[2] == ecx
}

 

Link to comment
Share on other sites

I understand, I was only pointing it out.  As for the title clearly stating this, I would argue that AMD chips, at least the chips in question here, execute the Intel x86 instruction set.  AMD's Zen and newer processors will support this instruction.  The developer should have simply checked for the feature bit to be set and disregarded manufacturer.  I don't mean to argue, I just wanted to point out that it fails to work in a case where it should work.  I thank you for wrapping the library for use in AutoIt.

Link to comment
Share on other sites

9 minutes ago, Tekk said:

it fails to work

It does not, AFAICT. You can still call the RdRand() function itself, it's only the official support-query that returns False. Just call RdRandInit() first without evaluating the returned state and ignore the error, to enable access to the dll.

Edited by RTFC
Link to comment
Share on other sites

On 9/5/2018 at 10:10 AM, RTFC said:

This is the 32-bit unsigned version, producing an integer in range: 0 to (4GB-1).

Cool  =D

I made  run to compare to the build in function Random()

; NB only some Intel CPUs support this feature
$RdRandsupported=_RdRandInit()
MsgBox(0,"Is RdRand supported on this CPU?",$RdRandsupported)
if $RdRandsupported=False then Exit
Global $lowerbound=0    ; NB: negative values are returned as 4GB - value (unsigned int)
Global $upperbound=1000
ConsoleWrite("RdRand lower bound: " & $lowerbound & @CRLF)
ConsoleWrite("RdRand upper bound: " & $upperbound & @CRLF & @CRLF)
$t = TimerInit()
For $rc=1 To 1000
    ConsoleWrite("RdRand: " & @TAB & _RdRand($lowerbound, $upperbound) & @TAB)
Next
ConsoleWrite(@CRLF & TimerDiff($t) & @CRLF)
_RdRandCleanUp()
$t = TimerInit()
For $rc=1 To 1000
    ConsoleWrite("RdRand: " & @TAB & Random($lowerbound, $upperbound,1) & @TAB)
Next
ConsoleWrite(@CRLF & TimerDiff($t) & @CRLF)

and found the internal function runs 4 times faster ( on a i7-8700k, have not tried other CPUs )

Thanks for sharing   =)

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

57 minutes ago, Earthshine said:

but is the internal one as good a randomizer?

no clue. I use random for ... random stuff. Never used it for 4 GB data. I have no idea. But for a simple random ... call it seed ( and I know seed is a different thing ) variable, any random number at the time is good for me. And I don't care much for speed, 0.4 ms. or 4.0 ms. is, to me on my general usage, the same.

But there may be the notion that using a DLL call may be better than an internal function of the same functionality, hence my posting.  That is all I wanted to point out. :)

Edited by argumentum
spelling

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

2 hours ago, argumentum said:

internal function runs 4 times faster

Okay, let me quickly try to address the confusion (from a fidgety hotel WiFi in faraway lands, with a weird foreign keyboard, so I have to be brief).

RdRand is not about speed (I think a call typically takes 420-460 cycles), it's about approximating TRNG through DRNG, rather than PRNG. PRNG comprises all regular randomiser functions you're used to (such as AutoIt's Mersenne twister). Read Intel's doc here. PRNGs produce a very very very long sequence of totally predictable numbers that for a sufficiently small sample look (and can be used as it they were truly) random. RdRand is different because it uses the (truly unpredictable) entropy of on-chip hardware (3GHz thermal noise) to produce a bitstream. So in theory the sequence of values produced by RdRand never repeats itself. Now in truth there are caveats to this (e.g., the refresh cycle itself), but in theory, if you need true randomness, then DRNG should approximate this significantly better than PRNGs as produced by any and all possible software implementations. And I didn't use a dll for speed, it was just faster to implement that way than to debug a fasm implementation from scratch. All clear? ;)About to be automatically disconnected, so I better post this now...

Edited by RTFC
weird foreign characters
Link to comment
Share on other sites

Hi,

here a short script including the (i hope it is easy enough :o) ) complete developing of an AutoIt-Function with the help of AssembleIt from 32-Bit-Assembler-code (example from  here, I used only the necessary code )

Function to use with AutoIt:

Func _RdRand()
    $binarycode = "0xB8010000000FA20FBAE11EB800000000731AB90B00000083E90174100FC7F273F68B7C24048917B801000000C3" ;returns the assembled code
    $tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)
    $RdRandstruct = DllStructCreate("uint")
    $RdRandstruct_ptr = DllStructGetPtr($RdRandstruct)

    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $RdRandstruct_ptr) ;call the code
    If $ret[0] = 1 Then
        $RdRand = DllStructGetData($RdRandstruct, 1)
    Else
        $RdRand = "Your Processor is not able to execute the Rdrand-instruction!"
    EndIf
    Return $RdRand
EndFunc                                                          ;==>_RdRand


;call the function
$RdRand = _RdRand()
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

If you need the fastest RdRand-version possible (ie. in a loop) , it is recommended to declare all the needed variables at the begin of the script and call the DllCallAddress() in only one line.

$RdRand_binarycode = "0xB8010000000FA20FBAE11EB800000000731AB90B00000083E90174100FC7F273F68B7C24048917B801000000C3" ;returns the assembled code
$RdRand_CodeBuffer = DllStructCreate("byte[" & StringLen($RdRand_binarycode) / 2 - 1 & "]")                 ;reserve Memory for opcodes
DllStructSetData($RdRand_CodeBuffer, 1, $RdRand_binarycode)
$RdRand_CodeBuffer=DllStructGetPtr($RdRand_CodeBuffer)
$RdRandstruct = DllStructCreate("uint")
$RdRandstruct_ptr = DllStructGetPtr($RdRandstruct)


For $i = 1 To 100
    $ret = DllCallAddress("uint:cdecl",$RdRand_CodeBuffer , "ptr", $RdRandstruct_ptr) ;call the code
    consolewrite (DllStructGetData($RdRandstruct, 1) & @CRLF)
Next

 

Example of developing.....

AssembleIt64 is attached.

Remember that it is possible to develop also 64-Bit-Code (see example scripts)

#include <assembleit2_64.au3>

#AutoIt3Wrapper_UseX64=n


#cs _RdRand
    Use32                                                        ;32Bit!
    mov eax,1
    cpuid                                                        ;is RdRand-instruction available on your processor?
    bt ecx,30                                                    ;looking for the "yes, it is"-Bit
    mov eax,0                                                    ;exit code to AutoIt: failure
    jnc .exit                                                    ;if the Bit is not set, then exit

    ; rdrand sets CF=0 if no random number
    ; was available. Intel documentation
    ; recommends 10 retries in a tight loop
    mov ecx,11
    .loop1:
    sub ecx, 1
    jz .exit                                                     ;exit if ecx=0, 10 loops were executed
    rdrand edx                                                   ;store RdRand into Register
    jnc .loop1                                                   ;loop 10 times?

    mov edi,[esp+4]                                              ;Pointer from AutoIt structure
    _asmdbg_()                                                   ;Debugger, so you can see the what´s going on :o)
    mov [edi],edx                                                ;store Register into AutoIt structure
    mov eax,1                                                    ;exit code to AutoIt: success

    .exit:
    ret

#ce


;##########first stage##########
;developer Version :o) including the ability to debug the code
$RdRandstruct = DllStructCreate("uint")
$RdRandstruct_ptr = DllStructGetPtr($RdRandstruct)

$ret = _AssembleIt2("uint", "_RdRand", "ptr", $RdRandstruct_ptr)
If $ret = 1 Then
    $RdRand = DllStructGetData($RdRandstruct, 1)
Else
    $RdRand = "Your Processor is not able to execute the Rdrand-instruction!"
EndIf
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

;##########second stage##########
;code assembled by AssembleIt
$binarycode = _AssembleIt2("retbinary", "_RdRand")               ;returns the assembled code
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console


;##########third stage##########
;get the assembled code, write it into memory and call it
$binarycode = "0xB8010000000FA20FBAE11EB800000000731AB90B00000083E90174100FC7F273F68B7C24048917B801000000C3" ;returns the assembled code
$tCodeBuffer = _DllStructCreate64("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
DllStructSetData($tCodeBuffer, 1, $binarycode)

$ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $RdRandstruct_ptr) ;call the code
If $ret[0] = 1 Then
    $RdRand = DllStructGetData($RdRandstruct, 1)
Else
    $RdRand = "Your Processor is not able to execute the Rdrand-instruction!"
EndIf
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console


;##########final stage##########
;make an easy function (speed is not necessary)

Func _RdRand()
    $binarycode = "0xB8010000000FA20FBAE11EB800000000731AB90B00000083E90174100FC7F273F68B7C24048917B801000000C3" ;returns the assembled code
    $tCodeBuffer = _DllStructCreate64("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)

    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $RdRandstruct_ptr) ;call the code
    If $ret[0] = 1 Then
        $RdRand = DllStructGetData($RdRandstruct, 1)
    Else
        $RdRand = "Your Processor is not able to execute the Rdrand-instruction!"
    EndIf
    Return $RdRand
EndFunc                                                          ;==>_RdRand


;call the function
$RdRand = _RdRand()
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

 

assembleit2_64 121.zip

 

//EDIT

why not use eax to return RdRand directly from the asm-code and save some clockticks? :o)

Needs 2 functions then , one to check if RdRand is supported by your processor, an an other (supershort ) function which only returns the RdRand 

GO ON!

Spoiler
#include <assembleit2_64.au3>

#AutoIt3Wrapper_UseX64=n

#cs _RdRand_superfast
    Use32                                                        ;32Bit!
    rdrand eax                                                   ;store RdRand into Register
    ret
#ce

#cs _RdRand_slow
    Use32                                                        ;32Bit!
    mov ecx,11
    .loop1:
    sub ecx, 1
    jz .exit                                                     ;exit if ecx=0, 10 loops were executed
    rdrand eax                                                   ;store RdRand into Register
    jnc .loop1                                                   ;loop 10 times?
    .exit:
    ret
#ce

#cs _RdRand_available
    Use32                                                        ;32Bit!
    mov eax,1
    cpuid                                                        ;is RdRand-instruction available on your processor?
    bt ecx,30                                                    ;looking for the "yes, it is"-Bit
    mov eax,0                                                    ;exit code to AutoIt: failure
    jnc .exit                                                    ;if the Bit is not set, then exit
    mov eax,1                                                    ;exit code to AutoIt: success
    .exit:
    ret
#ce


$ret = _AssembleIt2("uint", "_RdRand_available")
If $ret = 1 Then
    $RdRand_available = "Your Processor supports the Rdrand-instruction!"
Else
    $RdRand_available = "Your Processor is not able to execute the Rdrand-instruction!"
EndIf
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand_available = ' & $RdRand_available & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console


$RdRand = _AssembleIt2("uint", "_RdRand_slow")
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

$RdRand = _AssembleIt2("uint", "_RdRand_superfast")
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

 

 

Edited by AndyG
Link to comment
Share on other sites

hi,

what is wrong with this function which needs no dll and calls the RdRand Processor opcode?

Func _RdRand()
    $binarycode = "0xB8010000000FA20FBAE11EB800000000731AB90B00000083E90174100FC7F273F68B7C24048917B801000000C3" ;returns the assembled code
    $tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)
    $RdRandstruct = DllStructCreate("uint")
    $RdRandstruct_ptr = DllStructGetPtr($RdRandstruct)

    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $RdRandstruct_ptr) ;call the code
    If $ret[0] = 1 Then
        $RdRand = DllStructGetData($RdRandstruct, 1)
    Else
        $RdRand = "Your Processor is not able to execute the Rdrand-instruction!"
    EndIf
    Return $RdRand
EndFunc                                                          ;==>_RdRand


;call the function
$RdRand = _RdRand()
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $RdRand = ' & $RdRand & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

 

oh, and btw. It is ver easy with FASM to compile a dll with the given assembler-code....i will show an example here soon...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...