4

If the rand() function creates a random number that is 4 bytes in length, and I wanted to create a random number that is 1024 bits in length (128 bytes), is the easiest method to get this by concatenating the rand() function 256 times or is there an alternative method?

#include <stdio.h>
#include <string.h>

int main(void) {
  const char data[128];
  memset(&data, 0x36, 128);

  printf("%s\n", data);
  puts("");
  printf("%d\n", sizeof(data)/sizeof(data[0]));
  puts("");

  int i = 0;
  unsigned long rez = 0;

  for(i = 0; i < 20; i++) {
      unsigned int num = rand();
      rez = rez + num;
      printf("%x\n", rez);
  }

  printf("%x\n", rez);
  return 0;
}
nice_remark
  • 325
  • 3
  • 12
  • Yes, it's the easiest way. – HolyBlackCat Jun 12 '18 at 14:14
  • 6
    Why 256 times? 128 bytes / 4 bytes = 32 – Gerhardh Jun 12 '18 at 14:17
  • 1
    It's certainly the *easiest* way, once you get your math correct, as per @Gerhardh - you just have to investigate whether the distribution will be what you want. If it's a simple PRNG (for example, if it follows the `t(n) = t(n-1) * m + c modulo x` method), you may find some 128-bit values impossible. – paxdiablo Jun 12 '18 at 14:24
  • 1
    More generally, "concatenation" means that you have to repeatedly multiply the currently accumulated value by `RAND_MAX + 1` and then add the next `rand()` result to the product. Repeat until you reach the desired length. – AnT stands with Russia Jun 12 '18 at 14:25
  • 1024 bits -> 128 bytes, unless my math is wrong. So although the data is not necessary a string, it can still be concatenated through `strcat` or must a new function be created to concatenate the data? – nice_remark Jun 12 '18 at 14:27
  • 4
    Notice that `rand()` generates random numbers between 0 and `RAND_MAX`, which often is way smaller than 2³²-1 (classic case: MSVC++); thus, it's incorrect to say that it generates a 4-byte random number. – Matteo Italia Jun 12 '18 at 14:28
  • Calling it 32 times would be enough (32 * 4 = 128). – Paul Ogilvie Jun 12 '18 at 14:28
  • 1
    `strcat` has noting to do with this. The best way to achieve that depends on several factors. Do you have support for 1024-bit arithmetics? Is your `rand()` really generating 4 bytes of random bits? Again, the most general way to "concatenate" the results is described in my comment above: use arithmetic, multiply and add repeatedly. – AnT stands with Russia Jun 12 '18 at 14:30
  • So kind of like the sample code that I've been working with (added it recently to the question). But that does not cause the value to extend beyond 4 bytes, so something must be wrong... – nice_remark Jun 12 '18 at 14:44
  • For what purpose do you need such a random number? Do you just want it displayed or is it something else? – J...S Jun 12 '18 at 14:48
  • The number is supposed to be stored in memory, and move around memory when interrupted by another peripheral (its an embedded project). The data value is supposed to serve as a key or password – nice_remark Jun 12 '18 at 14:52

4 Answers4

4

is the easiest method to get this by concatenating the rand() function 256 times or is there an alternative method?

Each rand() returns a value in the [0...RAND_MAX] range. RAND_MAX is limited to 32767 <= RAND_MAX <= INT_MAX.

Very commonly RAND_MAX is a Mersenne number of the form 2n − 1. Code can take advantage of this this very common implementation dependent value. Each rand() call then provides RAND_MAX_BITS and not 32 as suggested by OP for a 4-byte int. @Matteo Italia

[See far below update]

#include <stdlib.h>

#if RAND_MAX == 0x7FFF
#define RAND_MAX_BITS 15
#elif RAND_MAX == 0x7FFFFFFF
#define RAND_MAX_BITS 31
#else
#error TBD code
#endif

Call rand() ⌈size * 8 / RAND_MAX_BITS⌉ times. This eases the number of rand() calls needed from size.

void rand_byte(uint8_t *dest, size_t size) {
  int r_queue = 0;
  int r_bit_count = 0;
  for (size_t i = 0; i < size; i++) {
    int r = 0;
    //printf("%3zu %2d %8x\n", i, r_bit_count, r_queue);
    if (r_bit_count < 8) {
      int need = 8 - r_bit_count;
      r = r_queue << need;
      r_queue = rand();
      r ^= r_queue;  // OK to flip bits already saved in `r`
      r_queue >>= need;
      r_bit_count = RAND_MAX_BITS - need;
    } else {
      r = r_queue;
      r_queue >>= 8;
      r_bit_count -= 8;
    }
    dest[i] = r;
  }
}

int main(void) {
  uint8_t buf[128];
  rand_byte(buf, sizeof buf);
  ...
  return 0;
}

If you want the easiest bit less efficient code, simply call rand() for each byte as answered by @dbush


[Update 2021]

@Anonymous Question Guy posted a nifty macro that returns the bit width of a Mersenne number, more generally than the #if RAND_MAX == 0x7FFF approach above.

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
              + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

_Static_assert((RAND_MAX & 1 && (RAND_MAX/2 + 1) & (RAND_MAX/2)) == 0, 
    "RAND_MAX is not a Mersenne number");
#define RAND_MAX_BITS IMAX_BITS(RAND_MAX)
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    cool. Both that article and the Wikipedia article also mention that "Mersenne number" is often used only for numbers where the exponent is prime. But it is often used your way, too. – rici Jun 12 '18 at 15:51
  • By the way, [this question](https://stackoverflow.com/q/29336510/) is probably relevant to your answer. The C standard only defines the range of possible values returned by `rand()`; it doesn't insist that all of them can be produced. So the OS X implementation cited is conformant. – rici Jun 12 '18 at 16:23
  • @rici True that the _quality_ of functions like `rand()`, `sqrt()`, `pow()` and many others are not tightly specified by C: [caveat emptor](https://en.wikipedia.org/wiki/Caveat_emptor). – chux - Reinstate Monica Jun 12 '18 at 16:31
  • TBH instead of wasting time sniffing the properties of the builtin `rand()` (which is often sub-par) I'd just embed a good XorShift generator with a full 32 or 64 bit output, call it 4 or 2 times and be happy with it. – Matteo Italia Jun 12 '18 at 16:33
  • 1
    @MatteoItalia True and that idea would serve as a good answer by you. As OP's question was about `rand()` usage (sometimes code requirements restrict options) - this answer details how to minimize calls about `rand()`. – chux - Reinstate Monica Jun 12 '18 at 17:24
3

The C standard states that RAND_MAX has a minimum value of 32767 (0x7fff), so it's best to work under that assumption.

Because the function will only return 15 random bits, using all the bits in one call will involve some bit shifting and masking to get the results in the proper place. The simplest way to do this would be to call rand 128 times, take the low order byte of each result, and write it to your byte array:

unsigned char rand_val[128];

for (int i=0; i<128; i++) {
    rand_val[i] = rand() & 0xff;
}

Don't forget to call srand exactly once somewhere before this in your code.

Using strcat as you mentioned in your comment won't work because this function works on null terminated strings, and a byte containing 0 is a valid random number.

If you plan on using these random values for anything involving cryptography, you're better off using a secure random number generator. If you have OpenSSL available, use RAND_bytes for this purpose:

unsigned char rand_val[128];
RAND_bytes(rand_val, sizeof(rand_val));
dbush
  • 205,898
  • 23
  • 218
  • 273
  • I used a loop similar to the first snippet of code you provided, but is there a way of taking that entire array and saving it to a singular variable? – nice_remark Jun 12 '18 at 15:14
  • 1
    @nice_remark It depends on the type of the variable. – dbush Jun 12 '18 at 15:15
2

On most POSIX (Unix-like) systems, you can also read 128 bytes from /dev/urandom which you would open like a regular file in binary mode — even though POSIX does not specify the device.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 3
    I don't believe Posix requires or even suggests `/dev/{,u}random`, although many popular unix-likes provide them. Posix requires `null`, `tty` and `console` in the `/dev/` namespace: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap10.html – rici Jun 12 '18 at 15:18
0

The properties of C rand() are vaguely specified by the standard; as said in a comment, the number of actual usable bits depends from implementation, and their quality has been historically plagued by sub-par implementations. Also, rand() affects the global state of the program and on many implementations is not thread safe.

Given that a there are good, known and simple PRNGs such as the ones from the XorShift family, I would just use one of them.

#include <stdint.h> 
/* The state must be seeded so that it is not all zero */
uint64_t s[2];
uint64_t xorshift128plus(void) {
    uint64_t x = s[0];
    uint64_t const y = s[1];
    s[0] = y;
    x ^= x << 23;
    s[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
    return s[1] + y;
}

void next128bits(unsigned char ch[16]) {
    uint64_t t = xorshift128plus();
    memcpy(ch, &t, sizeof(t));
    t = xorshift128plus();
    memcpy(ch + 8, &t, sizeof(t));
} 
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299