Getting to grips with pwdhash
It actually took me a good few minutes to get up and running with this environment. It seems that hashed-password.js relies on a variable, SPH_kPasswordPrefix, being defined. Although defined in pwdhash.js, it turns out to be our only dependency on that file. In wanting to keep the environment as simple as possible, I chose to copy the definition of that variable to hashed-password.js, allowing us to remain independent of pwdhash.js.
The very first thing that a newly created SPH_HashedPassword does, is to use the b64_hmac_md5 function, supplied in the md5.js library, to generate the initial hash. I really didn’t expect to be tripped up quite this early on, but I just couldn’t replicate the value obtained from b64_hmac_md5 (using digest/md5 and ruby-hmac.) I now realise that I didn’t fully understand what I was actually trying to replicate - I really should have paid more attention to those three little letters (b64 / base64) at the beginning of the function name…
Back to the drawing-board - starting to implement the md5.js library in ruby
class Integer def zero_fill_right_shift(count) (self >> count) & ((2 ** (size_in_bits-count))-1) # defined in Integer, size_in_bits is self.size * 8 end end
Back to the drawing-board (again) - using digest/md5 and ruby-hmac to replicate the results given by md5.js
Next up was a comparison of an HMAC MD5 of a key and some data.
Although we can easily replicate the result of hex_hmac_md5 in ruby, we actually want to replicate the result of b64_hmac_md5 (i.e. the hashed data encoded in base64 representation.)
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
Ok, so it seems I should just be able to remove the pad characters (=) from my generated hash and I’ll achieve the same value. Happy that this was a pretty easy difference to resolve, I could move forward with actually implementing the pwdhash magic in ruby…
Implementing the pwdhash magic in ruby
The pwdhash specific stuff is actually very simple. It alters the initial base 64 encoded hash to ensure that we have:
- One upper case char
- One lower case char
- One non alpha-numeric char
- One number
Investigate the whole world of unicode a bit more - I definitely don’t know as much about it as I should.