SHA512 to generate random numbers in PHP

2019-05-12 16:50发布

I'm filling an array with random numbers using $blockhash[$i] = rand().time().rand()

Then, for each random number in that array I calculate the correspondent SHA512

$SecretKey = "60674ccb549f1988439774adb82ff187e63a2dfd403a0dee852e4e4eab75a0b3";
$sha = hash_hmac('sha512', $value, $SecretKey);

Split it:

$pool  = str_split($sha, 2);

Then I get the first number from the $pool array, convert hex to dec and limit it within 1 and 50:

$dec = hexdec($pool[0]) % 50 + 1;

The problem is that the numbers are not that random and I don't know why. I'm counting the frequency for each number from 1 to 50 but the numbers 1,2,3,4,5 and 6 are coming up often than the others. See graph

enter image description here

Why is this happening and how to fix it? Thanks!

3条回答
家丑人穷心不美
2楼-- · 2019-05-12 17:13

Neither rand() or mt_rand() generate truly random values.

As the manual states:

This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using openssl_random_pseudo_bytes() instead.

See Better Random Generating PHP for an StackOverflow question that points the same issue and has some good answers.

查看更多
老娘就宠你
3楼-- · 2019-05-12 17:14

the 2 hex characters you are converting to decimal will be in the range of 0-255. you mod that by 50 and add 1 making 1-6 (range(0-5)+1) occur 6 times over 1-256 while every other number occurs only 5 times. This would account for a ~20% increase in those numbers coming up.

查看更多
干净又极端
4楼-- · 2019-05-12 17:29

You get 1-6 more often because you fetch two hexadecimal digits from the hash. That's one byte, so it can store values from 0 to 255. Then you use modulo 50. In result you get ranges 0-49, 50-99, 100-149, 150-199, 200-249 and... 250-255. This last one is responsible for extra prevalence of 1-6 in your results.

Solution: just use mt_rand(1,50);

[edit]

If you really need to convert a number from 0-255 range to 1-50 range, the solution would be scaling and rounding.

$number = round(($byteValue)/(255/49))+1; 
查看更多
登录 后发表回答