changeOutputFormat()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 21
ccs 17
cts 17
cp 1
rs 9.3888
cc 5
nc 5
nop 1
crap 5
1
<?php
2
3
/**
4
 * Abstraction for the strong/slow digestion algorithm objects that are resistant to hardware computational attacks.
5
 */
6
7
namespace CryptoManana\Core\Abstractions\MessageDigestion;
8
9
use CryptoManana\Core\Abstractions\MessageDigestion\AbstractPasswordBasedDerivationFunction as PasswordDerivation;
10
use CryptoManana\Core\StringBuilder as StringBuilder;
11
12
/**
13
 * Class AbstractHardwareResistantDerivation - The hardware resistant digestion algorithm abstraction representation.
14
 *
15
 * @package CryptoManana\Core\Abstractions\MessageDigestion
16
 */
17
abstract class AbstractHardwareResistantDerivation extends PasswordDerivation
18
{
19
    /**
20
     * The internal maximum length in bytes of the raw output digest for the algorithm.
21
     */
22
    const ALGORITHM_MAXIMUM_OUTPUT = PHP_INT_MAX;
23
24
    /**
25
     * The digest output format property storage.
26
     *
27
     * @var int The output format integer code value.
28
     */
29
    protected $digestFormat = self::DIGEST_OUTPUT_RAW;
30
31
    /**
32
     * Internal method for converting the digest's output format representation via the chosen format.
33
     *
34
     * @param string $digest The output digest.
35
     *
36
     * @return string The input data with proper salting.
37
     */
38 17
    protected function changeOutputFormat($digest)
39
    {
40 17
        switch ($this->digestFormat) {
41 17
            case self::DIGEST_OUTPUT_HEX_LOWER:
42 4
                $digest = bin2hex($digest);
43 4
                break;
44 15
            case self::DIGEST_OUTPUT_HEX_UPPER:
45 2
                $digest = StringBuilder::stringToUpper(bin2hex($digest));
46 2
                break;
47 15
            case self::DIGEST_OUTPUT_BASE_64:
48 2
                $digest = base64_encode($digest);
49 2
                break;
50 15
            case self::DIGEST_OUTPUT_BASE_64_URL:
51 2
                $digest = base64_encode($digest);
52 2
                $digest = StringBuilder::stringReplace(['+', '/', '='], ['-', '_', ''], $digest);
53 2
                break;
54
            default: // case self::DIGEST_OUTPUT_RAW:
55 15
                break;
56
        }
57
58 17
        return $digest;
59
    }
60
61
    /**
62
     * Internal method for converting a formatted digest to raw bytes.
63
     *
64
     * @param string $digest The digest string.
65
     *
66
     * @return string The raw bytes digest representation.
67
     */
68 7
    protected function convertFormattedDigest($digest)
69
    {
70 7
        $hexCasePattern = '/^[a-f0-9]+$/';
71 7
        $base64Pattern = '%^[a-zA-Z0-9/+]*={0,2}$%';
72 7
        $base64UrlFriendlyPattern = '/^[a-zA-Z0-9_-]+$/';
73
74 7
        if (preg_match($hexCasePattern, StringBuilder::stringToLower($digest))) {
75 2
            $digest = hex2bin(StringBuilder::stringToLower($digest));
76 7
        } elseif (preg_match($base64Pattern, $digest) && StringBuilder::stringLength($digest) % 4 === 0) {
77 2
            $digest = base64_decode($digest);
78 7
        } elseif (preg_match($base64UrlFriendlyPattern, $digest)) {
79 2
            $digest = StringBuilder::stringReplace(['-', '_'], ['+', '/'], $digest);
80 2
            $digest .= str_repeat('=', StringBuilder::stringLength($digest) % 4);
81 2
            $digest = base64_decode($digest);
82
        }
83
84 7
        return $digest;
85
    }
86
87
    /**
88
     * Fetch the correctly formatted internal variation for digestion.
89
     *
90
     * @return int|string The chosen variation for password hashing.
91
     */
92
    abstract protected function fetchAlgorithmVariation();
93
94
    /**
95
     * Fetch the correctly formatted internal parameters for digestion.
96
     *
97
     * @return array The chosen parameters for password hashing.
98
     */
99
    abstract protected function fetchAlgorithmParameters();
100
101
    /**
102
     * Password-based key derivation algorithm constructor.
103
     */
104 39
    public function __construct()
105
    {
106 39
        parent::__construct();
107
    }
108
109
    /**
110
     * Get debug information for the class instance.
111
     *
112
     * @return array Debug information.
113
     */
114 2
    public function __debugInfo()
115
    {
116 2
        return [
117 2
            'standard' => static::ALGORITHM_NAME,
118 2
            'type' => 'key stretching or password-based key derivation',
119 2
            'salt' => $this->salt,
120 2
            'mode' => $this->saltingMode,
121 2
            'algorithm variation version' => $this->fetchAlgorithmVariation(),
122 2
            'digestion parameters' => $this->fetchAlgorithmParameters(),
123 2
        ];
124
    }
125
126
    /**
127
     * Calculates a hash value for the given data.
128
     *
129
     * @param string $data The input string.
130
     *
131
     * @return string The digest.
132
     * @throws \Exception Validation errors.
133
     */
134 19
    public function hashData($data)
135
    {
136 19
        if (!is_string($data)) {
137 2
            throw new \InvalidArgumentException('The data for hashing must be a string or a binary string.');
138
        }
139
140 17
        $data = $this->addSaltString($data);
141
142 17
        $digest = password_hash($data, $this->fetchAlgorithmVariation(), $this->fetchAlgorithmParameters());
143
144 17
        $digest = $this->changeOutputFormat($digest);
145
146 17
        return $digest;
147
    }
148
149
    /**
150
     * Securely compares and verifies if a digestion value is for the given input data.
151
     *
152
     * @param string $data The input string.
153
     * @param string $digest The digest string.
154
     *
155
     * @return bool The result of the secure comparison.
156
     * @throws \Exception Validation errors.
157
     */
158 11
    public function verifyHash($data, $digest)
159
    {
160 11
        if (!is_string($data)) {
161 2
            throw new \InvalidArgumentException('The data for hashing must be a string or a binary string.');
162 9
        } elseif (!is_string($digest)) {
163 2
            throw new \InvalidArgumentException('The digest must be a string or a binary string.');
164
        }
165
166 7
        $data = $this->addSaltString($data);
167
168 7
        $digest = $this->convertFormattedDigest($digest);
169
170 7
        return password_verify($data, $digest);
171
    }
172
}
173