= 8; if (ctype_upper($ch) !== $upper) { return 'checksum'; } } return ''; } /** Keccak-256 (the original padding Ethereum uses, not NIST SHA3-256) of $message, as lower-case hex. */ public static function keccak256($message) { $rate = 136; $state = array_fill(0, 25, 0); $message .= "\x01" . str_repeat("\x00", ($rate - (strlen($message) + 1) % $rate) % $rate); $message[strlen($message) - 1] = chr(ord($message[strlen($message) - 1]) | 0x80); for ($offset = 0, $n = strlen($message); $offset < $n; $offset += $rate) { for ($lane = 0; $lane < 17; $lane++) { $state[$lane] ^= self::lane(substr($message, $offset + $lane * 8, 8)); } self::permute($state); } $out = ''; for ($lane = 0; $lane < 4; $lane++) { for ($byte = 0; $byte < 8; $byte++) { $out .= chr(($state[$lane] >> (8 * $byte)) & 0xFF); } } return bin2hex($out); } /** Eight bytes, little-endian, as one 64-bit lane. */ private static function lane($bytes) { $v = 0; for ($i = 7; $i >= 0; $i--) { $v = ($v << 8) | ord($bytes[$i]); } return $v; } /** 64-bit rotate left. PHP's >> is arithmetic, so the shifted-in sign bits are masked off. */ private static function rotl($v, $n) { if (0 === $n) { return $v; } return ($v << $n) | (($v >> (64 - $n)) & ~(-1 << $n)); } private static function permute(array &$a) { foreach (self::ROUND_CONSTANTS as $rc) { // theta $c = array(); for ($x = 0; $x < 5; $x++) { $c[$x] = $a[$x] ^ $a[$x + 5] ^ $a[$x + 10] ^ $a[$x + 15] ^ $a[$x + 20]; } for ($x = 0; $x < 5; $x++) { $d = $c[($x + 4) % 5] ^ self::rotl($c[($x + 1) % 5], 1); for ($y = 0; $y < 25; $y += 5) { $a[$x + $y] ^= $d; } } // rho and pi $b = array(); for ($x = 0; $x < 5; $x++) { for ($y = 0; $y < 5; $y++) { $b[$y + 5 * ((2 * $x + 3 * $y) % 5)] = self::rotl($a[$x + 5 * $y], self::ROTATIONS[$x + 5 * $y]); } } // chi for ($y = 0; $y < 25; $y += 5) { for ($x = 0; $x < 5; $x++) { $a[$x + $y] = $b[$x + $y] ^ (~$b[($x + 1) % 5 + $y] & $b[($x + 2) % 5 + $y]); } } // iota $a[0] ^= ($rc[0] << 32) | $rc[1]; } } }