Jump to content
RTFC

RdRand - calling Intel's on-chip RNG

Recommended Posts

RTFC

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

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
Earthshine

edited

Edited by Earthshine

My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
Tekk

Just wanted to point out that "rdrand_supported()" will return false when executed on AMD Zen processors even though rdrand is a supported instruction.  The dll author did not plan for the future.

Share this post


Link to post
Share on other sites
RTFC

@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
}

 

Share this post


Link to post
Share on other sites
Tekk

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.

  • Like 1

Share this post


Link to post
Share on other sites
RTFC
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

Share this post


Link to post
Share on other sites
Tekk

Right, but it was the check for support that I was questioning.  Sorry for being unclear.

Share this post


Link to post
Share on other sites
argumentum
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   =)

Share this post


Link to post
Share on other sites
Earthshine

but is the internal one as good a randomizer? probably not


My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
argumentum
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

Share this post


Link to post
Share on other sites
RTFC
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
  • Thanks 1

Share this post


Link to post
Share on other sites
Earthshine

yep. I like Intel's RdRand myself for truer randomness


My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
AndyG

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

Share this post


Link to post
Share on other sites
DennisChristensen

Hi

Thanks all.

Can i get a compiled dll somewhere?

Best regards

Dennis

Share this post


Link to post
Share on other sites
AndyG

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...

Share this post


Link to post
Share on other sites
DennisChristensen

Hi AndyG

Nothing I guess :-)

I just saw the DllCallAddress and assumed it needed a Dll.

I will soon try your code - thanks

Best regards

Dennis

Share this post


Link to post
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

×