How does jspwgen work?

jspwgen was updated on 13. Jan 14 to fix a few bugs and clean up the interface. Read more here.

jspwgen uses entropy (here, "randomness", random bits) which is generated by the user. At no time any system-provided random number generators are used. There are a lot of password generators that use system-provided entropy and the point of jspwgen is that these RNGs can not be trusted.

Entropy generation

Generating random bits is hard, and it is extremely hard on a computer, which itself is (almost) completely deterministic. This still holds when entropy is generated by a human being using a computer. In jspwgen, entropy is generated by the user moving their mouse "randomly" across a canvas element. Each time the browser fires an onmousemove event, the location of the point in conjunction with the current time is saved. When using a mobile device, the moves of fingers are recorded once they start sliding around inside the canvas element. There are some differences between using jspwgen on a mobile device and on a desktop (i.e. with a mouse), namely, that when using touch events, all events of all used fingers are recorded until the user lifts their finger from the screen again, even if they leave the canvas. However, this should not significantly weaken the entropy generation (mail me if you know better).

I initially designed jspwgen so that it would record points relative to the upper-left corner of the canvas, however, it ended up recording with respect to the viewport (apparently). This does not really matter (again, mail me if you know better) - the way it is now, the scrolling position provides a few bits more entropy.

For the current time jspwgen uses window.performance.now (see here) or, if that is not available, Date.getTime, which returns the number of milliseconds since 1.1.1970 (unix epoch). The former might contain more entropy since it has microsecond precision.

For the "weak entropy" bar to be full, jspwgen requires 700 recorded points, for the strong entropy bar it requires 7000 points. My humble estimation is that a single point contains only 0.5 to 1 bits of entropy. This is because, given all points \(\{0\dots n-1\}\) the \(n^{\text{th}}\) point can be estimated fairly well. How well it can be estimated depends on how "randomly" the user moves their mouse. The more derivatives of the path are non-zero, the harder it is to estimate the next point. For example, if the user followed a strict circle with a constant speed, the next point could always be estimated perfectly. Generally, following shapes decreases entropy, which is why drawing an image and then using it as entropy input for password generation is very bad.

One more note on timestamps: we could cut off the "known" part, i.e. the places before the comma, which rarely change (and if they do, they just count upwards), however, this does not harm the entropy so it is not done.

Compression

After generating the data points, they need to be compressed to the chosen password strength. (Note that jspwgen allows the user to be "stupid", i.e. they can already generate a "256bit" password as long as there is a single point of data. jspwgen relies on the psychological effect of the neatly moving entropy bars to keep users moving their mouse in amazement until they become bored.) For compression, PBKDF2 is used. The implementation used is http://crypto.stanford.edu/sjcl/ who I trust to implement it properly. The hash used in PBKDF2 is HMAC-SHA256. jspwgen actually "abuses" PBKDF2 in the sense that it does not use it as password based key derivation function but as entropy-pool based password generation function. The main reason to use PBKDF2 instead of just using a hash function was the ability to select a certain output size (if you know that I screwed up with this design choice, please mail me).

Password Generation

The output of the PBKDF2 is a binary array. In javascript, handling these is a bit painful - javascript only supports binary operations on 32 bit signed integers, i.e. you can not have an array of uint8_t. jspwgen uses http://silentmatt.com/biginteger/ to convert the array of signed integers into a big integer while accounting for cases where only a certain part of the 32 bits of an integer is actually used (e.g. in 72 bit passwords, which consist of two 32 bit integers and 8 bits of a third 32 bit integer). Interestingly, on 64 bit platforms, when the binary array is not a multiple of 32 bits in length, the remaining bits are represented as 64 bit integer (at least in firefox). Is this standard?

Once the array has been converted to a big integer, this integer is used to generate the indices of arrays of a certain size (e.g. the array of all printable ascii characters, or the array of english words). This is done by a typical basis conversion (check the code and mail me if I screwed it up). The elements at the indices are then used for the password. And there, you have your password.

© 2010-2021 Stefan Birgmeier
sbirgmeier@21er.org