Augment rand argument parsing to allow scaling
authorNeil Horman <nhorman@openssl.org>
Sun, 5 Nov 2023 22:51:38 +0000 (17:51 -0500)
committerTomas Mraz <tomas@openssl.org>
Mon, 13 Nov 2023 11:21:34 +0000 (12:21 +0100)
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)

apps/rand.c
doc/man1/openssl-rand.pod.in
test/recipes/05-test_rand.t

index c0ab51dd83af6064d1547d0f6c1a95c82d18bc51..b123a151ea74ce98437528587a756533006f23c0 100644 (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");
index af2c24ae4b0f8e2af191d6eb5b6b4ef3191bbcdf..d4427a67246cee73998a7510c6e2359891abe681 100644 (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
index 6d097c1721a99402eca7ecd736cbf130999c29dc..dac37b8fd131e7b0b4a83f939652953ccd7386f6 100644 (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);