Laravel: Implementing a MD5 Hasher

November 30, 2016 —John Koster

In this article we will create an implementation of Illuminate\Contracts\Hashing\Hasher using PHP's crypt function and the CRYPT_MD5 hashing function. Like in the previous sections, we will examine each method before looking at the full implementation.

make($value, array $options = [])

The make method is responsible for doing the actual hashing. It also accepts an $options array. We will allow an eight character long salt to be supplied using the options array. If no salt is supplied, we will generate one.

For the actual hashing, we will make a call to PHP's crypt function.

public function check($value, $hashedValue, array $options = [])

The check method is used to check that a known $value is the same as a given $hashedValue. We will not need any options for this method.

For the actual hashing, we will use PHP's crypt function.

public function needsRehash($hashedValue, array $options = [])

The needsRehash method is used to determine if a given hash needs to be updated, or rehashed. We will accept a salt as an option. If the salt from the $options array does not match the salt from the $hashedValue the $hashedValue needs to be rehashed.

MD5Hasher Options Description
salt A salt can be supplied to the make and needsRehash methods. If no salt is supplied to the make method, one will be generated.

The following is the complete implementation of the Md5Hasher class:

1<?php
2 
3namespace Laravel\Artisan\Hashing;
4 
5use RuntimeException;
6use Illuminate\Contracts\Hashing\Hasher as HasherContract;
7use Illuminate\Support\Str;
8 
9class Md5Hasher implements HasherContract
10{
11 
12 const FAILED_HASH = '*0';
13 
14 /**
15 * Hash the given value.
16 *
17 * @param string $value
18 * @param array $options
19 * @return string
20 *
21 * @throws \RuntimeException
22 */
23 public function make($value, array $options = [])
24 {
25 // If the user supplied a salt, use that. If not we
26 // can generate it using the Str helper methods.
27 $salt = isset($options['salt']) ?
28 $options['salt'] : Str::random(8);
29 
30 $salt = '$1$'.$salt.'$';
31 
32 $hash = crypt($value, $salt);
33 
34 if ($hash == self::FAILED_HASH) {
35 // Throw an exception because the hashing failed.
36 throw new RuntimeException('MD5 hashing failed.');
37 }
38 
39 return $hash;
40 }
41 
42 /**
43 * Check the given plain value against a hash.
44 *
45 * @param string $value
46 * @param string $hashedValue
47 * @param array $options
48 * @return bool
49 */
50 public function check($value, $hashedValue, array $options = [])
51 {
52 // Let's get the salt from the $hashedValue so we can hash
53 // $value using the same exact salt as $hashedValue.
54 $salt = mb_substr($hashedValue, 3, 8);
55 $userValue = $this->make($value, ['salt' => $salt]);
56 
57 return hash_equals($hashedValue, $userValue);
58 }
59 
60 /**
61 * Check if the given hash has been hashed using the given options.
62 *
63 * @param string $hashedValue
64 * @param array $options
65 * @return bool
66 */
67 public function needsRehash($hashedValue, array $options = [])
68 {
69 if (!isset($options['salt'])) {
70 return false;
71 }
72 
73 return (substr($hashedValue, 3, 8) !== $options['salt']);
74 }
75 
76}

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.