RandomRDRand.st
author Stefan Vogel <sv@exept.de>
Fri, 03 Apr 2020 18:50:48 +0200
changeset 5471 0bee7a5e40e8
parent 5080 aeb43e5edb85
permissions -rw-r--r--
#TUNING by stefan class: DirectoryContents changed: #directory: remove duplicate xecpetionCreator in SignalSet

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 2014 Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic2' }"

"{ NameSpace: Smalltalk }"

Object subclass:#RandomRDRand
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'Magnitude-Numbers-Random'
!

!RandomRDRand class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2014 Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    This generator uses the rdgen random generator which is built into modern intel chips.
    Before using, you should check via the isSupported query.

    Warning:
        there have been discussions about the security of the intel rdgen instruction
        and whether there are NSA backdoors built into it.
        Linus Torwalds refuses to use it for /dev/urandom in the linux kernel, for that very reason.
        Be sure you know what you are doing, if you use this generator for sensitive cryptographic stuff.
        We recommend using one of the libcrypt-based generators and use this only to get additional
        entropy for the seed.

    Warning2:
        the initial release of the AMD Ryzen3000 (without BIOS update) has a bug in returning the same
        (all 1s) random number every time.
        The code here checks for that an raises an error in the constructor if that bug is detected.
        Be sure to catch this exception in your code and fall back to another random generator if raised.
        
    NO WARRANTY

    [usage:]
        RandomRDRand new nextInteger

    [see also:]
        RandomGenerator - the default; uses the machine's /dev/random if available
        Random  - fast, but generates less quality random numbers
        RandomTT800 - another random generator
        RandomParkMiller - another random generator
        RandomMT19937 - another random generator
        RandomKISS - another random generator
        exept:libcrypt - a library containing more stuff based on hashes and cyphers

    [author:]
        Claus Gittinger.
"
! !

!RandomRDRand class methodsFor:'instance creation'!

new
    self isSupported ifFalse:[ self error:'this generator needs a cpu with rdgen instruction' ].
    ^ self basicNew initialize

    "
     self new nextInteger
    "
!

new:seed
    "seed is actually ignored"

    self isSupported ifFalse:[ self error:'this generator needs a cpu with rdgen instruction' ].
    ^ self basicNew initialize; seed:seed
! !

!RandomRDRand class methodsFor:'queries'!

isSupported
    "true if this architecture supports hardware random numbers"

    ^ (OperatingSystem getSystemInfo at:#extendedInstructions ifAbsent:#())
              includes:#rdmd

    "
     self isSupported
    "

    "Modified: / 28-03-2019 / 10:59:32 / Stefan Vogel"
! !

!RandomRDRand methodsFor:'initialization'!

initialize
    "check for Ryzen RDRAND bug (returning the same all-ones everytime)"

    |i1 i2|

    i1 := self nextInteger.
    i2 := self nextInteger.
    (i1 = i2) ifTrue:[
        self error:'Ryzen RDRand bug detected'
    ].

    "
     self new
    "

    "Modified (comment): / 25-07-2019 / 13:30:28 / Claus Gittinger"
!

seed:seed
    "/ ignored
! !

!RandomRDRand methodsFor:'random numbers'!

nextBoolean
    "generates a boolean random"

    ^ self nextInteger bitTest:1

    "
        |bag rng|

        rng := self new.
        bag := Bag new.
        100000 timesRepeat:[
            bag add:rng nextBoolean.
        ].
        bag
    "
!

nextInteger
    "generates the next integer in 0..MAXINT.
     Notice, it may raise an illegal instruction exception on some cpu chips,
     even though the cpuid instruction says that it is available"

    |cfStillSet|

%{
    unsigned INT r = 0;
    unsigned char cf;
    int count = 50;

    do {
#ifdef USE_DRAND64
        cf = _rdrand64_step(&r);
#elif defined(USE_DRAND32)
        cf = _rdrand32_step(&r);
#elif defined(__i386__) && defined(__GNUC__) && (__GNUC__ >= 2)
        // Encoding of rdrand %eax
        asm(".byte 0x0F, 0xC7, 0xF0; setc %1"
            : "=a" (r), "=r" (cf)
            : "0" (r), "1" (cf)
            : "cc");

#elif defined(__x86_64__) && defined(__GNUC__) && (__GNUC__ >= 2)
        // Encoding of rdrand %rax
        asm(".byte 0x48, 0x0F, 0xC7, 0xF0; setc %1"
            : "=a" (r), "=r" (cf)
            : "0" (r), "1" (cf)
            : "cc");

#else
        goto unsupported;
#endif
    } while ((cf == 0) && (--count > 0));
    if (cf != 0) {
        RETURN (__MKUINT(r));
    }
    cfStillSet = true;
unsupported: ;
%}.
    cfStillSet == true ifTrue:[
        self primitiveFailed:'carry flag not set after 50 tries'
    ].
    self primitiveFailed:'unsupported on this architecture'

    "
     self new nextInteger
    "

    "Modified: / 28-03-2019 / 10:46:29 / Stefan Vogel"
! !

!RandomRDRand class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !