Lets say I have an hash function md5 and I hash some string and it gives me the some value and I need to convert it into an integer value with a fixed range.
As given here for crc32() algorithm to convert md5 (or maybe another hashing method?) to integer where it is possible to set possible resulting integer ranges (eg: 1-10000)?
For example lets say:
$value=md5("dog");
echo $value;
Output: "06d80eb0c50b49a509b49f2424e8c805"`
Now I need to convert this value into integer so that I can use it in a look-up table of size 1000. Thus when I convert it the size of this hash value should be between 0 to 999. How can I do that?
You can use base_convert to change the hexadecimal output from md5 into decimal, and then use the bcmod function to convert that down to a number in your chosen range:
<?php
$valuetohash = "dog";
$range = 1000;
$hashslot = bcmod(base_convert(md5($valuetohash), 16, 10), $range);
echo $hashslot;
Output: 26
Changing "dog" to "parrot" produces 800, changing it to "cat" produces 260 (which, amusingly, shows that cats are 10 times better than dogs).
Note, if you are using a different hash that doesn't map to "a big hex number", eg crc32 maps to an integer, you will need to do the above differently (in the case of crc32, you can just straight %mod it to the range for example, as per the original comment on your question).
Related
In php is there a way to give a unique hash from a string, but that the hash was made up from numbers only?
example:
return md5(234); // returns 098f6bcd4621d373cade4e832627b4f6
but I need
return numhash(234); // returns 00978902923102372190
(20 numbers only)
the problem here is that I want the hashing to be short.
edit:
OK let me explain the back story here.
I have a site that has a ID for every registered person, also I need a ID for the person to use and exchange (hence it can't be too long), so far the ID numbering has been 00001, 00002, 00003 etc...
this makes some people look more important
this reveals application info that I don't want to reveal.
To fix point 1 and 2 I need to "hide" the number while keeping it unique.
Edit + SOLUTION:
Numeric hash function based on the code by https://stackoverflow.com/a/23679870/175071
/**
* Return a number only hash
* https://stackoverflow.com/a/23679870/175071
* #param $str
* #param null $len
* #return number
*/
public function numHash($str, $len=null)
{
$binhash = md5($str, true);
$numhash = unpack('N2', $binhash);
$hash = $numhash[1] . $numhash[2];
if($len && is_int($len)) {
$hash = substr($hash, 0, $len);
}
return $hash;
}
// Usage
numHash(234, 20); // always returns 6814430791721596451
An MD5 or SHA1 hash in PHP returns a hexadecimal number, so all you need to do is convert bases. PHP has a function that can do this for you:
$bignum = hexdec( md5("test") );
or
$bignum = hexdec( sha1("test") );
PHP Manual for hexdec
Since you want a limited size number, you could then use modular division to put it in a range you want.
$smallnum = $bignum % [put your upper bound here]
EDIT
As noted by Artefacto in the comments, using this approach will result in a number beyond the maximum size of an Integer in PHP, and the result after modular division will always be 0. However, taking a substring of the hash that contains the first 16 characters doesn't have this problem. Revised version for calculating the initial large number:
$bignum = hexdec( substr(sha1("test"), 0, 15) );
You can try crc32(). See the documentation at: http://php.net/manual/en/function.crc32.php
$checksum = crc32("The quick brown fox jumped over the lazy dog.");
printf("%u\n", $checksum); // prints 2191738434
With that said, crc should only be used to validate the integrity of data.
There are some good answers but for me the approaches seem silly.
They first force php to create a Hex number, then convert this back (hexdec) in a BigInteger and then cut it down to a number of letters... this is much work!
Instead why not
Read the hash as binary:
$binhash = md5('[input value]', true);
then using
$numhash = unpack('N2', $binhash); //- or 'V2' for little endian
to cast this as two INTs ($numhash is an array of two elements). Now you can reduce the number of bits in the number simply using an AND operation. e.g:
$result = $numhash[1] & 0x000FFFFF; //- to get numbers between 0 and 1048575
But be warned of collisions! Reducing the number means increasing the probability of two different [input value] with the same output.
I think that the much better way would be the use of "ID-Crypting" with a Bijectiv function. So no collisions could happen! For the simplest kind just use an Affine_cipher
Example with max input value range from 0 to 25:
function numcrypt($a)
{
return ($a * 15) % 26;
}
function unnumcrypt($a)
{
return ($a * 7) % 26;
}
Output:
numcrypt(1) : 15
numcrypt(2) : 4
numcrypt(3) : 19
unnumcrypt(15) : 1
unnumcrypt(4) : 2
unnumcrypt(19) : 3
e.g.
$id = unnumcrypt($_GET('userid'));
... do something with the ID ...
echo ' go ';
of course this is not secure, but if no one knows the method used for your encryption then there are no security reasons then this way is faster and collision safe.
The problem of cut off the hash are the collisions, to avoid it try:
return hexdec(crc32("Hello World"));
The crc32():
Generates the cyclic redundancy checksum polynomial of 32-bit lengths
of the str. This is usually used to validate the integrity of data
being transmitted.
That give us an integer of 32 bit, negative in 32 bits installation, or positive in the 64 bits. This integer could be store like an ID in a database. This donĀ“t have collision problems, because it fits into 32bits variable, once you convert it to decimal with the hexdec() function.
First of all, md5 is basically compromised, so you shouldn't be using it for anything but non-critical hashing.
PHP5 has the hash() function, see http://www.php.net/manual/en/function.hash.php.
Setting the last parameter to true will give you a string of binary data. Alternatively, you could split the resulting hexadecimal hash into pieces of 2 characters and convert them to integers individually, but I'd expect that to be much slower.
Try hashid.
It hash a number into format you can define. The formats include how many character, and what character included.
Example:
$hashids->encode(1);
Will return "28630" depends on your format,
Just use my manual hash method below:
Divide the number (e.g. 6 digit) by prime values, 3,5,7.
And get the first 6 values that are in the decimal places as the ID to be used. Do a check on uniqueness before actual creation of the ID, if a collision exists, increase the last digit by +1 until a non collision.
E.g. 123456 gives you 771428
123457 gives you 780952
123458 gives you 790476.
I'm trying to build a app that would identify a user by scanning a qrcode. For this, I want to use the primary key as the identifier. Since the character length of the integer is short, it wouldn't give a good look as a qrcode.
So my question is: Is it possible to convert the int to string which is longer than 10-12 chars (fixed length if possible),mix of chars and numbers which can be reversed to the original integer.
What you can do is to make SHA256 of your user's ID and convert it to QR code.
Then when user reads QR code and send you sha value you try to match it with SHA of user's IDs in the database.
So here is the way to have SHA hash from user id:
$hash = hash('sha256', $userId); // The result is long enough string for QA
The when you need to find a user based on SHA do the following:
select * from users where SHA2(id, 256) = 'SHA_PROVIDED_BY_USER';
You can in order to speed up the look up process store SHA in the DB as well then query will be much faster.
Another option is to prepend the number with some letters. It will give you random string, nice QRs and you can extract numeric ID with simple regexp.
Using function from PHP random string generator (don't forget to remove numbers from $characters) the code could be:
//encoding
$size = 12;
$str = generateRandomString($size-strlen($userId)).$userId;
//decoding
preg_match('/(\d+)$/', $str, $matching);
$userId = $matching[1];
you can convert your integer to any base with base_convert function.
here is the documentation.
http://php.net/manual/en/function.base-convert.php
The notion that a number, in PHP, has a "maximum size" is a little off (not wrong, just off =P)
From the manual:
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead.
So, you could use really large numbers for your QR Codes if you want. Shouldn't be an issue. However, what would be better is to think of "what exactly do you need"?
If you need a numeric value, but want it in hex, you can use base_convert() to go back and forth between the numbers:
$val = 1234;
$hex = base_convert($val, 10, 16);
However, if strings are more for you, you could use base64_encode() to encode it:
$val = 'awesome string value';
$encoded = base64_encode($val);
UPDATE
Based on comments, it sounds like you also want to pad the string if it's too short. You can use str_pad() to accomplish this:
$val = str_pad("1", 10, "0", STR_PAD_LEFT);
echo $val;
// displays: 0000000001
$orig = intval($val);
echo $orig;
// displays: 1
Coderpad Example of str_pad()
Good morning,
I have to create a random number from a given 256HASH using a secret key. Everyting is fine, until I have to "convert" the hash into an integer between 0 and 15.000.000 (which is the random number). I have been playing with ord() and bytes but I don't get anything that suits me.
My original idea was to cast the SHA256 string into an integer, and then apply a divisor to obtain the modulus. But I need a random number between a very big range. 0 to 15.000.000 (fifteen million). How would you do it?
Thanks!
A hash i basically a number, just with a base of 16. Having said that, you just have to convert it to an int.
The problem is, when you convert a hash into a int like so:
echo intval(hash('sha256','asdf'),16);
You will always get 2147483647 for 32-bit systems, which is the maximum value for intval.
My suggestion would be to cut the value of the hash to a few first characters like so
echo intval(substr(hash('sha256','bsbaf'),0,6),16);
This is now the seed for the random number, you can get the random by:
$hash = hash('sha256','bsbaf');
$seed = intval(substr($hash,0,6),16);
mt_srand($seed);
echo mt_rand(0,15000000);
Note that in some cases, you can have collisions, because you are using only the first 6 chars of the hash, but for most uses this will not be a problem. It's up to you to decide if this is acceptable since I do not know your specific use case.
UPDATE: An alternative is to make modulo from the resulting seed - like so:
$hash = hash('sha256','bsbaf');
$seed = intval(substr($hash,0,6),16);
echo $seed % 15000000;
PHP's "shm_get" function requires an integer semaphore key, which I realise to be a restriction of the underlying OS.
I am using the "sha1" function to hash some user input and using the hash to uniquely identify a number of resulting files and and background processes.
Is there a way to convince shm_get to accept an alphanumeric key or to convert a sha1 hash to an acceptable integer?
You can convert a hexadecimal number into a decimal number by using hexdec()
However if you have got a large number in your hash, this won't return an integer. But you need an integer. So you might want to cut it apart and only use a part of the hash.
$hash = sha1('http://www.hashcat.net/');
$hash = substr($hash, 0, 15); // ok on 64bit systems
$number = (int) hexdec($hash); // cap to PHP_INT_MAX anyway
var_dump($hash, $number);
In my user database table, I take the MD5 hash of the email address of a user as the id.
Example: email(example#example.org) = id(d41d8cd98f00b204e9800998ecf8427e)
Unfortunately, I have to represent the ids as integer values now - in order to be able to use an API where the id can only be an integer.
Now I'm looking for a way to encode the id into an integer for sending an decode it again when receiving. How could I do this?
My ideas so far:
convert_uuencode() and convert_uudecode() for the MD5 hash
replace every character of the MD5 hash by its ord() value
Which approach is better? Do you know even better ways to do this?
I hope you can help me. Thank you very much in advance!
Be careful. Converting the MD5s to an integer will require support for big (128-bit) integers. Chances are the API you're using will only support 32-bit integers - or worse, might be dealing with the number in floating-point. Either way, your ID will get munged. If this is the case, just assigning a second ID arbitrarily is a much better way to deal with things than trying to convert the MD5 into an integer.
However, if you are sure that the API can deal with arbitrarily large integers without trouble, you can just convert the MD5 from hexadecimal to an integer. PHP most likely does not support this built-in however, as it will try to represent it as either a 32-bit integer or a floating point; you'll probably need to use the PHP GMP library for it.
There are good reasons, stated by others, for doing it a different way.
But if what you want to do is convert an md5 hash into a string
of decimal digits (which is what I think you really mean by
"represent by an integer", since an md5 is already an integer in string form),
and transform it back into the same md5 string:
function md5_hex_to_dec($hex_str)
{
$arr = str_split($hex_str, 4);
foreach ($arr as $grp) {
$dec[] = str_pad(hexdec($grp), 5, '0', STR_PAD_LEFT);
}
return implode('', $dec);
}
function md5_dec_to_hex($dec_str)
{
$arr = str_split($dec_str, 5);
foreach ($arr as $grp) {
$hex[] = str_pad(dechex($grp), 4, '0', STR_PAD_LEFT);
}
return implode('', $hex);
}
Demo:
$md5 = md5('example#example.com');
echo $md5 . '<br />'; // 23463b99b62a72f26ed677cc556c44e8
$dec = md5_hex_to_dec($md5);
echo $dec . '<br />'; // 0903015257466342942628374306682186817640
$hex = md5_dec_to_hex($dec);
echo $hex; // 23463b99b62a72f26ed677cc556c44e8
Of course, you'd have to be careful using either string, like making sure to use them only as string type to avoid losing leading zeros, ensuring the strings are the correct lengths, etc.
A simple solution could use hexdec() for conversions for parts of the hash.
Systems that can accommodate 64-bit Ints can split the 128-bit/16-byte md5() hash into four 4-byte sections and then convert each into representations of unsigned 32-bit Ints. Each hex pair represents 1 byte, so use 8 character chunks:
$hash = md5($value);
foreach (str_split($hash, 8) as $chunk) {
$int_hashes[] = hexdec($chunk);
}
On the other end, use dechex() to convert the values back:
foreach ($int_hashes as $ihash) {
$original_hash .= dechex($ihash);
}
Caveat: Due to underlying deficiencies with how PHP handles integers and how it implements hexdec() and intval(), this strategy will not work with 32-bit systems.
Edit Takeaways:
Ints in PHP are always signed, there are no unsigned Ints.
Although intval() may be useful for certain cases, hexdec() is more performant and more simple to use for base-16.
hexdec() converts values above 7fffffffffffffff into Floats, making its use moot for splitting the hash into two 64-bit/8-byte chunks.
Similarly for intval($chunk, 16), it returns the same Int value for 7fffffffffffffff and above.
Why ord()? md5 produce normal 16-byte value, presented to you in hex for better readability. So you can't convert 16-byte value to 4 or 8 byte integer without loss. You must change some part of your algoritms to use this as id.
You could use hexdec to parse the hexadecimal string and store the number in the database.
Couldn't you just add another field that was an auto-increment int field?
what about:
$float = hexdec(md5('string'));
or
$int = (integer) (substr(hexdec(md5('string')),0,9)*100000000);
Definitely bigger chances for collision but still good enaugh to use instead of hash in DB though?
Add these two columns to your table.
`email_md5_l` bigint(20) UNSIGNED GENERATED ALWAYS AS (conv(left(md5(`email`),16),16,10)) STORED,
`email_md5_r` bigint(20) UNSIGNED GENERATED ALWAYS AS (conv(right(md5(`email`),16),16,10)) STORED,
It might or might not help to create a PK on these two columns though, as it probably concatenates two string representations and hashes the result. It would kind of defeat your purpose and a full scan might be quicker but that depends on number of columns and records. Don't try to read these bigints in php as it doesn't have unsigned integers, just stay in SQL and do something like:
select email
into result
from `address`
where url_md5_l = conv(left(md5(the_email), 16), 16, 10)
and url_md5_r = conv(right(md5(the_email), 16), 16, 10)
limit 1;
MD5 does collide btw.
Use the email address as the file name of a blank, temporary file in a shared folder, like /var/myprocess/example#example.org
Then, call ftok on the file name. ftok will return a unique, integer ID.
It won't be guaranteed to be unique though, but it will probably suffice for your API.