1
\$\begingroup\$

What's the best way to approach generating a password using a secure prng? In Python, I could simply use os.urandom. Any suggestions on this?

 _ = require('underscore')

exports.gen = (n=10) ->
  throw new Error 'Not a number!' if typeof(n) isnt 'number'
  throw new Error 'Prefered (n): [10..128]' if n < 10 or n > 128
  chars = (String.fromCharCode(i) for i in [33..126]).join ''
  _.times(n, -> chars[(Math.random() * chars.length) | 0]).join ''
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The equivalent to os.urandom is found in the crypto module: crypto.randomBytes. That module also contains most other functions you might want for password hashing etc.

To use the same password character set as your current code, you could do

exports.gen = (length = 10) ->
  throw new Error 'Length is not a number!' if typeof length isnt 'number'
  throw new Error 'Length must be [10..128]' unless 10 <= length <= 128

  range = 126 - 33
  buffer = require('crypto').randomBytes length # note: may throw an error

  (String.fromCharCode(33 + (range * c / 255) | 0) for c in buffer).join ''

I've changed a few small things

  • length instead of n
  • Slightly more descriptive error messages, i.e. what is not a number, and sterner wording for bad length values ("prefered" [sic] makes it sound as though it's just a suggestion, but it's actually a requirement)
  • CoffeeScript has chained comparisons like Python, so 10 <= length <= 128 can be used
\$\endgroup\$
6
  • \$\begingroup\$ You're definitely right about the error handles, I'll make that correction. I see what you're doing with randomBytes, as this derives from a more secure source. Should I be concerned with duplicate characters? \$\endgroup\$
    – user27606
    Commented Aug 15, 2014 at 16:07
  • \$\begingroup\$ @LucienLachance Well, it's up to you if you want to avoid duplicate characters. It's independent of the random number generator used (yours may produce duplicates as well), so I can't from the code say you should or shouldn't add that. If the passwords are meant for humans, it might be better to avoid O vs 0 and 1 vs l confusion \$\endgroup\$
    – Flambino
    Commented Aug 15, 2014 at 17:51
  • \$\begingroup\$ Thanks for explaining that. One last question, I see you noted that calling randomBytes could possibly throw an error. Is it necessary to handle the error much like how the docs uses their example? And could you explain what's going on in your last line (dividing by 255)? \$\endgroup\$
    – user27606
    Commented Aug 15, 2014 at 21:07
  • \$\begingroup\$ @LucienLachance If an exception may be thrown it's a good idea to expect it to be thrown. It's very unlikely that it's going to happen here, but still: Be prepared to handle it in some way. Or let it escalate up the call stack and catch it somewhere else. Again, hard to say without knowing the context and intended usage of the code. As for dividing by 255: The bytes in the buffer are basically integers valued 0-255. Since we can only use a smaller range (33-126), we divide each byte by 255 to get a number between 0 and 1, then multiply and add to get something in our desired range \$\endgroup\$
    – Flambino
    Commented Aug 16, 2014 at 1:17
  • \$\begingroup\$ @LucienLachance To put it another way: You used Math.random() to get a 0-1 number, where I used c / 255. Same principle. You could also do String.fromCharCode(33 + c % range) instead. Again the idea is to take a 0-255 integer and somehow convert it to a 33-126 integer. How exactly that's done is really all up to you. \$\endgroup\$
    – Flambino
    Commented Aug 16, 2014 at 1:27

Your Answer

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