How to get cryptographically strong integer from 0-X in PHP?

I want to generate random alphanumeric strings in PHP. They will be used in places where the power of random numbers is important (public identifiers in URLs, etc.).

As I understand it, in PHP the main source of cryptographically strong randomness is openssl_random_pseudo_bytes() . However, this returns an array of bytes, not alphanumeric characters.

To convert them to alphanumeric characters, I could either haveh them (which would create a longer than necessary string of a limited set of hexadecimal characters), or base64_encode() them (which would lead to the creation of a string with + , / and = , not alphabetic characters).

So, I think that instead, I could use random bytes as a source of entropy and generate my own string consisting only of characters 0-9a-zA-Z .

Then the problem arises - how to translate from 256 separate values ​​(one byte of input) to 62 different values ​​(one character of output). And in a sense, that all 62 characters are equally likely. (Otherwise there will be 8 characters that appear more often than the rest).

Or maybe I should use a different approach? I would like my string to be as short as possible (for example, 20 characters or more - shorter URLs) and consist only of alphanumeric characters (so this does not need to be specially escaped).

+6
source share
3 answers

You can implement your own base64 encoding, sort of. If you can allow two specific characters, it could be anything, for example . and - , it does not really matter. It may even be a space for one of them. In any case, what would you do is the following:

 $alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-"; // using . and - for the two symbols here $input = [123,193,21,13]; // whatever your input it, I'm assuming an array of bytes $output = ""; foreach($input as $byte) { $output .= $alphabet[$byte%64]; } 

Assuming random input, all characters have an equal probability of occurrence.

If you can't afford anything but pure alphanumeric, cut out the characters from $alphabet and use %62 instead of %64 . While this means that you have a slight bias with respect to the 0 ridges through 7 , I don't think this is important enough to worry about.

+2
source

I found this function on php.net in user comments.

  function crypto_rand($min,$max) { $range = $max - $min; if ($range == 0) return $min; // not so random... $length = (int) (log($range,2) / 8) + 1; return $min + (hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s))) % $range); } 

Then do something like

  for($i=0; $i<20; $i++) { $string.= chr(crypto_rand(1,26)+96); //or +64 for upper case } 

Or similar.

0
source

Note: THIS IS WRONG! I am leaving this answer for reference only.

(31 * 256)% 62 = 0

For each output alphanumeric character, generate 31 random values. Sum these 31 values ​​and take modulo 62.

It’s kind of cruel, but this is the only "correct mathematical option" that I can think of :)

0
source

Source: https://habr.com/ru/post/957203/


All Articles