Seite 1 von 1

QRandom - eigener Zufallszahlengenerator

Verfasst: 10.03.2013 13:11
von BlackFish
Hallo,
hier ein kleines Beispiel für einen Zufallszahlengenerator.
Ich habe vor allem deswegen programmiert, weil der Random()-Befehl von PureBasic viel zu unsicher ist und das Ergebnis reproduzierbar sein sollte(daher ist auch kein CryptRandom () möglich).
Der folgende Code sollte relativ sicher sein (zumindest habe ich mein Bestes geben), ich bin aber kein Kryptografie-Experte.
Evlt. kann ein Kryptografie-Experte hier im Forum den Code überprüfen und mir Feedback geben? Das wäre sehr nett.
Der Code darf frei verwendet werden.

Code: Alles auswählen

;================================================================================================================================
;SUBJECT: Generating random numbers with a string as seed
;         This should fill the gap between Random() and CryptRandom()
;AUTHOR:  codeprof aka BlackFish
;LICENSE: PUBLIC DOMAIN (can be used without any restriction)
;         This software is provided 'as-is', without any express or implied warranty.
;         In the author cannot be held liable for any damages arising from the use of this software.
;         Use this software at your own risk. 
;DATE:    2013-03-10
;================================================================================================================================

;Advantages:
; - String can be used as seed!
; - more secure than Random()
; - configurable whether it should be fast or more secure
; - supports Unicode (and returns the same values like in ANSI mode)
; - support for quad random numbers (up to 63 bits instead of 31 bits with Random())
; - works for Windows, Linux and MacOSX

;Disadvantages:
; - probably not as secure as CryptRandom()
; - not thread safe  ( as the "Threaded" keyword of PureBasic cannot be used inside procedures
;                      To make it thread safe move the "Static" variables otside of the procedure as
;                      global variables and use the "Threaded" keyword)



;================================================================================================================================
;Initializes the random number generator with an seed string.
;Parameters:
; seed                      : string with which the random number generator is initialized (max 128 chars)
; num_iterations [optional] : Number of times the result should be hashed with MD5
;                             The result should be at least hashed once for security reason (prevents known plaintext attacks...)
;                             If security is not important "num_iterations" can be set to 0 to improve performace.
;                             A high number will result in very slow number generation.
;                             This could be used to prevent timing attacks (make brute force attacks harder).
;================================================================================================================================
Procedure.q QRandomSeed(seed.s, num_iterations.i = 1)
  Static Dim key.q(1)
  Static Dim current.q(1)    
  Static Dim tmp.q(1)
  Static num_md5_iterations = 0
  Protected md5.s, i.i
  If seed <> ""
    ;Initalize random number generator
    Protected Dim seed_string.UNICODE(127 + 1)
    If Len(seed.s) > 128
      seed = Left(seed, 128)
    EndIf  
    PokeS(@seed_string(0), seed, 128, #PB_Unicode)    
    ;We use the MD5 of "seed.s" to generate a 128 bit key.
    md5.s = MD5Fingerprint(@seed_string(0), SizeOf(UNICODE) * 128)      
    key(0) = Val("$" + Left(md5, 16))
    key(1) = Val("$" + Right(md5, 16))   
    ;We use MD5(MD5(seed)) as initialisation value.
    ;The MD5 of the MD5 is used here so it is not possible to restore the key out of it (as reverting the MD5 is not possible).
    md5.s = MD5Fingerprint(@key(0), 16)      
    current(0) = Val("$" + Left(md5, 16))
    current(1) = Val("$" + Right(md5, 16))
    num_md5_iterations = num_iterations
  Else
    ;generate the next random number
    AESEncoder(@current(0), @tmp(0), 128 / 8, @key(0), 128, #Null, #PB_Cipher_ECB)
    CopyMemory(@tmp(0), @current(0), 128 / 8)     
    ;To prevent short cycles we increment the 128 bit key by 1.
    key(0) + 1
    If key(0) = 0:key(1) + 1:EndIf
    ;hash the result with MD5
    For i = 1 To num_md5_iterations
      md5.s = MD5Fingerprint(@tmp(0), 16)      
      tmp(0) = Val("$" + Left(md5, 16))
      tmp(1) = Val("$" + Right(md5, 16))      
    Next  
  EndIf  
  ProcedureReturn tmp(0) ! tmp(1)   
EndProcedure  


;================================================================================================================================
;Random number between "min" and "max" (including "min" and "max")
;The functions supports random numbers up to 63 bits.
; max                       : maximum number (must be greater than "min")
; min                       : minimum number (must be less than "max")
;================================================================================================================================
Procedure.q QRandom(max.q, min.q = 0)
  Protected val.q
  val.q = QRandomSeed("") & $7FFFFFFFFFFFFFFF
  val = val % ( max - min + 1)
  ProcedureReturn val + min
EndProcedure  





;Example

QRandomSeed("MY TOP SECRET MASTER PASSWORD", 100)
For j = 1 To 10
  passwd$ = ""
  For i = 0 To 30
    passwd$ + Chr(QRandom(126,33))
  Next   
  Debug "Password("+Str(j)+"): " + passwd$
Next  
/edit: kleine Verbesserung bzgl. Var. Deklaration. Danke an ts-soft

Re: QRandom - eigener Zufallszahlengenerator

Verfasst: 10.03.2013 14:10
von cxAlex
Also jetzt mal rein statistisch analysiert:

Code: Alles auswählen

#area = 1000
#samples = #area*1000
Define norm = #samples/#area
Dim results_qrand(#area)
Dim results_rand(#area)

Define i, x
For i = 1 To #samples
  x = QRandom(#area)
  results_qrand(x) + 1
  x = Random(#area)
  results_rand(x) + 1
Next
Define ew_rand.f, ew_qrand.f
For i = 0 To #area
  ew_rand + results_rand(i)
  ew_qrand + results_qrand(i)
Next
ew_rand/#area
ew_qrand/#area

Define ma_rand.f, ma_qrand.f
Define sa_rand.f, sa_qrand.f
For i = 0 To #area
  ma_rand + Abs(ew_rand - results_rand(i))
  ma_qrand + Abs(ew_qrand - results_qrand(i))
    sa_rand + Pow(Abs(ew_rand - results_rand(i)),2)
  sa_qrand + Pow(Abs(ew_qrand - results_qrand(i)),2)
Next
ma_rand/#area
ma_qrand/#area
sa_rand = Sqr(sa_rand/#area)
sa_qrand = Sqr(sa_qrand/#area)

 
MessageRequester("","Random() : " + #LF$ + "Erwartungswert: " + StrF(ew_rand, 2) + " ;Mittl. Abweichung: " + StrF(ma_rand, 2) + " ;Stand. Abweichung: " + StrF(sa_rand, 2) +
            #LF$ +  "QRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_qrand, 2) + " ;Mittl. Abweichung: " + StrF(ma_qrand, 2) + " ;Stand. Abweichung: " + StrF(sa_qrand, 2)) 
Beispiel für eine Bereich von 0-100 (#area = 100)

Code: Alles auswählen

Random() : 
Erwartungswert: 1000.00 ;Mittl. Abweichung: 25.34 ;Stand. Abweichung: 32.47
QRandom() : 
Erwartungswert: 1000.00 ;Mittl. Abweichung: 27.94 ;Stand. Abweichung: 35.09

Deine Implementierung und Random nehmen sich da nichts, mal ergibt Random() eine etwas bessere Verteilung, mal QRandom(), wobei der Unterschied minimal ist.

Gruß, Alex

//Edit: kleine Änderung aufgrund dieses Bugs (http://www.purebasic.fr/english/viewtop ... =4&t=53869) (und ja, das ist ein Bug in meinen Augen ... ) Ändert aber nichts gravierendes am Ergebniss ...

Re: QRandom - eigener Zufallszahlengenerator

Verfasst: 10.03.2013 16:17
von BlackFish
@cxAlex

Gute Idee. Ich habe dein Code mal etwas angepasst, da die falschen Werte herauskamen.

Code: Alles auswählen

QRandomSeed(Str(ElapsedMilliseconds()), 1)
OpenCryptRandom()

Define i, x
Define ew_rand.d, ew_qrand.d, var_rand.d, var_qrand.d, sa_rand.d, sa_qrand.d
#area = 1000
#samples = #area*1000

For i = 1 To #samples
  x = QRandom(#area)
  ew_rand + x
  var_rand + x * x
  x = CryptRandom(#area)
  ew_qrand + x
  var_qrand + x*x
Next

ew_rand/#samples
ew_qrand/#samples
var_rand/#samples
var_qrand/#samples

sa_rand = Sqr(var_rand - ew_rand  * ew_rand)
sa_qrand = Sqr(var_rand - ew_qrand * ew_qrand)

 
MessageRequester("","CryptRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_rand, 2) + " ;Stand. Abweichung: " + StrF(sa_rand, 2) +
            #LF$ +  "QRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_qrand, 2) + " ;Stand. Abweichung: " + StrF(sa_qrand, 2))

Re: QRandom - eigener Zufallszahlengenerator

Verfasst: 10.03.2013 16:36
von cxAlex
Hm? Nein, wir haben nur andere Herangehensweisen ... mein Code zeigt die Standardabweichung für jedes einzelne Ergebnis an, also für jedes Element 0-n. Jedes Dieser Elemente sollte idealverteilt gleich oft auftreten, daher ist in meinem Beispiel die Standardabweichung desto besser desto näher an 0.

Also Standardabweichung = Wurzel(Varianz); Varianz: (http://de.wikipedia.org/wiki/Stichprobe ... Berechnung) (korrigierte Stichprobenvarianz)

In deinem Fall bildest du die Abweichung für Über den gesamten Schnitt (Ist die formel Überhaupt korrekt, du Quadrierst den Schnitt und ziehst ihn von der Summe der Quadrate ab und bildest die Wurzel, kommt mir jetzt irgenwie komisch vor), das sind grundsätzlich andere Werte.

Gruß, Alex