2009-08-21 11 views
15

Potrzebuję zamiennik dla funkcji PHP rand(), który używa silnego kryptografu generator liczb losowych.Zamień Rand() z openssl_random_pseudo_bytes()

Funkcja openssl_random_pseudo_bytes() zapewnia dostęp do silnego generatora liczb losowych, ale wysyła dane w postaci ciągu bajtów. Zamiast tego potrzebuję liczby całkowitej od 0 do X.

Wyobrażam sobie, że kluczem jest uzyskanie wyniku z openssl_random_pseudo_bytes() na liczbę całkowitą, a następnie można wykonać dowolną matematykę. Mogę wymyślić kilka "brutalnych" sposobów konwersji z ciągu bajtowego na liczbę całkowitą, ale miałem nadzieję na coś ... eleganckiego.

+0

mt_rand() dadzą dużo lepszej jakości niż liczb losowych rand(), ale nie jest kryptograficznie silny albo. – David

+0

uważaj, ta odpowiedź jest błędna. dokładniej część "% $ range" jest niepoprawna. Podejmij następujący scenariusz: chcesz generować liczby od 1 (włącznie) do 4 (z wyłączeniem) (ponieważ robisz max-min). $ range = 3. Teraz 3 nie dzieli się równomiernie na 2^8, co oznacza, że ​​1 będzie bardziej powszechne niż 3. Bądź bardzo ostrożny, chciałeś stworzyć bezpieczny generator liczb losowych, ale nieumyślnie uczynił go niepewnym, dlatego zaleca się korzystanie z istniejących wdrożeń. – chacham15

+0

@ chacham15: Jeśli rozumiesz poprawnie, w 8-bitowym zasięgu dostaniesz 85 zestawów [1,2,3] plus pojedynczy [1,] pozostały, co spowoduje, że "1" o 1,17% będzie bardziej prawdopodobne niż " 2 "lub" 3 ", prawda? Jak rozwiązać ten problem? Oczywiście użycie * istniejącej implementacji * nie jest opcją, inaczej nie zadałbym pytania. – tylerl

Odpowiedz

11

pomocą dostarczonego sugestie, Utworzyłem spadek -w zamian za rand() za pomocą OpenSSL. Włączę to tutaj dla potomności.

Opcja $ pedantic daje wyniki bez uprzedzenia, zaczynając od początku, gdy wyniki nie będą równomiernie rozłożone w możliwym zakresie.

function crypto_rand($min,$max,$pedantic=True) { 
    $diff = $max - $min; 
    if ($diff <= 0) return $min; // not so random... 
    $range = $diff + 1; // because $max is inclusive 
    $bits = ceil(log(($range),2)); 
    $bytes = ceil($bits/8.0); 
    $bits_max = 1 << $bits; 
    // e.g. if $range = 3000 (bin: 101110111000) 
    // +--------+--------+ 
    // |....1011|10111000| 
    // +--------+--------+ 
    // bits=12, bytes=2, bits_max=2^12=4096 
    $num = 0; 
    do { 
     $num = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))) % $bits_max; 
     if ($num >= $range) { 
      if ($pedantic) continue; // start over instead of accepting bias 
      // else 
      $num = $num % $range; // to hell with security 
     } 
     break; 
    } while (True); // because goto attracts velociraptors 
    return $num + $min; 
} 
+0

Dodałem odpowiedź z przewodnikiem, w jaki sposób można użyć CryptoLib, aby zrobić to jako kroplę zastępczą. Jest to o wiele bezpieczniejsze (i ma bardziej wydajny kod) i zapewnia również funkcję sprawdzania powtarzalności. – mjsa

+0

@mjsa Chociaż jestem pewien, że twoja biblioteka kryptograficzna jest świetna, to pytanie dotyczyło samej mechaniki kodu, a nie prośby o zbudowanie innej biblioteki, aby to zrobić. – tylerl

+0

To nie jest złe rozwiązanie. Pomagam przenieść '' random_int() 'PHP 7 do projektów PHP 5, a nasze podejście działa nawet gdy' $ max - $ min> PHP_INT_MAX'. Możesz znaleźć nasze wysiłki na Githubie pod [random_compat] (https://github.com/paragonie/random_compat), jeśli chcesz rozważyć swoją własną perspektywę. –

8

Strona podręcznika dla openssl_random_pseudo_bytes() ma przykład, który myślę, że chcesz. Możesz po prostu zadzwonić pod numer bin2hex() na wyjściu openssl_random_pseudo_bytes(), aby przekonwertować na liczbę szesnastkową, a następnie hexdec() na tę wartość, aby przekonwertować na wartość dziesiętną.

$rand_num = hexdec(bin2hex(openssl_random_pseudo_bytes($length, $strong))); 

W tym momencie możesz zrobić dowolną matematykę, aby uzyskać wartość w wymaganym zakresie. Inną (oszust) opcją, którą możesz mieć, jest uruchomienie polecenia systemowego do wygenerowania liczby losowej - istnieje kilka dobrych opcji dla generatorów liczb losowych dla różnych systemów operacyjnych dostępnych online.

+0

To nie w pełni odpowiada na pytanie, zobacz inne odpowiedzi poniżej. – Andrew

1

cóż, po prostu użyj hexdec na wyniku openssl_random_pseudo_bytes, a otrzymasz swoją liczbę całkowitą. To jest tak elegancki jak to robi :)

print hexdec('45261b8f'); 

> 1160125327 
+0

Nie odpowiada na pytanie dotyczące ops, ponieważ rand zajmuje min i max, a to po prostu zwraca liczbę losową. – nate

+0

Nie bezpośrednio, ale dodawanie tego jest całkiem banalne - jak widać z rozwiązania dostarczonego przez samego op. –

2

Herezje wersja z powyższych rozwiązań, które nie korzysta z funkcji rekurencyjnej wzywa:

function secure_rand($min,$max) { 
    $range = $max - $min + 1; 
    if ($range == 0) return $min; 
    $length = (int) (log($range,2)/8) + 1; 
    $max = pow(2, 8 * $length); 
    $num = $max + 1; // Hackish, I know.. 
    while ($num > $max) { 
     $num = hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s))); 
    } 
    return ($num % $range) + $min; 
} 
-1
function ($min,$max){ 
    $range = $max-$min+1; 
    do{ 
     $result = floor($range*(hexdec(bin2hex(openssl_random_pseudo_bytes(4)))/0xffffffff)); 
    } while($result == $range);  
    return $result + $min; 
} 
0

Najprostszym sposobem, aby to zrobić (i najbezpieczniejszym ze wszystkich tutaj opcji) jest użycie CryptoLib który posiada funkcję randomInt który zapewnia zamiennik dla rand.

Najpierw pobierz CryptoLib od i trzymać go w swoim projekcie: https://github.com/IcyApril/CryptoLib

Dwa, spadek w tym kodzie. zastąpić ścieżka/do/z katalogu cryptolib.php min i max ze swoim minimalnej i maksymalnej liczby:

<?php 
    require_once('path/to/cryptoLib.php'); 

    $min = 1; 
    $max = 5; 

    $randomNum = CryptoLib::randomInt($min, $max); 
?> 

CryptoLib pełna dokumentacja jest pod adresem: https://cryptolib.ju.je/

2

Ponieważ PHP 7 jest poza pory Najłatwiejszym sposobem rozwiązania tego problemu jest zastąpienie wszystkich wystąpień mt_rand za pomocą random_int.

(Zakładając, że zmodernizowane, że jest).