Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
36 / 36 |
|
100.00% |
7 / 7 |
CRAP | |
100.00% |
1 / 1 |
GenericHash | |
100.00% |
36 / 36 |
|
100.00% |
7 / 7 |
12 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
validateKey | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
validateHashLength | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
signature | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
verify | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
makeHash | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
makeKey | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php declare(strict_types=1); |
2 | /* |
3 | * This file is part of Aplus Framework Crypto Library. |
4 | * |
5 | * (c) Natan Felles <natanfelles@gmail.com> |
6 | * |
7 | * For the full copyright and license information, please view the LICENSE |
8 | * file that was distributed with this source code. |
9 | */ |
10 | namespace Framework\Crypto; |
11 | |
12 | use LengthException; |
13 | use RangeException; |
14 | use SensitiveParameter; |
15 | use SodiumException; |
16 | |
17 | /** |
18 | * Class GenericHash. |
19 | * |
20 | * @package crypto |
21 | */ |
22 | class GenericHash |
23 | { |
24 | protected string $key; |
25 | protected int $hashLength = \SODIUM_CRYPTO_GENERICHASH_BYTES; |
26 | |
27 | /** |
28 | * GenericHash constructor. |
29 | * |
30 | * @param string $key |
31 | * @param int $hashLength |
32 | * |
33 | * @see GenericHash::makeKey() |
34 | * |
35 | * @throws LengthException if key length is not between 16 and 64 |
36 | * @throws RangeException if the hashLength value is not in the range 16 to 64 |
37 | */ |
38 | public function __construct( |
39 | #[SensitiveParameter] string $key, |
40 | int $hashLength = \SODIUM_CRYPTO_GENERICHASH_BYTES |
41 | ) { |
42 | $this->validateKey($key); |
43 | $this->validateHashLength($hashLength); |
44 | $this->key = $key; |
45 | $this->hashLength = $hashLength; |
46 | } |
47 | |
48 | /** |
49 | * Validates a key. |
50 | * |
51 | * @param string $key |
52 | * |
53 | * @throws LengthException if key length is not between 16 and 64 |
54 | */ |
55 | protected function validateKey(#[SensitiveParameter] string $key) : void |
56 | { |
57 | $length = \mb_strlen($key, '8bit'); |
58 | if ($length < \SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MIN |
59 | || $length > \SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MAX |
60 | ) { |
61 | throw new LengthException( |
62 | 'GenericHash key must have a length between ' |
63 | . \SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MIN . ' and ' |
64 | . \SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MAX . ', ' |
65 | . $length . ' given' |
66 | ); |
67 | } |
68 | } |
69 | |
70 | /** |
71 | * Validates a hash length. |
72 | * |
73 | * @param int $length |
74 | * |
75 | * @throws RangeException if the length value is not in the range 16 to 64 |
76 | */ |
77 | protected function validateHashLength(int $length) : void |
78 | { |
79 | if ($length < \SODIUM_CRYPTO_GENERICHASH_BYTES_MIN |
80 | || $length > \SODIUM_CRYPTO_GENERICHASH_BYTES_MAX |
81 | ) { |
82 | throw new RangeException( |
83 | 'Hash length must be a value between ' . \SODIUM_CRYPTO_GENERICHASH_BYTES_MIN |
84 | . ' and ' . \SODIUM_CRYPTO_GENERICHASH_BYTES_MAX . ', ' |
85 | . $length . ' given' |
86 | ); |
87 | } |
88 | } |
89 | |
90 | /** |
91 | * Gets a message signature. |
92 | * |
93 | * @param string $message |
94 | * @param int|null $hashLength A custom hash length or null to use the length set in |
95 | * the constructor |
96 | * |
97 | * @throws RangeException if the hashLength is set and is not in the range 16 to 64 |
98 | * @throws SodiumException |
99 | * |
100 | * @return string |
101 | */ |
102 | public function signature( |
103 | #[SensitiveParameter] string $message, |
104 | int $hashLength = null |
105 | ) : string { |
106 | return Utils::bin2base64( |
107 | $this->makeHash($message, $hashLength), |
108 | \SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING |
109 | ); |
110 | } |
111 | |
112 | /** |
113 | * Verifies if a message matches a signature. |
114 | * |
115 | * @param string $message |
116 | * @param string $signature |
117 | * @param int|null $hashLength A custom hash length or null to use the length set in |
118 | * the constructor |
119 | * |
120 | * @throws RangeException if the hashLength is set and is not in the range 16 to 64 |
121 | * @throws SodiumException |
122 | * |
123 | * @return bool |
124 | */ |
125 | public function verify( |
126 | #[SensitiveParameter] string $message, |
127 | #[SensitiveParameter] string $signature, |
128 | int $hashLength = null |
129 | ) : bool { |
130 | return \hash_equals( |
131 | $this->makeHash($message, $hashLength), |
132 | Utils::base642bin($signature, \SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING) |
133 | ); |
134 | } |
135 | |
136 | /** |
137 | * Makes a hash to a message. |
138 | * |
139 | * @param string $message |
140 | * @param int|null $length A custom length or null to use the length set in |
141 | * the constructor |
142 | * |
143 | * @throws RangeException if the length is set and is not in the range 16 to 64 |
144 | * @throws SodiumException |
145 | * |
146 | * @return string |
147 | */ |
148 | protected function makeHash(#[SensitiveParameter] string $message, int $length = null) : string |
149 | { |
150 | if ($length !== null) { |
151 | $this->validateHashLength($length); |
152 | } |
153 | return \sodium_crypto_generichash( |
154 | $message, |
155 | $this->key, |
156 | $length ?? $this->hashLength |
157 | ); |
158 | } |
159 | |
160 | /** |
161 | * Makes a key. |
162 | * |
163 | * @return string |
164 | */ |
165 | public static function makeKey() : string |
166 | { |
167 | return \sodium_crypto_generichash_keygen(); |
168 | } |
169 | } |