2

In my .bashrc file I appended the line psswd() { LC_ALL=C tr -dc 'a-zA-Z0-9-!"@/#$%^&*()_+~' < /dev/urandom | head -c "$1";echo ;} so that when I type psswd n in the terminal, it returns a string of n random characters. I would like to achieve the same but using /dev/random instead of /dev/urandom. However when I replace /urandom by /random, calling psswd does nothing (cannot even output a single random character after 1 hour), it's as if it's frozen. I don't know why it is so, and I know it's not a problem of not having enough entropy. The reason is that the command od -An -N1 -i /dev/random returns a random number.

Note that this last command returns a random number almost instantly if I type it say after a fresh reboot. But if I have invoked a call to psswd n with /random, then the command returns a random number after about 15 seconds. So the call to /random seems to have some effect on /dev/random even though it produces no output when I call the function psswd.

Overall I'd like to know how I could create a function that uses /dev/random to generate a random string of n characters.

6
  • May I ask why you want to use /dev/random? (See also sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers) Commented Aug 1, 2015 at 19:42
  • May I ask what the original problem was that you were asked to solve? And why /dev/random became so important to the solution of the original problem? You don't state what problem you set out to solve. Commented Aug 1, 2015 at 19:50
  • @melpomene : Basically for the fun of it. It's doing something I can do with urandom already, with random. Ryan : I wasn't asked to solve any problem. All I want to do is to create a function in bash that generates a string of n random characters using /dev/random. Commented Aug 1, 2015 at 20:06
  • Thanks for the clarification: maybe useful? 10 Ways to Generate a Random Password from the Command Line Commented Aug 1, 2015 at 20:36
  • That's basically where I got the idea to try such a thing, note that the penultimate command is very similar to the one I tried for my .bashrc file. But it uses /dev/urandom and not /dev/random. Commented Aug 1, 2015 at 20:50

2 Answers 2

1

This happens because libc will buffer tr's output when it's not a terminal. On GNU/Linux, it's 4096 bytes. This means that tr has to produce 4096 bytes of output before head will see the first few bytes, even though it just asks for e.g. 8.

Since you only keep 78 out of 256 values, /dev/random has to produce on average 4096*256/78 = 13443 bytes of random output before you get your password.

/dev/random on my system, starting from an empty pool, took 26 seconds to generate 20 bytes. That means those bytes would take 13443*26/20 = 17475 seconds, or almost 5 hours, to generate a password.

At this point it would print the password, but it would require another bufferful for tr to realize head doesn't want anymore, so it would take another 5 hours before the command would exit.

If you disabled buffering, you would only need to generate (8+1)*256/78 = 29 bytes, which would take a mere ~38 seconds. On GNU/Linux, you can do this with stdbuf -o0:

$ time { LC_ALL=C stdbuf -o0 tr -dc 'a-zA-Z0-9-!"@/#$%^&*()_+~' < /dev/random | head -c 8; echo; }
9D^MKbT)

real    0m36.172s
user    0m0.000s
sys     0m0.010s
Sign up to request clarification or add additional context in comments.

Comments

0

…when I replace /urandom by /random, calling psswd does nothing…

That happens. As Wikipedia explains:

In Unix-like operating systems, /dev/random is a special file that serves as a blocking pseudorandom number generator....

A counterpart to /dev/random is /dev/urandom ("unlimited"/non-blocking random source [Emphasis added]

"Blocking" means that when it thinks it has run out of entropy, it stops producing numbers. For example, on my system, the first command in your pipeline produces the following:

$ LC_ALL=C tr -dc 'a-zA-Z0-9-!"@/#$%^&*()_+~' < /dev/random  
~Sk(+!h

And, then, it hangs, presumably while waiting for more entropy.

This issue is discussed further here where it is argued that /dev/urandom is good enough.

Speeding it up

tr appears to buffer its output which delays the appearance of characters. The work-around is to use stdbuf and, for me, this results in a substantial speed-up:

LC_ALL=C stdbuf -o0 tr -dc 'a-zA-Z0-9-!"@/#$%^&*()_+~' < /dev/random

3 Comments

Well it blocks without returning any output in my case. The command LC_ALL=C tr -dc 'a-zA-Z0-9-!"@/#$%^&*()_+~' < /dev/random does not output anything to me, even if I wait 1 hour. On the other hand using od returns something out of /dev/random. So it has enough entropy (as I said in my first post), to return at least a string of length 1. I don't know why using tr does not produce any output in my case.
/dev/random may have to produce several characters before the output of tr has even one. Further, head is waiting for either $1` characters or a SIGPIPE before it produces any output. So, one character from /dev/random will only produce output if you asked for a 1-character password and the character that /dev/random produced just happened to match the list in the tr command.
I see. Another question: why od gets an output though? od -An -N10 -t c /dev/random returns numbers and special characters to me, almost instantly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.