2

For authentification with Dovecot, I use SSHA256 hashes but I have no clue how to validate a given password against the existing hash. The following PHP functions (found them in the web) are used to create the SSHA256 hash:

function ssha256($pw) {
        $salt = make_salt();
        return "{SSHA256}" . base64_encode( hash('sha256', $pw . $salt, true ) . $salt );
}

function make_salt() {
        $len   = 4;
        $bytes = array();
        for ($i = 0; $i < $len; $i++ ) {
                $bytes[] = rand(1,255);
        }
        $salt_str = '';
        foreach ($bytes as $b) {
                $salt_str .= pack('C', $b);
        }
        return $salt_str;
}

Example output: {SSHA256}lGq49JTKmBC49AUrk7wLyQVmeZ7cGl/V13A9QbY4RVKchckL

Do I have to extract the salt, but how? I totally lost the way for solving the problem, has anyone a hint for this?

Thanks to everyone for helping!

Oh and sorry, I have to use SSHA256, because Dovecot 1.2.15 supports only those schemes: CRYPT MD5 MD5-CRYPT SHA SHA1 SHA256 SMD5 SSHA SSHA256 PLAIN CLEARTEXT CRAM-MD5 HMAC-MD5 DIGEST-MD5 PLAIN-MD4 PLAIN-MD5 LDAP-MD5 LANMAN NTLM OTP SKEY RPA

Fips
  • 53
  • 6
  • 1
    You should use cryptographically secure salt and iterated hashing. Better yet, just use bcrypt, which does all of this for you. – SLaks Feb 17 '13 at 14:52

2 Answers2

2

You need to store the salt along with the hashed value.

When you need to validate the password, you simply calculate the hash again with the user input password + the stored salt. If the hashes match, the user entered the correct password.

For your format, use base64_decode first, the last 4 bytes of the result will be the salt.

driis
  • 161,458
  • 45
  • 265
  • 341
  • Hi, thanks for your quick answer! The salt is actually stored with the hash, but I don't know how to get it out of the raw format packed in Base64. – Fips Feb 17 '13 at 15:02
  • Look, the salt ist appended to the hash (raw output) and then the whole thing gets encoded with Base64. When I try to use base64_decode I get binary data but I don't know what to do with it. – Fips Feb 17 '13 at 15:09
  • Last 4 bytes of your binary data is the salt, the rest is the hash. – driis Feb 17 '13 at 15:14
  • More specifically, the LDAP-style salt is all bytes that are not the hash. It can be any length: https://issues.apache.org/jira/browse/DIRSERVER-1375 – James May 10 '13 at 15:22
2

You should not be using the SHA family for password hashing. They are fast and designed for hashing files at speed. You need pashword hashing to be expensive. Use bcrypt, PHPass or just use this class, which I rolled myself (but not until you learn to pick holes in it):

class PassHash {
    public static function rand_str($length) {
        $total = $length % 2;
        $output = "";
        if ($total !== 0) {
            $count = floor($length / 2);
            $output .= ".";
        } else $count = $length / 2;

        $bytes = openssl_random_pseudo_bytes($count);
        $output .= bin2hex($bytes);

        // warning: prepending with a dot if the length is odd.
        // this can be very dangerous. no clue why you'd want your
        // bcrypt salt to do this, but /shrug

        return $output;
    }
    // 2y is an exploit fix, and an improvement over 2a. Only available in 5.4.0+
    public static function hash($input) {
        return crypt($input, "$2y$13$" . self::rand_str(22));

    }

    // legacy support, add exception handling and fall back to <= 5.3.0
    public static function hash_weak($input) {
        return crypt($input, "$2a$13$" . self::rand_str(22));
    }

    public static function compare($input, $hash) {
        return (crypt($input, $hash) === $hash);
    }
}

You have to hash the plaintext given and compare that hash against one you have stored. The salts are stored in the hashes, and should be random. If you like, add a pepper. You should also make the workrate variable, so that you can change the workrate at any moment when needed and still have your system work.


If, like you say, you have no way of implementing this, you can unpack the hash as follows:

function unpack_hash($hash) {
        $hash = base64_decode($hash);
        $split = str_split($hash, 64);
        return array("salt" => $split[1], "hash" => $split[0]);

This is because SHA256 is 256 bits, or 64 hex characters. You can just always assume the first 64 chars are the hash

Amelia
  • 2,967
  • 2
  • 24
  • 39
  • Hi, thank you very much for your answer! I'm using Bcrypt for all my scripts but unfortunately Dovecot 1.2 doesn't support it. These are the supported schemes: CRYPT MD5 MD5-CRYPT SHA SHA1 SHA256 SMD5 SSHA SSHA256 PLAIN CLEARTEXT CRAM-MD5 HMAC-MD5 DIGEST-MD5 PLAIN-MD4 PLAIN-MD5 LDAP-MD5 LANMAN NTLM OTP SKEY RPA – Fips Feb 17 '13 at 15:03
  • Ouch. No chance of being able to add support in? – Amelia Feb 17 '13 at 15:04
  • 1
    Thank you so much for your help! According to [this question](http://stackoverflow.com/questions/4099333/how-to-generate-a-good-salt-is-my-function-secure-enough) I replaced the salt by `uniqid()`. Oh and I had to remove the Brackets: `str_replace('{SSHA256}', '', $hash);` Thank you! – Fips Feb 17 '13 at 16:01
  • 1
    @Fips - I found a hint in the [dovecot documentation](http://wiki.dovecot.org/Authentication/PasswordSchemes#SHA256_and_SHA512_in_libc), that iterated hashing is supported in some cases via the CRYPT scheme. If your system supports this, you should really use this scheme. About the salt, a cryptographic strong salt should come from the OS random source, and is therefore unique and unpredictable, `uniqid()` on the other side is more or less a timestamp. – martinstoeckli Feb 17 '13 at 21:22
  • Thank you, I'll have a look on it and for now I will change the salt. – Fips Feb 22 '13 at 16:36