The security argument for PBKDF2 is better than for your proposal. This doesn't mean that your proposal is any less secure, just that PBKDF2 is easier to justify.
This is because PBKDF2 incorporates three additional ideas beyond just your iteration proposal. First, PBKDF2 explicitly incorporates a salt, while your example doesn't. Even if you implicitly assumed that key
would be something like salt + password
, a password scrambler really should take password and salt as separate arguments.
Second, PBKDF2 iterates not a public hash function like SHA-256, but a pseudorandom function ("PRF") like HMAC-SHA-256, keyed with the password. This is a very subtle theoretical point, but what this means is that PBKDF can work with functions that are weaker than a full-blown hash function like SHA-256.
Third, PBKDF2 doesn't just chain the outputs of the PRF, but also XORs its outputs. This is a (perhaps paranoid) safeguard against weak PRFs that have short cycles when applied to their own outputs.
PBKDF2 is actually pretty simple. It's simple enough that laypersons are unlikely to mess it up. If your language and library have SHA-256 they likely have HMAC-SHA-256 as well, but if not that's easy to implement as well. So you really ought to just give it a shot—it's better to stick to standard, well-tested techniques than to cook up your own.
Final note: the output of a hash function is binary data, and really should be treated as so. That is, your use of hexdigest
in your example is wasteful and not conducive to good quality code; data should be processed in its native type as much as possible! (Using hex digests would also give you an incorrect PBKDF2 implementation.)