2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/hash/ext_hash.h"
21 #include "hphp/runtime/base/comparisons.h"
22 #include "hphp/runtime/ext/std/ext_std_file.h"
23 #include "hphp/runtime/ext/string/ext_string.h"
24 #include "hphp/runtime/ext/hash/hash_md.h"
25 #include "hphp/runtime/ext/hash/hash_sha.h"
26 #include "hphp/runtime/ext/hash/hash_ripemd.h"
27 #include "hphp/runtime/ext/hash/hash_whirlpool.h"
28 #include "hphp/runtime/ext/hash/hash_tiger.h"
29 #include "hphp/runtime/ext/hash/hash_snefru.h"
30 #include "hphp/runtime/ext/hash/hash_gost.h"
31 #include "hphp/runtime/ext/hash/hash_adler32.h"
32 #include "hphp/runtime/ext/hash/hash_crc32.h"
33 #include "hphp/runtime/ext/hash/hash_haval.h"
34 #include "hphp/runtime/ext/hash/hash_fnv1.h"
35 #include "hphp/runtime/ext/hash/hash_furc.h"
36 #include "hphp/runtime/ext/hash/hash_murmur.h"
37 #include "hphp/runtime/ext/hash/hash_keccak.h"
38 #include "hphp/runtime/ext/hash/hash_joaat.h"
41 #define furc_hash furc_hash_internal
43 #include "mcrouter/lib/fbi/hash.h" // @nolint
48 static struct HashExtension final
: Extension
{
49 HashExtension() : Extension("hash", "1.0") { }
50 void moduleInit() override
{
59 HHVM_FE(furchash_hphp_ext
);
60 HHVM_FE(hphp_murmurhash
);
62 HHVM_RC_INT(HASH_HMAC
, k_HASH_HMAC
);
68 ///////////////////////////////////////////////////////////////////////////////
71 static HashEngineMap HashEngines
;
72 using HashEnginePtr
= std::shared_ptr
<HashEngine
>;
74 struct HashEngineMapInitializer
{
75 HashEngineMapInitializer() {
76 HashEngines
["md2"] = HashEnginePtr(new hash_md2());
77 HashEngines
["md4"] = HashEnginePtr(new hash_md4());
78 HashEngines
["md5"] = HashEnginePtr(new hash_md5());
79 HashEngines
["sha1"] = HashEnginePtr(new hash_sha1());
80 HashEngines
["sha224"] = HashEnginePtr(new hash_sha224());
81 HashEngines
["sha256"] = HashEnginePtr(new hash_sha256());
82 HashEngines
["sha384"] = HashEnginePtr(new hash_sha384());
83 HashEngines
["sha512"] = HashEnginePtr(new hash_sha512());
84 HashEngines
["ripemd128"] = HashEnginePtr(new hash_ripemd128());
85 HashEngines
["ripemd160"] = HashEnginePtr(new hash_ripemd160());
86 HashEngines
["ripemd256"] = HashEnginePtr(new hash_ripemd256());
87 HashEngines
["ripemd320"] = HashEnginePtr(new hash_ripemd320());
88 HashEngines
["whirlpool"] = HashEnginePtr(new hash_whirlpool());
90 // The original version of tiger got the endianness backwards
91 // This fb-specific version remains for backward compatibility
92 HashEngines
["tiger128,3-fb"]
93 = HashEnginePtr(new hash_tiger(true, 128, true));
95 HashEngines
["tiger128,3"] = HashEnginePtr(new hash_tiger(true, 128));
96 HashEngines
["tiger160,3"] = HashEnginePtr(new hash_tiger(true, 160));
97 HashEngines
["tiger192,3"] = HashEnginePtr(new hash_tiger(true, 192));
98 HashEngines
["tiger128,4"] = HashEnginePtr(new hash_tiger(false, 128));
99 HashEngines
["tiger160,4"] = HashEnginePtr(new hash_tiger(false, 160));
100 HashEngines
["tiger192,4"] = HashEnginePtr(new hash_tiger(false, 192));
102 HashEngines
["snefru"] = HashEnginePtr(new hash_snefru());
103 HashEngines
["gost"] = HashEnginePtr(new hash_gost());
104 HashEngines
["joaat"] = HashEnginePtr(new hash_joaat());
106 HashEngines
["adler32-fb"] = HashEnginePtr(new hash_adler32(true));
108 HashEngines
["adler32"] = HashEnginePtr(new hash_adler32(false));
109 HashEngines
["crc32"] = HashEnginePtr(new hash_crc32(false));
110 HashEngines
["crc32b"] = HashEnginePtr(new hash_crc32(true));
111 HashEngines
["haval128,3"] = HashEnginePtr(new hash_haval(3,128));
112 HashEngines
["haval160,3"] = HashEnginePtr(new hash_haval(3,160));
113 HashEngines
["haval192,3"] = HashEnginePtr(new hash_haval(3,192));
114 HashEngines
["haval224,3"] = HashEnginePtr(new hash_haval(3,224));
115 HashEngines
["haval256,3"] = HashEnginePtr(new hash_haval(3,256));
116 HashEngines
["haval128,4"] = HashEnginePtr(new hash_haval(4,128));
117 HashEngines
["haval160,4"] = HashEnginePtr(new hash_haval(4,160));
118 HashEngines
["haval192,4"] = HashEnginePtr(new hash_haval(4,192));
119 HashEngines
["haval224,4"] = HashEnginePtr(new hash_haval(4,224));
120 HashEngines
["haval256,4"] = HashEnginePtr(new hash_haval(4,256));
121 HashEngines
["haval128,5"] = HashEnginePtr(new hash_haval(5,128));
122 HashEngines
["haval160,5"] = HashEnginePtr(new hash_haval(5,160));
123 HashEngines
["haval192,5"] = HashEnginePtr(new hash_haval(5,192));
124 HashEngines
["haval224,5"] = HashEnginePtr(new hash_haval(5,224));
125 HashEngines
["haval256,5"] = HashEnginePtr(new hash_haval(5,256));
126 HashEngines
["fnv132"] = HashEnginePtr(new hash_fnv132(false));
127 HashEngines
["fnv1a32"] = HashEnginePtr(new hash_fnv132(true));
128 HashEngines
["fnv164"] = HashEnginePtr(new hash_fnv164(false));
129 HashEngines
["fnv1a64"] = HashEnginePtr(new hash_fnv164(true));
130 HashEngines
["sha3-224"] = HashEnginePtr(new hash_keccak( 448, 28));
131 HashEngines
["sha3-256"] = HashEnginePtr(new hash_keccak( 512, 32));
132 HashEngines
["sha3-384"] = HashEnginePtr(new hash_keccak( 768, 48));
133 HashEngines
["sha3-512"] = HashEnginePtr(new hash_keccak(1024, 64));
137 static HashEngineMapInitializer s_engine_initializer
;
139 ///////////////////////////////////////////////////////////////////////////////
142 struct HashContext
: SweepableResourceData
{
143 HashContext(HashEnginePtr ops_
, void *context_
, int options_
)
144 : ops(ops_
), context(context_
), options(options_
), key(nullptr) {
147 explicit HashContext(req::ptr
<HashContext
>&& ctx
) {
149 assert(ctx
->ops
->context_size
>= 0);
151 context
= malloc(ops
->context_size
);
152 ops
->hash_copy(context
, ctx
->context
);
153 options
= ctx
->options
;
155 key
= static_cast<char*>(malloc(ops
->block_size
));
156 memcpy(key
, ctx
->key
, ops
->block_size
);
163 /* Just in case the algo has internally allocated resources */
165 assert(ops
->digest_size
>= 0);
166 unsigned char* dummy
= (unsigned char*)alloca(
167 sizeof(unsigned char) * ops
->digest_size
);
168 ops
->hash_final(dummy
, context
);
175 bool isInvalid() const override
{
176 return context
== nullptr;
179 CLASSNAME_IS("Hash Context")
180 DECLARE_RESOURCE_ALLOCATION(HashContext
)
182 // overriding ResourceData
183 const String
& o_getClassNameHook() const override
{ return classnameof(); }
190 TYPE_SCAN_IGNORE_FIELD(context
);
193 IMPLEMENT_RESOURCE_ALLOCATION(HashContext
)
195 ///////////////////////////////////////////////////////////////////////////////
197 static req::ptr
<HashContext
> get_valid_hash_context_resource(const Resource
& context
,
198 const char* func_name
) {
199 auto hash
= dyn_cast_or_null
<HashContext
>(context
);
200 if (hash
== nullptr || hash
->isInvalid()) {
202 "%s(): supplied resource is not a valid Hash Context resource",
210 ///////////////////////////////////////////////////////////////////////////////
213 Array
HHVM_FUNCTION(hash_algos
) {
215 for (HashEngineMap::const_iterator iter
= HashEngines
.begin();
216 iter
!= HashEngines
.end(); ++iter
) {
217 ret
.append(String(iter
->first
));
222 static HashEnginePtr
php_hash_fetch_ops(const String
& algo
) {
223 HashEngineMap::const_iterator iter
=
224 HashEngines
.find(HHVM_FN(strtolower
)(algo
).data());
225 if (iter
== HashEngines
.end()) {
226 return HashEnginePtr();
231 static Variant
php_hash_do_hash(const String
& algo
, const String
& data
,
234 HashEnginePtr ops
= php_hash_fetch_ops(algo
);
236 raise_warning("Unknown hashing algorithm: %s", algo
.data());
241 f
= HHVM_FN(fopen
)(data
, "rb");
242 if (same(f
, false)) {
247 void *context
= malloc(ops
->context_size
);
248 ops
->hash_init(context
);
251 for (Variant chunk
= HHVM_FN(fread
)(f
.toResource(), 1024);
252 !is_empty_string(chunk
);
253 chunk
= HHVM_FN(fread
)(f
.toResource(), 1024)) {
254 String schunk
= chunk
.toString();
255 ops
->hash_update(context
, (unsigned char *)schunk
.data(), schunk
.size());
258 ops
->hash_update(context
, (unsigned char *)data
.data(), data
.size());
261 String raw
= String(ops
->digest_size
, ReserveString
);
262 char *digest
= raw
.mutableData();
263 ops
->hash_final((unsigned char *)digest
, context
);
266 raw
.setSize(ops
->digest_size
);
270 return HHVM_FN(bin2hex
)(raw
);
273 Variant
HHVM_FUNCTION(hash
, const String
& algo
, const String
& data
,
274 bool raw_output
/* = false */) {
275 return php_hash_do_hash(algo
, data
, false, raw_output
);
278 Variant
HHVM_FUNCTION(hash_file
, const String
& algo
, const String
& filename
,
279 bool raw_output
/* = false */) {
280 if (filename
.size() != strlen(filename
.data())) {
282 "hash_file() expects parameter 2 to be a valid path, string given"
284 return uninit_variant
;
286 return php_hash_do_hash(algo
, filename
, true, raw_output
);
289 static char *prepare_hmac_key(HashEnginePtr ops
, void *context
,
291 char *K
= (char*)malloc(ops
->block_size
);
292 memset(K
, 0, ops
->block_size
);
293 if (key
.size() > ops
->block_size
) {
294 /* Reduce the key first */
295 ops
->hash_update(context
, (unsigned char *)key
.data(), key
.size());
296 ops
->hash_final((unsigned char *)K
, context
);
297 /* Make the context ready to start over */
298 ops
->hash_init(context
);
300 memcpy(K
, key
.data(), key
.size());
304 for (int i
= 0; i
< ops
->block_size
; i
++) {
307 ops
->hash_update(context
, (unsigned char *)K
, ops
->block_size
);
311 static void finalize_hmac_key(char *K
, HashEnginePtr ops
, void *context
,
313 /* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
314 for (int i
= 0; i
< ops
->block_size
; i
++) {
318 /* Feed this result into the outter hash */
319 ops
->hash_init(context
);
320 ops
->hash_update(context
, (unsigned char *)K
, ops
->block_size
);
321 ops
->hash_update(context
, (unsigned char *)digest
, ops
->digest_size
);
322 ops
->hash_final((unsigned char *)digest
, context
);
325 memset(K
, 0, ops
->block_size
);
329 Variant
HHVM_FUNCTION(hash_init
, const String
& algo
,
330 int64_t options
/* = 0 */,
331 const String
& key
/* = null_string */) {
332 HashEnginePtr ops
= php_hash_fetch_ops(algo
);
334 raise_warning("Unknown hashing algorithm: %s", algo
.data());
338 if ((options
& k_HASH_HMAC
) && key
.empty()) {
339 raise_warning("HMAC requested without a key");
343 void *context
= malloc(ops
->context_size
);
344 ops
->hash_init(context
);
346 const auto hash
= req::make
<HashContext
>(ops
, context
, options
);
347 if (options
& k_HASH_HMAC
) {
348 hash
->key
= prepare_hmac_key(ops
, context
, key
);
350 return Variant(std::move(hash
));
353 bool HHVM_FUNCTION(hash_update
, const Resource
& context
, const String
& data
) {
354 auto hash
= get_valid_hash_context_resource(context
, __FUNCTION__
);
358 hash
->ops
->hash_update(hash
->context
, (unsigned char *)data
.data(),
363 Variant
HHVM_FUNCTION(hash_final
, const Resource
& context
,
364 bool raw_output
/* = false */) {
365 auto hash
= get_valid_hash_context_resource(context
, __FUNCTION__
);
370 String raw
= String(hash
->ops
->digest_size
, ReserveString
);
371 char *digest
= raw
.mutableData();
372 hash
->ops
->hash_final((unsigned char *)digest
, hash
->context
);
373 if (hash
->options
& k_HASH_HMAC
) {
374 finalize_hmac_key(hash
->key
, hash
->ops
, hash
->context
, digest
);
378 hash
->context
= NULL
;
380 raw
.setSize(hash
->ops
->digest_size
);
384 return HHVM_FN(bin2hex
)(raw
);
387 Variant
HHVM_FUNCTION(hash_copy
, const Resource
& context
) {
388 auto oldhash
= get_valid_hash_context_resource(context
, __FUNCTION__
);
392 return Resource(req::make
<HashContext
>(std::move(oldhash
)));
396 * It is important that the run time of this function is dependent
397 * only on the length of the user-supplied string.
399 * The only branch in the code below *should* result in non-branching
402 * Do not try to optimize this function.
404 bool HHVM_FUNCTION(hash_equals
, const Variant
& known
, const Variant
& user
) {
405 if (!known
.isString()) {
407 "hash_equals(): Expected known_string to be a string, %s given",
408 getDataTypeString(known
.getType()).c_str()
412 if (!user
.isString()) {
414 "hash_equals(): Expected user_string to be a string, %s given",
415 getDataTypeString(user
.getType()).c_str()
419 String known_str
= known
.toString();
420 String user_str
= user
.toString();
421 const auto known_len
= known_str
.size();
422 const auto known_limit
= known_len
- 1;
423 const auto user_len
= user_str
.size();
424 int64_t result
= known_len
^ user_len
;
427 for (int64_t ui
= 0; ui
< user_len
; ++ui
) {
428 result
|= user_str
[ui
] ^ known_str
[ki
];
429 if (ki
< known_limit
) {
433 return (result
== 0);
436 int64_t HHVM_FUNCTION(furchash_hphp_ext
, const String
& key
,
437 int64_t len
, int64_t nPart
) {
438 len
= std::max
<int64_t>(std::min
<int64_t>(len
, key
.size()), 0);
439 return furc_hash(key
.data(), len
, nPart
);
442 int64_t HHVM_FUNCTION(hphp_murmurhash
, const String
& key
,
443 int64_t len
, int64_t seed
) {
444 len
= std::max
<int64_t>(std::min
<int64_t>(len
, key
.size()), 0);
445 return murmur_hash_64A(key
.data(), len
, seed
);
448 ///////////////////////////////////////////////////////////////////////////////