Cyr
Cyr

Reputation: 77

PHP crypt() to Qt

I'm currently learning Qt5 for GUI applications. My first project is to make an authentication script and connect it to our database.

The problem is the password column is populated by PHP's crypt(), which generates a hash string that starts with $1$.

(e.g. echo crypt("password"); prints $1$d41.iA3.$XfuFXpCJfxSduzidGnKBR0

How do I use Qt to compare the password inputted in my GUI application to the password column in database that's generated by crypt()?

Upvotes: 0

Views: 372

Answers (1)

Simon Warta
Simon Warta

Reputation: 11418

TL;DR: Use a key derivation function for password storing.


PHP's crypt() is horrible to use from other programming languages, because

  • it uses a strange way to mix salt and password, instead standard HMAC
  • it has it's own base64 alphabet that needs to be re-implemented

Apart from that you're using plain md5 as a password hash algorithm. Never use md5 for password hashing. Read more. And more. And more.


But let's get our handy dirty then.

  1. The structure of crypt()s output is the following: enter image description here where algorithm $1$ with no algorithm options means MD5.
  2. The hashed password is base64 encoded using a custom alphabet. This can be archived by replacing the output of QByteArray::toBase64() with PHPs alphabet ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz or re-implementing base64 if performance matters.
  3. As seen in the md5_crypt.c implementation, max. 8 chars of the salt are used (d41.iA3. is your case).
  4. Then input for md5 is constructed as foo = password || $1$ || salt where || is the string concatenation. Use QByteArray as type for foo.
  5. Calculae md5(password || salt || password) and call it bar.
  6. Set bar = '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
  7. Cut length(password) bytes from bar (in binary representation) and append it to foo. Repeat bar as often as needed if length(password) > 16.
  8. Uff, let me quote the original source Then something really weird...

    for (j = 0, i = length(password); i; i >>= 1)
        if (i & 1)
            foo += bar[j]
        else
            foo += password[j]
    

    which I hope i ready properly from the source.

  9. Run md5 on QByteArray: bar = md5(foo).
  10. Do that

    for (i = 0; i < 1000; i++) {
        moo = ""
        if (i & 1) {
            moo += password
        }
        else {
            moo += bar
        }
        if (i % 3) {
            moo += salt
        }
        if (i % 7) {
            moo += password
        }
        if (i & 1) {
            moo += bar
        }
        else {
            moo += password
        }
        bar = md5(moo)
    }
    
  11. Glue everything together: $1$ || salt || $ || base64(bar).

Upvotes: 4

Related Questions