John WH Smith
John WH Smith

Reputation: 2773

Salted sha512 in C, cannot synchronise with Symfony2's FOSUserBundle

My developement is separated into two components :

Some information about my environment

The problem itself

My problem occurs in the authentication module : I'm unable to get the same hash as the one produced by Symfony's FOSUserBundle. Here's my example :

With this information, Symfony stores this final hash :

fH5vVoACB4e8h1GX81n+aYiRkSWxeu4TmDibNChtLNZS3jmFKBZijGCXcfzCSJFg+YvNthxefHOBk65m/U+3OA==

Now, in my C authentication module, I run this piece of code (crypt.h is included) :

char* password = "test";
char* salt = "$6$bcccy6eiye8kg44scw0wk8g4g0wc0sk";

char* hash = malloc(256);
memset(hash, 0, 256);

encode64(crypt(password, salt), hash, strlen(password));
fprintf(stdout, "%s\n", hash);

(here is my base64 encoder : http://libremail.tuxfamily.org/sources/base64-c.htm)

And this outputs...

JDYkYg==

Which is completely different from my Symfony2 hash.

Browsing Stack Overflow, I found this question (Symfony2 (FOSUserBundle) SHA512 hash doesn't match C# SHA512 hash) written by someone encountering the same issue (with C# though). So I decided to run this test...

char* password = "test{bcccy6eiye8kg44scw0wk8g4g0wc0sk}";
char* salt = "$6$bcccy6eiye8kg44scw0wk8g4g0wc0sk"; // I tried without salt, or with "$6$" as well.

char* hash = malloc(256);
memset(hash, 0, 256);

encode64(crypt(password, salt), hash, strlen(password));
fprintf(stdout, "%s\n", hash);

Of course, it was a complete failure, I got :

JDYkYmNjY3k2ZWl5ZThrZzQ0cyRycmN6TnpJUXFOYU1VRlZvMA==

I've tried mixing the password and the salt in various ways, but I could never get the Symfony's salt in the authentication module. Is there something I've missed on the way ? Have I misunderstood the way Symfony's FOSUserBundle stores passwords ?

Upvotes: 2

Views: 1354

Answers (1)

Cerad
Cerad

Reputation: 48865

Not really an answer but I'm guessing you have not looked into how Symfony encodes passwords in any great detail? The encoding process is tucked away into an encoder object. For SHA512 we use:

namespace Symfony\Component\Security\Core\Encoder;

class MessageDigestPasswordEncoder extends BasePasswordEncoder
{
/**
 * Constructor.
 *
 * @param string  $algorithm          The digest algorithm to use
 * @param Boolean $encodeHashAsBase64 Whether to base64 encode the password hash
 * @param integer $iterations         The number of iterations to use to stretch the password hash
 */
public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 5000)
{
    $this->algorithm = $algorithm;
    $this->encodeHashAsBase64 = $encodeHashAsBase64;
    $this->iterations = $iterations;
}
public function encodePassword($raw, $salt)
{
    if (!in_array($this->algorithm, hash_algos(), true)) {
        throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
    }

    $salted = $this->mergePasswordAndSalt($raw, $salt);
    $digest = hash($this->algorithm, $salted, true);

    // "stretch" hash
    for ($i = 1; $i < $this->iterations; $i++) {
        $digest = hash($this->algorithm, $digest.$salted, true);
    }

    return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
}
public function isPasswordValid($encoded, $raw, $salt)
{
    return $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
}
protected function mergePasswordAndSalt($password, $salt)
{
    if (empty($salt)) {
        return $password;
    }

    if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) {
        throw new \InvalidArgumentException('Cannot use { or } in salt.');
    }

    return $password.'{'.$salt.'}';
}

As you can see, one immediate problem is that hashing is repeated 5000 times by default. This (as well as the other inputs can all be adjusted in you app/config/security.yml file).

You can also see where the salt and password get merged together. Which explains the other stackoverflow answer.

It would be trivial to make a symfony command to just run this encoding algorithm from the symfony console for testing. After that is just a question of adjusting the inputs or tweaking your C code until the results match.

If you are lucky then all your will have to do is add the iteration loop.

Upvotes: 4

Related Questions