diff --git a/apps/rand.c b/apps/rand.c index c0ab51dd83..b123a151ea 100644 --- a/apps/rand.c +++ b/apps/rand.c @@ -25,7 +25,7 @@ typedef enum OPTION_choice { } OPTION_CHOICE; const OPTIONS rand_options[] = { - {OPT_HELP_STR, 1, '-', "Usage: %s [options] num\n"}, + {OPT_HELP_STR, 1, '-', "Usage: %s [options] num[K|M|G|T]\n"}, OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, @@ -52,8 +52,10 @@ int rand_main(int argc, char **argv) BIO *out = NULL; char *outfile = NULL, *prog; OPTION_CHOICE o; - int format = FORMAT_BINARY, r, i, ret = 1, buflen = 131072; + int format = FORMAT_BINARY, r, i, ret = 1; + size_t buflen = (1 << 16); /* max rand chunk size is 2^16 bytes */ long num = -1; + uint64_t scaled_num = 0; uint8_t *buf = NULL; prog = opt_init(argc, argv, rand_options); @@ -95,8 +97,85 @@ int rand_main(int argc, char **argv) argc = opt_num_rest(); argv = opt_rest(); if (argc == 1) { - if (!opt_long(argv[0], &num) || num <= 0) + int factoridx = 0; + int shift = 0; + + /* + * special case for requesting the max allowed + * number of random bytes to be generated + */ + if (!strcmp(argv[0], "max")) { + /* + * 2^61 bytes is the limit of random output + * per drbg instantiation + */ + scaled_num = UINT64_MAX >> 3; + } else { + /* + * iterate over the value and check to see if there are + * any non-numerical chars + * A non digit suffix indicates we need to shift the + * number of requested bytes by a factor of: + * K = 1024^1 (1 << (10 * 1)) + * M = 1024^2 (1 << (10 * 2)) + * G = 1024^3 (1 << (10 * 3)) + * T = 1024^4 (1 << (10 * 4)) + * which can be achieved by bit-shifting the number + */ + while (argv[0][factoridx]) { + if (!isdigit((int)(argv[0][factoridx]))) { + switch(argv[0][factoridx]) { + case 'K': + shift = 10; + break; + case 'M': + shift = 20; + break; + case 'G': + shift = 30; + break; + case 'T': + shift = 40; + break; + default: + BIO_printf(bio_err, "Invalid size suffix %s\n", + &argv[0][factoridx]); + goto opthelp; + } + break; + } + factoridx++; + } + + if (shift != 0 && strlen(&argv[0][factoridx]) != 1) { + BIO_printf(bio_err, "Invalid size suffix %s\n", + &argv[0][factoridx]); + goto opthelp; + } + } + /* Remove the suffix from the arg so that opt_long works */ + if (shift != 0) + argv[0][factoridx] = '\0'; + + if ((scaled_num == 0) && (!opt_long(argv[0], &num) || num <= 0)) goto opthelp; + + if (shift != 0) { + /* check for overflow */ + if ((UINT64_MAX >> shift) < (size_t)num) { + BIO_printf(bio_err, "%lu bytes with suffix overflows\n", + num); + goto opthelp; + } + scaled_num = num << shift; + if (scaled_num > (UINT64_MAX >> 3)) { + BIO_printf(bio_err, "Request exceeds max allowed output\n"); + goto opthelp; + } + } else { + if (scaled_num == 0) + scaled_num = num; + } } else if (!opt_check_rest_arg(NULL)) { goto opthelp; } @@ -116,10 +195,10 @@ int rand_main(int argc, char **argv) } buf = app_malloc(buflen, "buffer for output file"); - while (num > 0) { - long chunk; + while (scaled_num > 0) { + int chunk; - chunk = (num > buflen) ? buflen : num; + chunk = scaled_num > buflen ? (int)buflen : (int)scaled_num; r = RAND_bytes(buf, chunk); if (r <= 0) goto end; @@ -131,7 +210,7 @@ int rand_main(int argc, char **argv) if (BIO_printf(out, "%02x", buf[i]) != 2) goto end; } - num -= chunk; + scaled_num -= chunk; } if (format == FORMAT_TEXT) BIO_puts(out, "\n"); diff --git a/doc/man1/openssl-rand.pod.in b/doc/man1/openssl-rand.pod.in index af2c24ae4b..d4427a6724 100644 --- a/doc/man1/openssl-rand.pod.in +++ b/doc/man1/openssl-rand.pod.in @@ -14,12 +14,20 @@ B [B<-hex>] {- $OpenSSL::safe::opt_engine_synopsis -}{- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_provider_synopsis -} -I +I[K|M|G|T] =head1 DESCRIPTION This command generates I random bytes using a cryptographically -secure pseudo random number generator (CSPRNG). +secure pseudo random number generator (CSPRNG). A suffix [K|M|G|T] may be +appended to the num value to indicate the requested value be scaled as a +multiple of KiB/MiB/GiB/TiB respectively. Note that suffixes are case +sensitive, and that the suffixes represent binary multiples +(K = 1024 bytes, M = 1024*1024 bytes, etc). + +The string 'max' may be substituted for a numercial value in num, to request the +maximum number of bytes the CSPRNG can produce per instantiation. Currently, +this is restricted to 2^61 bytes as per NIST SP 800-90C. The random bytes are generated using the L function, which provides a security level of 256 bits, provided it managed to diff --git a/test/recipes/05-test_rand.t b/test/recipes/05-test_rand.t index 6d097c1721..dac37b8fd1 100644 --- a/test/recipes/05-test_rand.t +++ b/test/recipes/05-test_rand.t @@ -32,6 +32,10 @@ SKIP: { ok($success && $randdata[0] eq $expected, "rand with ossltest: Check rand output is as expected"); + @randdata = run(app(['openssl', 'rand', '-hex', '2K' ]), + capture => 1, statusvar => \$success); + chomp(@randdata); + @randdata = run(app(['openssl', 'rand', '-engine', 'dasync', '-hex', '16' ]), capture => 1, statusvar => \$success); chomp(@randdata);