<?php
# This function determines if a number is prime.
# See second if statement of HavokN.
function is_prime( int $number )
{
if ($number < 2) {
return false;
} else {
for ($i = 2; $i <= sqrt($number); $i++) {
if ($number % $i == 0) {
return false;
}
}
return true;
}
}
function HavokN( $data, int $size, int $iterations = 0 ) {
# If the amount of bytes requested is equal to or greater than what the
# CPU supports, don't let the user continue because it /will not/ work.
if ( $size > PHP_INT_SIZE*8 ) {
trigger_error("Requested hash size is greater than the bit depth of your processor", E_USER_ERROR);
}
# Calculate the size once and re-use it instead of recalculating it every
# time we need it. This should greatly improve the performance of the
# following if statement.
$exponatedSize = ((2 ** $size)-1);
# Don't bother if the requested hash size is already a prime number, but
# if it isn't, keep subtracting 1 from the size until we find one that is
# prime. This is probably terribly inefficient, but it gets the job done.
if( is_prime($exponatedSize) || $size == 1 ) {
$prime = $exponatedSize;
} else {
for ( $i = $exponatedSize; !is_prime($i); $i-- ) {
$prime = $i-1;
}
}
# Tell PHP we can free that variable from RAM if needed (it'll optimize for
# CPU cycles or RAM, whichever is more neccessary) now that we're done with
# it.
unset($exponatedSize);
# Unpacks the input data into an array of 8-bit bytes
$data = unpack('C*', $data);
$count = count($data)+1;
$sum1 = 0;
$sum2 = 0;
# This is where the algorithm actually starts
for ( $index = 1; $index < $count; $index++ ) {
$sum1 = ($sum1 + $data[$index]) % $prime;
$sum2 = ($sum2 + $sum1) % $prime;
}
# '<<' is an operator that bitshifts the number on the left by the number
# on the right. It needs to be half of the length because $sum1 is
# (theoretically) half the length of sum2, and we're basically just
# appending $sum1 to $sum2 but in a much more well-defined way than a more
# PHP-y way could be.
# Then it takes that and bitwise-or it against sum1.
$combinedsum = ($sum2 << intdiv($size,2)) | $sum1;
# This impliments a very basic iterative function TO THE FINAL CALCULATED
# CHECKSUM. This is helpful for if you want similar inputs' checksums to
# look more different from eachother.
while($iterations > 0) {
$combinedsum = ($combinedsum * $size) % $prime;
$iterations--;
}
# Finally, return whatever we calculated
return $combinedsum;
}
if ( defined('STDIN') && str_contains($argv[0], basename(__FILE__)) ){
if ( !isset($argv[1]) ) {
echo "Usage: php ".basename(__FILE__)." <string|file> [depth] [iterations]";
exit(1);
}
if ( !isset($argv[2]) ) {
$argv[2] = 8;
}
if ( !isset($argv[3]) ) {
$argv[3] = 0;
}
if (file_exists($argv[1])) {
$input = file_get_contents("$argv[1]");
} else {
$input = $argv[1];
}
echo HavokN($input, $argv[2], (int)$argv[3])."\n";
}
?>