I recently read about the bug in Django with regards to PBKDF2 causing Denial of Service with large passwords:
This is because PBKDF2 "mixes" in the input password with each iteration, therefore amplifying the effect of a large input. Django originally solved this by setting a password character limit of 4096 bytes.
I tried to replicate this in Go, only to find that their implementation isn't vulnerable. This is because they HMAC the input password before performing the iteration loops, hence the iterations are always on a constant size input. Here's the source in question:
I then found that the Django "fix" which set a maximum input size was reversed, and they instead fixed it by implementing a similar solution to Go:
This is baffling to me. Does the PBKDF2 spec say that it's supposed to be HMACed first? As far as I can tell, it doesn't. Does that mean that the Go and current Django implementations are non-compliant to the spec? Was the previous Django implementation more correct, but they've modified PBKDF2 to fix the DoS issue?
The OpenBSD implementation doesn't seem to perform the initial HMAC either: http://bxr.su/OpenBSD/lib/libutil/pkcs5_pbkdf2.c
But the PHP implementation does: https://github.com/php/php-src/blob/1c295d4a9ac78fcc2f77d6695987598bb7abcb83/ext/hash/hash.c#L600-L723
Why does there seem to be no standardization in this regard? Which is the "correct" implementation?
PS: I found this discussion regarding it, which came to the conclusion that PBKDF2 is not meant to be HMACed first, and that a "user friendly" wrapper should perform the HMAC before handing to the PBKDF2 algo: https://github.com/defuse/php-encryption/issues/230
That makes sense to me based on my reading of the RFC. Is that correct?