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);
∎