| 1 |
lars |
1 |
<?php
|
|
|
2 |
/**
|
|
|
3 |
* Implementation of the Hashed Message Authentication Code algorithm with
|
|
|
4 |
* support for a wide range of hashing algorithms available using either of
|
|
|
5 |
* the "hash" or "mhash" extensions, or the native md5() and sha1() functions.
|
|
|
6 |
*
|
|
|
7 |
* PHP version 5
|
|
|
8 |
*
|
|
|
9 |
* LICENSE:
|
|
|
10 |
*
|
|
|
11 |
* Copyright (c) 2005-2007, Pádraic Brady <padraic.brady@yahoo.com>
|
|
|
12 |
* All rights reserved.
|
|
|
13 |
*
|
|
|
14 |
* Redistribution and use in source and binary forms, with or without
|
|
|
15 |
* modification, are permitted provided that the following conditions
|
|
|
16 |
* are met:
|
|
|
17 |
*
|
|
|
18 |
* * Redistributions of source code must retain the above copyright
|
|
|
19 |
* notice, this list of conditions and the following disclaimer.
|
|
|
20 |
* * Redistributions in binary form must reproduce the above copyright
|
|
|
21 |
* notice, this list of conditions and the following disclaimer in the
|
|
|
22 |
* documentation and/or other materials provided with the distribution.
|
|
|
23 |
* * The name of the author may not be used to endorse or promote products
|
|
|
24 |
* derived from this software without specific prior written permission.
|
|
|
25 |
*
|
|
|
26 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
|
27 |
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
28 |
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
29 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
30 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
31 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
32 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
33 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
|
34 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
35 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
36 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
37 |
*
|
|
|
38 |
* @category Encryption
|
|
|
39 |
* @package Crypt_HMAC2
|
|
|
40 |
* @author Pádraic Brady <padraic.brady@yahoo.com>
|
|
|
41 |
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
|
|
42 |
* @version $Id: HMAC2.php 965 2012-02-28 07:37:41Z tiefland $
|
|
|
43 |
* @link http://
|
|
|
44 |
*/
|
|
|
45 |
|
|
|
46 |
/**
|
|
|
47 |
* Crypt_HMAC2 class
|
|
|
48 |
*
|
|
|
49 |
* Example usage:
|
|
|
50 |
* $key = str_repeat("\xaa", 20); // insecure key only for example
|
|
|
51 |
* $data = "Hello World!";
|
|
|
52 |
* $hmac = new Crypt_HMAC2($key, 'SHA256');
|
|
|
53 |
* $hmacHash = $hmac->hash($data);
|
|
|
54 |
*
|
|
|
55 |
* Supported hashing algorithms are limited by your PHP version access
|
|
|
56 |
* to the hash, mhash extensions and supported native functions like
|
|
|
57 |
* md5() and sha1().
|
|
|
58 |
*
|
|
|
59 |
* To obtain raw binary output, set the optional second parameter of
|
|
|
60 |
* Crypt_HMAC2::hash() to Crypt_HMAC2::BINARY.
|
|
|
61 |
*
|
|
|
62 |
* $hmacRawHash = $hmac->hash($data, Crypt_HMAC2::BINARY);
|
|
|
63 |
*
|
|
|
64 |
* @category Encryption
|
|
|
65 |
* @package Crypt_HMAC2
|
|
|
66 |
* @author Pádraic Brady <padraic.brady@yahoo.com>
|
|
|
67 |
* @copyright 2005-2007 Pádraic Brady
|
|
|
68 |
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
|
|
69 |
* @link http://
|
|
|
70 |
* @version @package_version@
|
|
|
71 |
* @access public
|
|
|
72 |
*/
|
|
|
73 |
class Crypt_HMAC2
|
|
|
74 |
{
|
|
|
75 |
|
|
|
76 |
/**
|
|
|
77 |
* The key to use for the hash
|
|
|
78 |
*
|
|
|
79 |
* @var string
|
|
|
80 |
*/
|
|
|
81 |
protected $_key = null;
|
|
|
82 |
|
|
|
83 |
/**
|
|
|
84 |
* pack() format to be used for current hashing method
|
|
|
85 |
*
|
|
|
86 |
* @var string
|
|
|
87 |
*/
|
|
|
88 |
protected $_packFormat = null;
|
|
|
89 |
|
|
|
90 |
/**
|
|
|
91 |
* Hashing algorithm; can be the md5/sha1 functions or any algorithm name
|
|
|
92 |
* listed in the output of PHP 5.1.2+ hash_algos().
|
|
|
93 |
*
|
|
|
94 |
* @var string
|
|
|
95 |
*/
|
|
|
96 |
protected $_hashAlgorithm = 'md5';
|
|
|
97 |
|
|
|
98 |
/**
|
|
|
99 |
* Supported direct hashing functions in PHP
|
|
|
100 |
*
|
|
|
101 |
* @var array
|
|
|
102 |
*/
|
|
|
103 |
protected $_supportedHashNativeFunctions = array(
|
|
|
104 |
'md5',
|
|
|
105 |
'sha1',
|
|
|
106 |
);
|
|
|
107 |
|
|
|
108 |
/**
|
|
|
109 |
* List of hash pack formats for each hashing algorithm supported.
|
|
|
110 |
* Only required when hash or mhash are not available, and we are
|
|
|
111 |
* using either md5() or sha1().
|
|
|
112 |
*
|
|
|
113 |
* @var array
|
|
|
114 |
*/
|
|
|
115 |
protected $_hashPackFormats = array(
|
|
|
116 |
'md5' => 'H32',
|
|
|
117 |
'sha1' => 'H40'
|
|
|
118 |
);
|
|
|
119 |
|
|
|
120 |
/**
|
|
|
121 |
* List of algorithms supported my mhash()
|
|
|
122 |
*
|
|
|
123 |
* @var array
|
|
|
124 |
*/
|
|
|
125 |
protected $_supportedMhashAlgorithms = array('adler32',' crc32', 'crc32b', 'gost',
|
|
|
126 |
'haval128', 'haval160', 'haval192', 'haval256', 'md4', 'md5', 'ripemd160',
|
|
|
127 |
'sha1', 'sha256', 'tiger', 'tiger128', 'tiger160');
|
|
|
128 |
|
|
|
129 |
/**
|
|
|
130 |
* Constants representing the output mode of the hash algorithm
|
|
|
131 |
*/
|
|
|
132 |
const STRING = 'string';
|
|
|
133 |
const BINARY = 'binary';
|
|
|
134 |
|
|
|
135 |
/**
|
|
|
136 |
* Constructor; optionally set Key and Hash at this point
|
|
|
137 |
*
|
|
|
138 |
* @param string $key
|
|
|
139 |
* @param string $hash
|
|
|
140 |
*/
|
|
|
141 |
public function __construct($key = null, $hash = null)
|
|
|
142 |
{
|
|
|
143 |
if (!is_null($key)) {
|
|
|
144 |
$this->setKey($key);
|
|
|
145 |
}
|
|
|
146 |
if (!is_null($hash)) {
|
|
|
147 |
$this->setHashAlgorithm($hash);
|
|
|
148 |
}
|
|
|
149 |
}
|
|
|
150 |
|
|
|
151 |
/**
|
|
|
152 |
* Set the key to use when hashing
|
|
|
153 |
*
|
|
|
154 |
* @param string $key
|
|
|
155 |
* @return Crypt_HMAC2
|
|
|
156 |
*/
|
|
|
157 |
public function setKey($key)
|
|
|
158 |
{
|
|
|
159 |
if (!isset($key) || empty($key)) {
|
|
|
160 |
require_once 'Crypt/HMAC2/Exception.php';
|
|
|
161 |
throw new Crypt_HMAC2_Exception('provided key is null or empty');
|
|
|
162 |
}
|
|
|
163 |
$this->_key = $key;
|
|
|
164 |
return $this;
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
/**
|
|
|
168 |
* Getter to return the currently set key
|
|
|
169 |
*
|
|
|
170 |
* @return string
|
|
|
171 |
*/
|
|
|
172 |
public function getKey()
|
|
|
173 |
{
|
|
|
174 |
if (is_null($this->_key)) {
|
|
|
175 |
require_once 'Crypt/HMAC2/Exception.php';
|
|
|
176 |
throw new Crypt_HMAC2_Exception('key has not yet been set');
|
|
|
177 |
}
|
|
|
178 |
return $this->_key;
|
|
|
179 |
}
|
|
|
180 |
|
|
|
181 |
/**
|
|
|
182 |
* Setter for the hash method. Supports md5() and sha1() functions, and if
|
|
|
183 |
* available the hashing algorithms supported by the hash() PHP5 function or
|
|
|
184 |
* the mhash extension.
|
|
|
185 |
*
|
|
|
186 |
* Since they are so many varied HMAC methods in PHP these days this method
|
|
|
187 |
* does a lot of checking to figure out what's available and not.
|
|
|
188 |
*
|
|
|
189 |
* @param string $hash
|
|
|
190 |
* @return Crypt_HMAC2
|
|
|
191 |
*/
|
|
|
192 |
public function setHashAlgorithm($hash)
|
|
|
193 |
{
|
|
|
194 |
if (!isset($hash) || empty($hash)) {
|
|
|
195 |
require_once 'Crypt/HMAC2/Exception.php';
|
|
|
196 |
throw new Crypt_HMAC2_Exception('provided hash string is null or empty');
|
|
|
197 |
}
|
|
|
198 |
$hash = strtolower($hash);
|
|
|
199 |
$hashSupported = false;
|
|
|
200 |
if (function_exists('hash_algos') && in_array($hash, hash_algos())) {
|
|
|
201 |
$hashSupported = true;
|
|
|
202 |
}
|
|
|
203 |
if ($hashSupported === false && function_exists('mhash') && in_array($hash, $this->_supportedMhashAlgorithms)) {
|
|
|
204 |
$hashSupported = true;
|
|
|
205 |
}
|
|
|
206 |
if ($hashSupported === false && in_array($hash, $this->_supportedHashNativeFunctions) && in_array($hash, array_keys($this->_hashPackFormats))) {
|
|
|
207 |
$this->_packFormat = $this->_hashPackFormats[$hash];
|
|
|
208 |
$hashSupported = true;
|
|
|
209 |
}
|
|
|
210 |
if ($hashSupported === false) {
|
|
|
211 |
require_once 'Crypt/HMAC2/Exception.php';
|
|
|
212 |
throw new Crypt_HMAC2_Exception('hash algorithm provided is not supported on this PHP instance; please enable the hash or mhash extensions');
|
|
|
213 |
}
|
|
|
214 |
$this->_hashAlgorithm = $hash;
|
|
|
215 |
return $this;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
/**
|
|
|
219 |
* Return the current hashing algorithm
|
|
|
220 |
*
|
|
|
221 |
* @return string
|
|
|
222 |
*/
|
|
|
223 |
public function getHashAlgorithm()
|
|
|
224 |
{
|
|
|
225 |
return $this->_hashAlgorithm;
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
/**
|
|
|
229 |
* Perform HMAC and return the keyed data
|
|
|
230 |
*
|
|
|
231 |
* @param string $data
|
|
|
232 |
* @param bool $internal Option to not use hash() functions for testing
|
|
|
233 |
* @return string
|
|
|
234 |
*/
|
|
|
235 |
public function hash($data, $output = self::STRING, $internal = false)
|
|
|
236 |
{
|
|
|
237 |
if (function_exists('hash_hmac') && $internal === false) {
|
|
|
238 |
if ($output == self::BINARY) {
|
|
|
239 |
return hash_hmac($this->getHashAlgorithm(), $data, $this->getKey(), 1);
|
|
|
240 |
}
|
|
|
241 |
return hash_hmac($this->getHashAlgorithm(), $data, $this->getKey());
|
|
|
242 |
}
|
|
|
243 |
|
|
|
244 |
if (function_exists('mhash') && $internal === false) {
|
|
|
245 |
if ($output == self::BINARY) {
|
|
|
246 |
return mhash($this->_getMhashDefinition($this->getHashAlgorithm()), $data, $this->getKey());
|
|
|
247 |
}
|
|
|
248 |
$bin = mhash($this->_getMhashDefinition($this->getHashAlgorithm()), $data, $this->getKey());
|
|
|
249 |
return bin2hex($bin);
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
// last ditch effort for MD5 and SHA1 only
|
|
|
253 |
$key = $this->getKey();
|
|
|
254 |
$hash = $this->getHashAlgorithm();
|
|
|
255 |
|
|
|
256 |
if (strlen($key) < 64) {
|
|
|
257 |
$key = str_pad($key, 64, chr(0));
|
|
|
258 |
} elseif (strlen($key) > 64) {
|
|
|
259 |
$key = pack($this->_packFormat, $this->_digest($hash, $key, $output));
|
|
|
260 |
}
|
|
|
261 |
$padInner = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
|
|
|
262 |
$padOuter = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
|
|
|
263 |
|
|
|
264 |
return $this->_digest($hash, $padOuter . pack($this->_packFormat, $this->_digest($hash, $padInner . $data, $output)), $output);
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
/**
|
|
|
268 |
* Method of working around the inability to use mhash constants; this
|
|
|
269 |
* will locate the Constant value of any support Hashing algorithm named
|
|
|
270 |
* in the string parameter.
|
|
|
271 |
*
|
|
|
272 |
* @param string $hashAlgorithm
|
|
|
273 |
* @return integer
|
|
|
274 |
*/
|
|
|
275 |
protected function _getMhashDefinition($hashAlgorithm)
|
|
|
276 |
{
|
|
|
277 |
for ($i = 0; $i <= mhash_count(); $i++) {
|
|
|
278 |
$types[mhash_get_hash_name($i)] = $i;
|
|
|
279 |
}
|
|
|
280 |
return $types[strtoupper($hashAlgorithm)];
|
|
|
281 |
}
|
|
|
282 |
|
|
|
283 |
/**
|
|
|
284 |
* Digest method when using native functions which allows the selection
|
|
|
285 |
* of raw binary output.
|
|
|
286 |
*
|
|
|
287 |
* @param string $hash
|
|
|
288 |
* @param string $key
|
|
|
289 |
* @param string $mode
|
|
|
290 |
* @return string
|
|
|
291 |
*/
|
|
|
292 |
protected function _digest($hash, $key, $mode)
|
|
|
293 |
{
|
|
|
294 |
if ($mode == self::BINARY) {
|
|
|
295 |
return $hash($key, true);
|
|
|
296 |
}
|
|
|
297 |
return $hash($key);
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
}
|