Augment rand argument parsing to allow scaling

Instead of just accepting a number of bytes, allows openssl rand to
accept a k|m|g suffix to scale to kbytes/mbytes/gbytes

Fixes #22622

Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Tom Cosgrove <tom.cosgrove@arm.com>
Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22624)
This commit is contained in:
Neil Horman 2023-11-05 17:51:38 -05:00 committed by Tomas Mraz
parent 66c27d06e0
commit ae9fe65d9f
3 changed files with 100 additions and 9 deletions

View File

@ -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");

View File

@ -14,12 +14,20 @@ B<openssl rand>
[B<-hex>]
{- $OpenSSL::safe::opt_engine_synopsis -}{- $OpenSSL::safe::opt_r_synopsis -}
{- $OpenSSL::safe::opt_provider_synopsis -}
I<num>
I<num>[K|M|G|T]
=head1 DESCRIPTION
This command generates I<num> 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<RAND_bytes(3)> function,
which provides a security level of 256 bits, provided it managed to

View File

@ -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);