codemod 2010-2016 to 2010-present
[hiphop-php.git] / hphp / runtime / ext / hash / ext_hash.cpp
blobb4e42d61398dec1952d98640d2791c551fde9294
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include <algorithm>
20 #include <memory>
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"
40 #if defined(HPHP_OSS)
41 #define furc_hash furc_hash_internal
42 #else
43 #include "mcrouter/lib/fbi/hash.h" // @nolint
44 #endif
46 namespace HPHP {
48 static struct HashExtension final : Extension {
49 HashExtension() : Extension("hash", "1.0") { }
50 void moduleInit() override {
51 HHVM_FE(hash);
52 HHVM_FE(hash_algos);
53 HHVM_FE(hash_file);
54 HHVM_FE(hash_final);
55 HHVM_FE(hash_init);
56 HHVM_FE(hash_update);
57 HHVM_FE(hash_copy);
58 HHVM_FE(hash_equals);
59 HHVM_FE(furchash_hphp_ext);
60 HHVM_FE(hphp_murmurhash);
62 HHVM_RC_INT(HASH_HMAC, k_HASH_HMAC);
64 loadSystemlib();
66 } s_hash_extension;
68 ///////////////////////////////////////////////////////////////////////////////
69 // hash engines
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());
89 #ifdef FACEBOOK
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));
94 #endif
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());
105 #ifdef FACEBOOK
106 HashEngines["adler32-fb"] = HashEnginePtr(new hash_adler32(true));
107 #endif
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 ///////////////////////////////////////////////////////////////////////////////
140 // hash context
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) {
148 assert(ctx->ops);
149 assert(ctx->ops->context_size >= 0);
150 ops = ctx->ops;
151 context = malloc(ops->context_size);
152 ops->hash_copy(context, ctx->context);
153 options = ctx->options;
154 if (ctx->key) {
155 key = static_cast<char*>(malloc(ops->block_size));
156 memcpy(key, ctx->key, ops->block_size);
157 } else {
158 key = nullptr;
162 ~HashContext() {
163 /* Just in case the algo has internally allocated resources */
164 if (context) {
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);
169 free(context);
172 free(key);
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(); }
185 HashEnginePtr ops;
186 void *context;
187 int options;
188 char *key;
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()) {
201 raise_warning(
202 "%s(): supplied resource is not a valid Hash Context resource",
203 func_name + 2
205 return nullptr;
207 return hash;
210 ///////////////////////////////////////////////////////////////////////////////
211 // hash functions
213 Array HHVM_FUNCTION(hash_algos) {
214 Array ret;
215 for (HashEngineMap::const_iterator iter = HashEngines.begin();
216 iter != HashEngines.end(); ++iter) {
217 ret.append(String(iter->first));
219 return ret;
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();
228 return iter->second;
231 static Variant php_hash_do_hash(const String& algo, const String& data,
232 bool isfilename,
233 bool raw_output) {
234 HashEnginePtr ops = php_hash_fetch_ops(algo);
235 if (!ops) {
236 raise_warning("Unknown hashing algorithm: %s", algo.data());
237 return false;
239 Variant f;
240 if (isfilename) {
241 f = HHVM_FN(fopen)(data, "rb");
242 if (same(f, false)) {
243 return false;
247 void *context = malloc(ops->context_size);
248 ops->hash_init(context);
250 if (isfilename) {
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());
257 } else {
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);
264 free(context);
266 raw.setSize(ops->digest_size);
267 if (raw_output) {
268 return raw;
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())) {
281 raise_warning(
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,
290 const String& key) {
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);
299 } else {
300 memcpy(K, key.data(), key.size());
303 /* XOR ipad */
304 for (int i = 0; i < ops->block_size; i++) {
305 K[i] ^= 0x36;
307 ops->hash_update(context, (unsigned char *)K, ops->block_size);
308 return K;
311 static void finalize_hmac_key(char *K, HashEnginePtr ops, void *context,
312 char *digest) {
313 /* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
314 for (int i = 0; i < ops->block_size; i++) {
315 K[i] ^= 0x6A;
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);
324 /* Zero the key */
325 memset(K, 0, ops->block_size);
326 free(K);
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);
333 if (!ops) {
334 raise_warning("Unknown hashing algorithm: %s", algo.data());
335 return false;
338 if ((options & k_HASH_HMAC) && key.empty()) {
339 raise_warning("HMAC requested without a key");
340 return false;
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__);
355 if (!hash) {
356 return false;
358 hash->ops->hash_update(hash->context, (unsigned char *)data.data(),
359 data.size());
360 return true;
363 Variant HHVM_FUNCTION(hash_final, const Resource& context,
364 bool raw_output /* = false */) {
365 auto hash = get_valid_hash_context_resource(context, __FUNCTION__);
366 if (!hash) {
367 return false;
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);
375 hash->key = NULL;
377 free(hash->context);
378 hash->context = NULL;
380 raw.setSize(hash->ops->digest_size);
381 if (raw_output) {
382 return raw;
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__);
389 if (!oldhash) {
390 return false;
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
400 * machine code.
402 * Do not try to optimize this function.
404 bool HHVM_FUNCTION(hash_equals, const Variant& known, const Variant& user) {
405 if (!known.isString()) {
406 raise_warning(
407 "hash_equals(): Expected known_string to be a string, %s given",
408 getDataTypeString(known.getType()).c_str()
410 return false;
412 if (!user.isString()) {
413 raise_warning(
414 "hash_equals(): Expected user_string to be a string, %s given",
415 getDataTypeString(user.getType()).c_str()
417 return false;
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;
426 int64_t ki = 0;
427 for (int64_t ui = 0; ui < user_len; ++ui) {
428 result |= user_str[ui] ^ known_str[ki];
429 if (ki < known_limit) {
430 ++ki;
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 ///////////////////////////////////////////////////////////////////////////////