Nick Sabalausky Posted in reply to Casey



Permalink Reply

On 8/30/2014 10:19 AM, Casey wrote: > Would it be possible to support bcrypt style password hashing? The one > key feature of it is that it encrypts/hashes the password many times to > slow down brute force attacks. Hash algorithms are too fast to prevent > this. DAuth is designed for everything to be overridable. So, while specific support for that is not currently built-in (I definitely want to add it though), you should still be able to use it. The best way to do it: All you need is a Phobos-compatible std.digest.digest[1]-style interface for bcrypt. You can wrap an existing bcrypt algorithm like this: [1] http:// dlang.org/ phobos/ std_digest_ digest.html ------------------------------ import your_ordinary_bcrypt_module; import std.digest.digest : WrapperDigest; // Phobos's "template"-style struct BCrypt { // Implement Phobos digest interface: void start(); void put(scope const(ubyte)[] data...); ubyte[SIZE_OF_YOUR_FINAL_HASH] finish(); } // Phobos's OO-style alias BCryptDigest = WrapperDigest!BCrypt; ------------------------------ The easiest way (although maybe not most flexible) to handle BCrypt's "number of rounds" parameter would be to make it a compile-time argument for your struct, and then make a few convenience aliases: ------------------------------ struct BCrypt(int numRounds) {...} alias BCrypt256 = BCrypt!256; alias BCrypt1024 = BCrypt!1024; // Or whatever ------------------------------ Now you have a BCrypt that's fully compatible with Phobos's digest system. If you don't care about DAuth's hash string format, then that's all you need: Just pass the in digest to any function which takes it: ------------------------------ // Generate password hash Hash!Bcrypt256 hash = makeHash!Bcrypt256(pass); ubyte[] mySalt = hash.salt; ubyte[SIZE_OF_YOUR_FINAL_HASH] myHash = hash.hash; saveItAllToYourDB(mySalt, myHash); // Test password against a hash Hash!Bcrypt256 someHash; someHash.salt = mySalt; someHash.hash = myHash; bool accessGranted = isSameHash!Bcrypt256(pass, someHash); ------------------------------ To fully integrate it with DAuth and be able to use DAuth's hash strings, you can pass Hash.toString() and parseHash() with custom callbacks for 'digestCodeOfObj' and 'digestFromDAuthCode'. Examples of this are provided in the API reference: http:// semitwist.com/ dauth/ dauth/core/ Hash.toS tring.html http:// semitwist.com/ dauth/ dauth/core/ parseHash.html If you don't want to create a Phobos-style digest wrapper for bcrypt, you don't have to. You can just bypass makeHash/isSameHash entirely: ------------------------------ import dauth; import std.digest.digest; import your_bcrypt_lib; auto makeBCryptHash(Password pass, int rounds) { // Digest is std.digest.digest.Digest, an OO interface Digest dummyDigest; Hash!Digest hash; hash.salt = randomSalt(); hash.hash = do_the_bcrypt(h.salt ~ pass.data, rounds); return hash; } bool isSameBCryptHash(Password pass, int rounds, Hash!Digest hash) { ubyte[] testHash = do_the_bcrypt(hash.salt ~ pass.data, rounds); return lengthConstantEquals(testHash, hash.hash); } // Don't use Hash.toString() or parseHash() since you opted // not to create a Phobos-style digest. Instead, just // save/load hash.salt and hash.hash manually. ------------------------------ I really should add a tutorial page to the official docs showing all this "how to add support for another hashing algorithm".