10

Occasionally I need to generate a password while working at the Unix command line. I have the following alias in my zshrc:

alias randpass='openssl rand -base64 32 | tr -d /=+ | cut -c -30'

These commands generate a string of 30 alphanumeric characters by doing the following things:

  1. Use OpenSSL to generate 32 bytes of random data.
  2. Base64-encode the result.
  3. Remove all characters except for uppercase letters, lowercase letters, and digits.
  4. Truncate the result to 30 characters.

The only flaw I can see is that there may be fewer than 30 characters left after removing instances of /, =, and +. Of course, that would require these three symbols to make up over a third of the base64-encoded characters, which is phenomenally unlikely.

Is the output of “openssl rand” sufficiently random to use as the basis of passwords? Is there any kind of bias introduced by base64-encoding the random bytes? (I assume that removing /, =, and + introduces a bias but that this is pretty minimal.)

2 Answers 2

2

The other answer is correct that the encoding to base64 does not introduce bias. But it did not address that a sequence of 32 bytes is 45 characters long in base64 and, when it is cut to 30 characters, some randomness is lost. Not much of it is lost, however, for practical purposes. But I still decided to share my investigation in case someone has a similar doubt. I also suggest an improvement to the OP's command below.


256 possible values of a byte are mapped to 64 possible base64 values, so that is why the base64-encoded string is longer. Base64 encodes the input bytes sequentially. When a sequence of 32 bytes is encoded into base64 and the resulting string is truncated to 30 characters, the remaining string encodes only the first 22 bytes of the original byte sequence, so about 10 bytes are lost. This conclusion can be verified as follows.

$ openssl rand -base64 32 > test_base64
$ cut -c -30 < test_base64 > test_base64_cut
$ LC_ALL=C base64 -d < test_base64_cut | LC_ALL=C dd > test_base64_cut_decoded
base64: invalid input
0+1 records in
0+1 records out
22 bytes copied, 9.5475e-05 s, 230 kB/s
$ LC_ALL=C base64 -d < test_base64 | head -c22 > test_base64_decoded_cut
$ LC_ALL=C cmp test_base64_cut_decoded test_base64_decoded_cut

Since there are 256 possible values for a byte, the resulting invalidly formatted base64 string of 30 characters long is supposed to have 256^22 possible values because it encodes only the first 22 bytes. It amounts to 22*log(256)/log(2)=176 bits of entropy. However, for comparison, 62^29 < 64^29 < 256^22 < 62^30 < 64^30 possible values or, after rounding up, respectively 174≈174<176<179<180 bits of entropy. So, it is 4 bits of entropy less than it could be. When a 30-character long password is generated this way, it is still better than using just a character shorter random alphanumeric password. The removal of the "=" character does not affect our conclusion since it is a padding character and does not encode the data. Removing "/" and "+" characters should affect it, but it does not seem promising to investigate it any further for the practical purposes of generating a password of such length for personal use.


However, the command can be easily improved by skipping the base64 encoding step:

$ openssl rand 256 | tr -dc A-Za-z0-9 | cut -c -30

It renders 256 random bytes, then keeps only alphanumeric characters and cuts the resulting string to the first 30 characters. In this case, each character is drawn at random. Since there are 62 alphanumeric characters, the resulting string has 62^30 possible values, which is approximately 179 bits of entropy.

If special characters are wanted too, just add them to the range for the tr command. For example, that will give 95^30 possible values in case of 33 extra characters from OWASP password special characters list, which is approximately 198 bits of entropy if rounded up.

$ openssl rand 256 | tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~' | cut -c -30 
9

You could increase the number of bytes to 45. Then you are guaranteed not to get the value truncated to lower than 30 bytes because then at least a half (30 characters) would need to be /, = or + to render a string shorter than 30 characters.

Yes its sufficiently random.

No bias is introduced by Base64-encoding the characters, since you just convert them to a Another base format. Its still the exact same number. The only small bias introduced is removing the /, and +, which would correspond to removing every occurrence of 111110 and 111111 in the string, you would basically only lose 2 bits of entropy over a string that is 180 bits long, it would reduce the random to 178 bits, which is still much better than a SHA1 string (that many passwords is stored as), MD5 or AES 128 key.

The character = is only padding and does not have any meaningful value in a base64 string.

2
  • many sites require symbols so output of base64 is too limiting Sep 6, 2016 at 22:21
  • @ScottStensland Then you can keep /, + or = in the Base64 string. I have found many sites who FORBID anything else than A-Za-z0-9 and a few special characters, and thats why I suggest filtering out /, = and +. Sep 7, 2016 at 1:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .