Timo Huovinen
Timo Huovinen

Reputation: 55623

php: number only hash?

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...

  1. this makes some people look more important
  2. 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

Upvotes: 52

Views: 58835

Answers (7)

Ben
Ben

Reputation: 1

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.

Upvotes: 0

Irfandi D. Vendy
Irfandi D. Vendy

Reputation: 1004

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,

Upvotes: 0

Reynel Fals de Pedro
Reynel Fals de Pedro

Reputation: 111

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.

Upvotes: 8

Thomas
Thomas

Reputation: 2445

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 '<a href="do.php?userid='. numcrypt($id) . '"> go </a>';

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.

Upvotes: 16

derekerdmann
derekerdmann

Reputation: 18252

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) );

Upvotes: 71

David Titarenco
David Titarenco

Reputation: 33396

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.

Upvotes: 20

tdammers
tdammers

Reputation: 20721

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.

Upvotes: 1

Related Questions