1

Suppose I have an array of bytes from a secure PRNG, and I need to generate a number between 1 and 10 using that data, how would I do that correctly?

Maestro
  • 9,046
  • 15
  • 83
  • 116
  • 6
    Don't get me wrong, but the answer is `by writing a piece of code.` :-) – Sourav Ghosh Mar 18 '15 at 12:30
  • BTW, are you looking for [modulus operator](http://www.cprogramming.com/tutorial/modulus.html)? – Sourav Ghosh Mar 18 '15 at 12:32
  • @SouravGhosh Yes it could be done using modulus, if you make the comment an answer I accept it. – Maestro Mar 18 '15 at 12:35
  • 1
    If you go for `%`, then you also might want to read about the [modulo bias](https://zuttobenkyou.wordpress.com/2012/10/18/generating-random-numbers-without-modulo-bias/). – cremno Mar 18 '15 at 12:41
  • Personally I would look up a piece of code (usually called something like `getInt(int max)`) and integrate that. Otherwise it's pretty easy to create bias. Related [JavaCard code](http://stackoverflow.com/a/17309537/589259) . Mind the remarks. – Maarten Bodewes Mar 18 '15 at 13:53
  • I presume you mean `1 <= x <= 10` here :) – Maarten Bodewes Mar 18 '15 at 15:19
  • I think the [first paragraph of this answer](http://crypto.stackexchange.com/a/5721/1172) tells you all you need to know. – Maarten Bodewes Mar 18 '15 at 15:28

4 Answers4

1

As per the comments follow-up, it seems what you need is modulus operator [%].

You may also need to check the related wiki.

Note: Every time we use the modulo operator on a random number, there is a probability that we'll be running into modulo bias, which ends up in disbalancing the fair distribution of random numbers. You've to take care of that.

For a detailed discussion on this, please see this question and related answers.

Community
  • 1
  • 1
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Please add some information about bias to make this answer complete. – Maarten Bodewes Mar 18 '15 at 13:58
  • @MaartenBodewes Updated. Please review. – Sourav Ghosh Mar 18 '15 at 14:45
  • Much better. Unfortunately the answers in the link are pretty poor. Maybe try [this](http://crypto.stackexchange.com/q/7996/1172) instead. You could of course use 8 bit number for a range 0..10 - depends if you want to change to a larger range and how much data you want to extract from the random pool (you would be OK with just 4 bits, but you would have a reject 6 times out of 16, which is rather poor). – Maarten Bodewes Mar 18 '15 at 15:15
1

Think of the array as one big unsigned integer. Then the answer is simple:

(Big_Number % 10) + 1

So all that is needed is a method to find the modulus 10 of big integers. Using modular exponentiation:

#include <limits.h>
#include <stdlib.h>

int ArrayMod10(const unsigned char *a, size_t n) {
  int mod10 = 0;
  int base = (UCHAR_MAX + 1) % 10;
  for (size_t i = n; i-- > 0;  ) {
    mod10 = (base*mod10 + a[i]) % 10;
    base = (base * base) % 10;
  }
  return mod10;
}

void test10(size_t n) {
  unsigned char a[n];

  // fill array with your secure PRNG
  for (size_t i = 0; i<n; i++) a[i] = rand();

  return ArrayMod10(a, n) + 1;
}

There will be a slight bias as 256^n is not a power of 10. With large n, this will rapidly decrease in significance.

Untested code: Detect if a biased result occurred. Calling code could repeatedly call this function with new a array values to get an unbiased result on the rare occasions when bias occurs.

int ArrayMod10BiasDetect(const unsigned char *a, size_t n, bool *biasptr) {
  bool bias = true;
  int mod10 = 0;
  int base = (UCHAR_MAX + 1) % 10;  // Note base is usually 6: 256%10, 65536%10, etc.
  for (size_t i = n; i-- > 0;  ) {
    mod10 = (base*mod10 + a[i]) % 10;
    if (n > 0) {
      if (a[i] < UCHAR_MAX) bias = false;
    } else {
      if (a[i] < UCHAR_MAX + 1 - base) bias = false;
    }
    base = (base * base) % 10;
  }
  *biaseptr = bias;
  return mod10;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • @Maarten Bodewes Given OP is using a "secure PRNG", certainly of reasonable length, so agree, no practical need for bias concerns. – chux - Reinstate Monica Mar 18 '15 at 17:44
  • I want to use this code, but in reality I dont need to pick between 1 and 10, it needs to be variabele. Can I just safely replace every occurence of 1 and 10 in your code with a upper/lower bound variabele? – Maestro Mar 18 '15 at 18:33
  • @Muis Almost. Let `ArrayModX()` return the range [0...X-1], then add lower bound with`X = upper-lower+1`. If the number is outside the range 255, use larger objects than `unsigned char *a` like `uint32_t *a` and then the "10" can be replaced. Some additional concerns come in if your "10" is larger than `INT_MAX`. IOWs saying "code with a upper/lower bound variable" is too vague. Better to say the type or range of the number like: `0 <= lower_bound <= upper_bound <= INT_MAX` or something like that. – chux - Reinstate Monica Mar 18 '15 at 19:08
  • This answer does still have bias, where no bias is really required. – Maarten Bodewes Mar 18 '15 at 21:41
  • @Maarten Bodewes `ArrayMod10BiasDetect()` detects and reports the bias that occurs with modulo `10` on a binary number. Do you refer to some other bias? – chux - Reinstate Monica Mar 18 '15 at 22:42
0

It depends on a bunch of things. Secure PRNG sometimes makes long byte arrays instead of integers, let's say it is 16 bytes long array, then extract 32 bit integer like so: buf[0]*0x1000000+buf[1]*0x10000+buf[2]*0x100+buf[3] or use shift operator. This is random so big-endian/little-endian doesn't matter.

char randbytes[16];
//...

const char *p = randbytes;

//assumes size of int is 4
unsigned int rand1 = p[0] << 24 + p[1] << 16 + p[2] << 8 + p[3]; p += 4;
unsigned int rand2 = p[0] << 24 + p[1] << 16 + p[2] << 8 + p[3]; p += 4;
unsigned int rand3 = p[0] << 24 + p[1] << 16 + p[2] << 8 + p[3]; p += 4;
unsigned int rand4 = p[0] << 24 + p[1] << 16 + p[2] << 8 + p[3];

Then use % on the integer

ps, I think that's a long answer. If you want number between 1 and 10 then just use % on first byte.

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • This will leave you with a small but detectable bias Why would you need 16 bytes to generate an integer of 32 bits???. – Maarten Bodewes Mar 18 '15 at 17:21
  • It's 4 different integers extracted from 16 bytes, it's typical output of secure PRNG. I changed my mind after typing it, I don't think the question has anything to do with secure PRNG – Barmak Shemirani Mar 18 '15 at 19:22
0

OK, so this answer is in Java until I get to my Eclipse C/C++ IDE:

public final static int simpleBound(Random rbg, int n) {

    final int BYTE_VALUES = 256;

    // sanity check, only return positive numbers
    if (n <= 0) {
        throw new IllegalArgumentException("Oops");
    }

    // sanity check: choice of value 0 or 0...
    if (n == 1) {
        return 0;
    }

    // sanity check: does not fit in byte
    if (n > BYTE_VALUES) {
        throw new IllegalArgumentException("Oops");
    }

    // optimization for n = 2^y
    if (Integer.bitCount(n) == 1) {
        final int mask = n - 1;
        return retrieveRandomByte(rbg) & mask;
    }

    // you can skip to this if you are sure n = 10

    // z is upper bound, and contains floor(z / n) blocks of n values
    final int z = (BYTE_VALUES / n) * n;
    int x;
    do {
        x = retrieveRandomByte(rbg);
    } while (x >= z);
    return x % n;
}

So n is the maximum value in a range [0..n), i.e. n is exclusive. For a range [1..10] simply increase the result with 1.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Note that you need a maximum of 24 bytes to generate a number between 1 and 10 (after that the chance of not generating a number between 1 and 10 becomes smaller than 1 / 2 ^ 128 :P ) – Maarten Bodewes Mar 18 '15 at 16:56
  • Note too that it would be recommended to use a 16 bit number if you want to bring down the maximum number of bytes required (in this case to 20 bytes). – Maarten Bodewes Mar 18 '15 at 17:16
  • I dont know how to translate (Integer.bitCount(n) == 1) to C, so I cannot use this function. – Maestro Mar 18 '15 at 18:36
  • You can simply leave it out, that's required for optimization only. However, [this is stackoverflow](http://stackoverflow.com/q/109023/589259). If you need a link on how to convert `unsigned char` to a 32 bit value, just scream :) – Maarten Bodewes Mar 18 '15 at 18:39