Arquivos
hhvm/hphp/runtime/ext/ext_hash.cpp
T
Sara Golemon 497a22c04f Fix endianness for all tiger algos
The PHP version the implementations in hphp were based off of
had wrong byte ordering.  Fix them in line with PHP having been fixed.
2013-05-06 10:31:24 -07:00

408 linhas
14 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <runtime/ext/ext_hash.h>
#include <runtime/ext/ext_file.h>
#include <runtime/ext/hash/hash_md.h>
#include <runtime/ext/hash/hash_sha.h>
#include <runtime/ext/hash/hash_ripemd.h>
#include <runtime/ext/hash/hash_whirlpool.h>
#include <runtime/ext/hash/hash_tiger.h>
#include <runtime/ext/hash/hash_snefru.h>
#include <runtime/ext/hash/hash_gost.h>
#include <runtime/ext/hash/hash_adler32.h>
#include <runtime/ext/hash/hash_crc32.h>
#include <runtime/ext/hash/hash_haval.h>
#include <runtime/ext/hash/hash_fnv1.h>
#include <runtime/ext/hash/hash_furc.h>
#include <runtime/ext/hash/hash_murmur.h>
#if defined(HPHP_OSS)
#define furc_hash furc_hash_internal
#else
#include <memcache/ch/hash.h>
#endif
namespace HPHP {
IMPLEMENT_DEFAULT_EXTENSION(hash);
///////////////////////////////////////////////////////////////////////////////
// hash engines
static HashEngineMap HashEngines;
class HashEngineMapInitializer {
public:
HashEngineMapInitializer() {
HashEngines["md2"] = HashEnginePtr(new hash_md2());
HashEngines["md4"] = HashEnginePtr(new hash_md4());
HashEngines["md5"] = HashEnginePtr(new hash_md5());
HashEngines["sha1"] = HashEnginePtr(new hash_sha1());
HashEngines["sha224"] = HashEnginePtr(new hash_sha224());
HashEngines["sha256"] = HashEnginePtr(new hash_sha256());
HashEngines["sha384"] = HashEnginePtr(new hash_sha384());
HashEngines["sha512"] = HashEnginePtr(new hash_sha512());
HashEngines["ripemd128"] = HashEnginePtr(new hash_ripemd128());
HashEngines["ripemd160"] = HashEnginePtr(new hash_ripemd160());
HashEngines["ripemd256"] = HashEnginePtr(new hash_ripemd256());
HashEngines["ripemd320"] = HashEnginePtr(new hash_ripemd320());
HashEngines["whirlpool"] = HashEnginePtr(new hash_whirlpool());
#ifdef FACEBOOK
HashEngines["tiger128,3-fb"]
= HashEnginePtr(new hash_tiger(true, 128, true));
// Temporarily leave tiger128,3 algo inverting its hash output
// to retain BC pending conversion of user code to correct endianness
// sgolemon(2013-04-30)
HashEngines["tiger128,3"] = HashEnginePtr(new hash_tiger(true, 128, true));
#else
HashEngines["tiger128,3"] = HashEnginePtr(new hash_tiger(true, 128));
#endif
HashEngines["tiger160,3"] = HashEnginePtr(new hash_tiger(true, 160));
HashEngines["tiger192,3"] = HashEnginePtr(new hash_tiger(true, 192));
HashEngines["tiger128,4"] = HashEnginePtr(new hash_tiger(false, 128));
HashEngines["tiger160,4"] = HashEnginePtr(new hash_tiger(false, 160));
HashEngines["tiger192,4"] = HashEnginePtr(new hash_tiger(false, 192));
HashEngines["snefru"] = HashEnginePtr(new hash_snefru());
HashEngines["gost"] = HashEnginePtr(new hash_gost());
HashEngines["adler32"] = HashEnginePtr(new hash_adler32());
HashEngines["crc32"] = HashEnginePtr(new hash_crc32(false));
HashEngines["crc32b"] = HashEnginePtr(new hash_crc32(true));
HashEngines["haval128,3"] = HashEnginePtr(new hash_haval(3,128));
HashEngines["haval160,3"] = HashEnginePtr(new hash_haval(3,160));
HashEngines["haval192,3"] = HashEnginePtr(new hash_haval(3,192));
HashEngines["haval224,3"] = HashEnginePtr(new hash_haval(3,224));
HashEngines["haval256,3"] = HashEnginePtr(new hash_haval(3,256));
HashEngines["haval128,4"] = HashEnginePtr(new hash_haval(4,128));
HashEngines["haval160,4"] = HashEnginePtr(new hash_haval(4,160));
HashEngines["haval192,4"] = HashEnginePtr(new hash_haval(4,192));
HashEngines["haval224,4"] = HashEnginePtr(new hash_haval(4,224));
HashEngines["haval256,4"] = HashEnginePtr(new hash_haval(4,256));
HashEngines["haval128,5"] = HashEnginePtr(new hash_haval(5,128));
HashEngines["haval160,5"] = HashEnginePtr(new hash_haval(5,160));
HashEngines["haval192,5"] = HashEnginePtr(new hash_haval(5,192));
HashEngines["haval224,5"] = HashEnginePtr(new hash_haval(5,224));
HashEngines["haval256,5"] = HashEnginePtr(new hash_haval(5,256));
HashEngines["fnv132"] = HashEnginePtr(new hash_fnv132(false));
HashEngines["fnv1a32"] = HashEnginePtr(new hash_fnv132(true));
HashEngines["fnv164"] = HashEnginePtr(new hash_fnv164(false));
HashEngines["fnv1a64"] = HashEnginePtr(new hash_fnv164(true));
}
};
static HashEngineMapInitializer s_engine_initializer;
///////////////////////////////////////////////////////////////////////////////
// hash context
class HashContext : public SweepableResourceData {
public:
static StaticString s_class_name;
// overriding ResourceData
virtual CStrRef o_getClassNameHook() const { return s_class_name; }
HashContext(HashEnginePtr ops_, void *context_, int options_)
: ops(ops_), context(context_), options(options_), key(NULL) {
}
~HashContext() {
/* Just in case the algo has internally allocated resources */
if (context) {
unsigned char *dummy = (unsigned char *)malloc(ops->digest_size);
ops->hash_final(dummy, context);
free(dummy);
free(context);
}
if (key) {
memset(key, 0, ops->block_size);
free(key);
}
}
HashEnginePtr ops;
void *context;
int options;
char *key;
};
StaticString HashContext::s_class_name("Hash Context");
///////////////////////////////////////////////////////////////////////////////
// hash functions
Array f_hash_algos() {
Array ret;
for (HashEngineMap::const_iterator iter = HashEngines.begin();
iter != HashEngines.end(); ++iter) {
ret.append(String(iter->first));
}
return ret;
}
static HashEnginePtr php_hash_fetch_ops(CStrRef algo) {
HashEngineMap::const_iterator iter =
HashEngines.find(StringUtil::ToLower(algo).data());
if (iter == HashEngines.end()) {
return HashEnginePtr();
}
return iter->second;
}
static Variant php_hash_do_hash(CStrRef algo, CStrRef data, bool isfilename,
bool raw_output) {
HashEnginePtr ops = php_hash_fetch_ops(algo);
if (!ops) {
raise_warning("Unknown hashing algorithm: %s", algo.data());
return false;
}
Variant f;
if (isfilename) {
f = f_fopen(data, "rb");
if (same(f, false)) {
return false;
}
}
void *context = malloc(ops->context_size);
ops->hash_init(context);
if (isfilename) {
for (Variant chunk = f_fread(f, 1024); !is_empty_string(chunk);
chunk = f_fread(f, 1024)) {
String schunk = chunk.toString();
ops->hash_update(context, (unsigned char *)schunk.data(), schunk.size());
}
} else {
ops->hash_update(context, (unsigned char *)data.data(), data.size());
}
String raw = String(ops->digest_size, ReserveString);
char *digest = raw.mutableSlice().ptr;
ops->hash_final((unsigned char *)digest, context);
free(context);
raw.setSize(ops->digest_size);
if (raw_output) {
return raw;
}
return StringUtil::HexEncode(raw);
}
Variant f_hash(CStrRef algo, CStrRef data, bool raw_output /* = false */) {
return php_hash_do_hash(algo, data, false, raw_output);
}
Variant f_hash_file(CStrRef algo, CStrRef filename,
bool raw_output /* = false */) {
return php_hash_do_hash(algo, filename, true, raw_output);
}
static char *prepare_hmac_key(HashEnginePtr ops, void *context, CStrRef key) {
char *K = (char*)malloc(ops->block_size);
memset(K, 0, ops->block_size);
if (key.size() > ops->block_size) {
/* Reduce the key first */
ops->hash_update(context, (unsigned char *)key.data(), key.size());
ops->hash_final((unsigned char *)K, context);
/* Make the context ready to start over */
ops->hash_init(context);
} else {
memcpy(K, key.data(), key.size());
}
/* XOR ipad */
for (int i = 0; i < ops->block_size; i++) {
K[i] ^= 0x36;
}
ops->hash_update(context, (unsigned char *)K, ops->block_size);
return K;
}
static void finalize_hmac_key(char *K, HashEnginePtr ops, void *context,
char *digest) {
/* Convert K to opad -- 0x6A = 0x36 ^ 0x5C */
for (int i = 0; i < ops->block_size; i++) {
K[i] ^= 0x6A;
}
/* Feed this result into the outter hash */
ops->hash_init(context);
ops->hash_update(context, (unsigned char *)K, ops->block_size);
ops->hash_update(context, (unsigned char *)digest, ops->digest_size);
ops->hash_final((unsigned char *)digest, context);
/* Zero the key */
memset(K, 0, ops->block_size);
free(K);
}
static Variant php_hash_do_hash_hmac(CStrRef algo, CStrRef data,
bool isfilename, CStrRef key,
bool raw_output /* = false */) {
HashEnginePtr ops = php_hash_fetch_ops(algo);
if (!ops) {
raise_warning("Unknown hashing algorithm: %s", algo.data());
return false;
}
Variant f;
if (isfilename) {
f = f_fopen(data, "rb");
if (same(f, false)) {
return false;
}
}
void *context = malloc(ops->context_size);
ops->hash_init(context);
char *K = prepare_hmac_key(ops, context, key);
if (isfilename) {
for (Variant chunk = f_fread(f, 1024); !is_empty_string(chunk);
chunk = f_fread(f, 1024)) {
String schunk = chunk.toString();
ops->hash_update(context, (unsigned char *)schunk.data(), schunk.size());
}
} else {
ops->hash_update(context, (unsigned char *)data.data(), data.size());
}
String raw = String(ops->digest_size, ReserveString);
char *digest = raw.mutableSlice().ptr;
ops->hash_final((unsigned char *)digest, context);
finalize_hmac_key(K, ops, context, digest);
free(context);
raw.setSize(ops->digest_size);
if (raw_output) {
return raw;
}
return StringUtil::HexEncode(raw);
}
Variant f_hash_hmac(CStrRef algo, CStrRef data, CStrRef key,
bool raw_output /* = false */) {
return php_hash_do_hash_hmac(algo, data, false, key, raw_output);
}
Variant f_hash_hmac_file(CStrRef algo, CStrRef filename, CStrRef key,
bool raw_output /* = false */) {
return php_hash_do_hash_hmac(algo, filename, true, key, raw_output);
}
Variant f_hash_init(CStrRef algo, int options /* = 0 */,
CStrRef key /* = null_string */) {
HashEnginePtr ops = php_hash_fetch_ops(algo);
if (!ops) {
raise_warning("Unknown hashing algorithm: %s", algo.data());
return false;
}
if ((options & k_HASH_HMAC) && key.empty()) {
raise_warning("HMAC requested without a key");
return false;
}
void *context = malloc(ops->context_size);
ops->hash_init(context);
HashContext *hash = new HashContext(ops, context, options);
if (options & k_HASH_HMAC) {
hash->key = prepare_hmac_key(ops, context, key);
}
return Object(hash);
}
bool f_hash_update(CObjRef context, CStrRef data) {
HashContext *hash = context.getTyped<HashContext>();
hash->ops->hash_update(hash->context, (unsigned char *)data.data(),
data.size());
return true;
}
bool f_hash_update_file(CObjRef init_context, CStrRef filename,
CObjRef stream_context /* = null */) {
Variant f = f_fopen(filename, "rb");
if (same(f, false)) {
return false;
}
HashContext *hash = init_context.getTyped<HashContext>();
for (Variant chunk = f_fread(f, 1024); !is_empty_string(chunk);
chunk = f_fread(f, 1024)) {
String schunk = chunk.toString();
hash->ops->hash_update(hash->context, (unsigned char *)schunk.data(),
schunk.size());
}
return true;
}
int64_t f_hash_update_stream(CObjRef context, CObjRef handle,
int length /* = -1 */) {
HashContext *hash = context.getTyped<HashContext>();
int didread = 0;
while (length) {
Variant chunk = f_fread(handle, length > 0 ? length : 1024);
if (is_empty_string(chunk)) {
return didread;
}
String schunk = chunk.toString();
hash->ops->hash_update(hash->context, (unsigned char *)schunk.data(),
schunk.size());
didread += schunk.size();
length -= schunk.size();
}
return didread;
}
String f_hash_final(CObjRef context, bool raw_output /* = false */) {
HashContext *hash = context.getTyped<HashContext>();
String raw = String(hash->ops->digest_size, ReserveString);
char *digest = raw.mutableSlice().ptr;
hash->ops->hash_final((unsigned char *)digest, hash->context);
if (hash->options & k_HASH_HMAC) {
finalize_hmac_key(hash->key, hash->ops, hash->context, digest);
hash->key = NULL;
}
free(hash->context);
hash->context = NULL;
raw.setSize(hash->ops->digest_size);
if (raw_output) {
return raw;
}
return StringUtil::HexEncode(raw);
}
int64_t f_furchash_hphp_ext(CStrRef key, int len, int nPart) {
len = std::max(std::min(len, key.size()), 0);
return furc_hash(key.data(), len, nPart);
}
bool f_furchash_hphp_ext_supported() {
return true;
}
int64_t f_hphp_murmurhash(CStrRef key, int len, int seed) {
len = std::max(std::min(len, key.size()), 0);
return murmur_hash_64A(key.data(), len, seed);
}
///////////////////////////////////////////////////////////////////////////////
}