Laravel: Implementing a CRYPT_STD_DES 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_STD_DES hashing function. Before we write the code for the CRYPT_STD_DES implementation, we will examine each method that the Hasher interface requires, as well as think about the options our hasher might utilize:

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

The make method is responsible for doing the actual hashing. It also accepts an $options array. We will allow a 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 $hashedValue. We will not need any options for this method.

Internally, we will use the make method and PHP's hash_equals 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.

The following is the complete implementation of the StandardDesHasher class:

StandardDesHasher Option Description
salt Users can supply their own two character salt to the make and needsRehash methods. If no salt is supplied to the make method, one will be generated.
1<?php
2 
3namespace Laravel\Artisan\Hashing;
4 
5use RuntimeException;
6use Illuminate\Contracts\Hashing\Hasher as HasherContract;
7use Illuminate\Support\Str;
8 
9class StandardDesHasher 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']) ? $options['salt'] : Str::random(2);
28 
29 $hash = crypt($value, $salt);
30 
31 if ($hash == self::FAILED_HASH) {
32 // Throw an exception because the hashing failed.
33 throw new RuntimeException('DES hashing failed.');
34 }
35 
36 return $hash;
37 }
38 
39 /**
40 * Check the given plain value against a hash.
41 *
42 * @param string $value
43 * @param string $hashedValue
44 * @param array $options
45 * @return bool
46 */
47 public function check($value, $hashedValue, array $options = [])
48 {
49 $userValue = $this->make($value, ['salt' => $hashedValue]);
50 
51 return hash_equals($hashedValue, $userValue);
52 }
53 
54 /**
55 * Check if the given hash has been hashed using the given options.
56 *
57 * @param string $hashedValue
58 * @param array $options
59 * @return bool
60 */
61 public function needsRehash($hashedValue, array $options = [])
62 {
63 if (!isset($options['salt'])) {
64 return false;
65 }
66 
67 return (mb_substr($hashedValue, 0, 2)
68 !== mb_substr($options['salt'], 0, 2));
69 }
70 
71}

The following sample demonstrates the usage of the custom StandardDesHasher hasher implementation:

1<?php
2 
3use Laravel\Artisan\StandardDesHasher;
4 
5// Create a new instance of StandardDesHasher
6$hasher = new StandardDesHasher;
7 
8// Calculate a hash, randomly generated salt. Output
9// of this method call should change each time.
10$randomSalt = $hasher->make('test');
11 
12// Calculate a hash, with a known salt. Should always
13// return 'teH0wLIpW0gyQ'
14$suppliedSalt = $hasher->make('test', ['salt' => 'te']);
15 
16// Check if 'test' matches the hash.
17//
18// true
19$isValid = $hasher->check('test', $randomSalt);
20 
21// Check if 'test' matches the hash.
22//
23// true
24$isValid = $hasher->check('test', $suppliedSalt);
25 
26// Check if $randomSalt need to be rehashed. This will most likely
27// always return true, but it is possible it will return false.
28$needsRehash = $hasher->needsRehash(
29 $randomSalt,
30 ['salt' => 'te']
31);
32 
33// Check if the $suppliedSalt needs to be rehashed. This will always
34// return false, unless the salt option is changed.
35$needsRehash = $hasher->needsRehash(
36 $suppliedSalt,
37 ['salt' => 'te']
38);

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.