The $hash identifier calculates a simple hash of the supplied text. Hash is shown as a decimal number in the range from 1 to 2^32-1. You should probably avoid using this hash for reasons explained in the Notes section.
Contents
Synopsis
$hash(<text>,<N>)
Switches
None
Parameters
text text string or %variable to be hashed
N Bit length for the returned hash. $hash returns $null if N is not in the range 2-32
Properties
None
Example
//echo -a The hash is $hash(test,32) returns: The hash is 1702094848
Notes
The 'fakehash' alias below is based on the code posted here: https://forums.mirc.com/ubbthreads.php/topics/98371/Re:_$hash_function#Post98371
$hash uses a hash function that is very weak for several reasons. The weaknesses are more easily seen by showing an alias which mimics $hash output, and showing $hash output in hex. Some weaknesses include:
- 1. Similar text have similar hash. Strings of 1-3 bytes are most obvious:
//echo -a $base($hash(abc,32),10,16) is $+($base($asc(a),10,16,2),$base($asc(b),10,16,2),$base($asc(c),10,16,2),00) returns: 61626300 is 61626300
- 2. When N is greater than 24, the rightmost extra bits above 24 are always zero, making it effectively a 24-bit hash not a 32. Only 2^24 of the values within the range of 0 - 2^32-1 can possibly be hashes. i.e. a 27-bit hash always has the least-significant 3 bits as zero:
//var %i 99999 | while (%i) { var %n $rand(1,99999999) , %bits $rand(25,32) | if ( $right($base($hash(%n,%bits),10,2,%bits) , $calc(%bits -24) )) echo -a this message will never show | dec %i }
- 3. One of the properties of good hash hash functions is that each changed bit of the input should change close to half the bits in a seemingly-random pattern. Changing 1 bit of the text has minimal change of the bits in the $hash output, often changing just 1 bit. It often takes several additional bytes before the bits set by the first byte are altered.
//echo -a $base($hash(mIRC,32),10,16) / $base($hash(mIRD,32),10,16) returns: 4952B000 / 4952B100
- 4. It's easy to create duplicate hashes, especially when the length of the text is a multiple of 3:
//echo -a $base($hash(ABCDEF,32),10,16) / $base($hash(ABDDEE,32),10,16)
Instead of using $hash, you would be better off using other substitutes. For example, if you need it to be a decimal number with a variable number of bits from 1-32, use $crc then convert to decimal then reduce the number of bits:
alias crchash { return $calc( $base($crc($1,0),16,10) % 2^$2 ) }
If the hash needs to be crypto-level secure, use 8 bits from $sha1 or $sha512 instead of from $crc.
When the fakehash alias is in a remotes script, you should get the same answers from $fakehash as from $hash:
//var %i 999 | while (%i) { var %text $rand(1,999999999) , %bits $rand(16,24) | if ($hash(%text,%bits) != $fakehash(%text,%bits)) echo -a this should never show: %text %bits | dec %i } alias fakehash { if ( ($1 == $null) || ($2 !isnum 2-32) ) return $null var %i 1 | var %len $len(%string) | var %x 0 | var %bits $int($2) while (%i <= $len($1)) { var %y $int($calc( $and(%x,$base(ff000000,16,10)) / 2^24 )) var %x = $calc( %x + %y + $asc($mid($1,%i,1)) ) var %x = $calc( (%x * 256) % (2^32) ) inc %i } var %y = $base(%x,10,2,32) var %z = $base($left(%y,%bits),2,10) if ($mid(%y,$calc(1+%bits))) inc %z return $calc( %z % (2^%bits) ) }
Compatibility
Added: mIRC v5.4
Added on: 23 Jun 1998
Note: Unless otherwise stated, this was the date of original functionality.
Further enhancements may have been made in later versions.