Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


Generating random integers with PHP
New on LowEndTalk? Please Register and read our Community Rules.

All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.

Generating random integers with PHP

Not being any sort of crypto or math expert, I adapted this code from some code I found.

Is anyone here able to verify if this is a valid way to generate cryptographically sound random integers in PHP?

Crypt::create_iv() uses openssl_random_pseudo_bytes() with an affirmative $crypt_strong flag.

  public static function integer($max = PHP_INT_MAX, $min = 0)
  {
      $min = (int) $min;
      $max = (int) $max;
      $bits = 1;

      if ($max < $min):
          throw new RandomException('min is greater than max');
      elseif ($min === $max):
          throw new RandomException('min is equal to max');
      endif;

      $range = $max - $min + 1;

      while ((1 << $bits) <= $range) {
          $bits++;
      }

      $num_bytes = (int) (($bits + 7) / 8);
      $mask = (1 << $bits) - 1;

      do {
          $data = Crypt::create_iv($num_bytes);
          $result = 0;

          for ($i = 0; $i < $num_bytes; $i++):
              $result = ($result * 256) + ord($data{$i});
          endfor;

          $result = $result & $mask;
      } while ($result >= $range);

      return $result + $min;
  }

Comments

  • CoreyCorey Member
    edited October 2014

    What's wrong with rand(),

    and why are you throwing an exception when generating a random number.

    NVM on that.... but this seems weird to me...

  • @Corey said:
    What's wrong with rand(), and why are you throwing an exception when generating a random number.
    NVM on that.... but this seems weird to me...

    Erm, rand() won't generate you cryptographically secure random numbers. Also, that's a method in a class, so error handling is being done by throwing exception, which is the correct way.

  • @Corey said:
    What's wrong with rand(),

    It's not in any way, shape or form, cryptographically strong.

    NVM on that.... but this seems weird to me...

    I prefer handling errors with exceptions. You're going to be really horrified when I finish.

  • I don't know very much about the theory but to me, depending on random.org has been a better idea.

  • MicrolinuxMicrolinux Member
    edited October 2014

    It's for a session class (other things eventually), so I'm not sure they'd appreciate the volume or if the latency would be acceptable.

  • emgemg Veteran

    Microlinux asks a very good question. I will let others respond regarding his PHP code.

    An equally valid question is: How secure is the openssl random number generator that his code depends on? If it is running on a virtual machine (VPS), then it may not be starting with a good seed, nor is it gathering much entropy (randomness).

  • perennateperennate Member, Host Rep
    edited October 2014

    I'm not convinced that your approach uniformly distributed entropy from openssl_random_pseudo_bytes over the range. If 2^$bits is 1.2 times of the desired range, then doesn't that favor the beginning 20% of the range?

    Also, use a library: https://github.com/ircmaxell/random_compat

    Edit2: also this one, no idea what the difference is but..: https://github.com/ircmaxell/RandomLib

  • perennateperennate Member, Host Rep

    emg said: An equally valid question is: How secure is the openssl random number generator that his code depends on? If it is running on a virtual machine (VPS), then it may not be starting with a good seed, nor is it gathering much entropy (randomness).

    I feel like the virtualization would just introduce additional entropy. Anyway doubt you can get anything better than OpenSSL.

  • serverian said: Erm, rand() won't generate you cryptographically secure random numbers. Also, that's a method in a class, so error handling is being done by throwing exception, which is the correct way.

    I guess what I'm confused about is how is max going to be less than min and why do you even need to pass in anything, why not always use the constant?

  • perennateperennate Member, Host Rep
    edited October 2014

    Corey said: I guess what I'm confused about is how is max going to be less than min and why do you even need to pass in anything, why not always use the constant?

    The function caller may wish to generate numbers between an arbitrary minimum and maximum; you could write a function that doesn't accept any inputs and just generates random 32-bit unsigned integer, but then this would be less useful to the caller. As the writer of a PHP function, you have no control over the specific integers that the caller will pass, except by throwing exceptions upon receiving bad inputs. In general it's preferable to build underlying functions to fail so that errors resulting from bad inputs don't propogate and make debugging complicated.

    Thanked by 1Microlinux
  • MicrolinuxMicrolinux Member
    edited October 2014

    @perennate said:
    I'm not convinced that your approach uniformly distributed entropy from openssl_random_pseudo_bytes over the range. If 2^$bits is 1.2 times of the desired range, then doesn't that favor the beginning 20% of the range?

    Not being an expert, that sounds like something valid to research.

    Also, use a library: https://github.com/ircmaxell/random_compat

    Edit2: also this one, no idea what the difference is but..: https://github.com/ircmaxell/RandomLib

    I just need the bare minimum, but those examples give me something to evaluate against, thanks.

  • ausaus Member
    edited October 2014

    openssl_random_pseudo_bytes(32, $cstrong) with some extra hashing maybe ;)

  • perennateperennate Member, Host Rep
    edited October 2014

    On second thought, it looks fine, since you're actually looping until you get something within range; didn't realize that the first time.

  • @perennate said:
    On second thought, it looks fine, since you're actually looping until you get something within range; didn't realize that the first time.

    Ok, thanks for the feedback!

Sign In or Register to comment.