Update base_58 library.

This commit is contained in:
SerHack 2019-06-16 10:09:43 +02:00 committed by GitHub
parent 596a1e29ad
commit fea43715ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,120 +1,135 @@
<?php <?php
/*
* base58.php
*
* PHP Base58 codec
*
* Based on https://github.com/MoneroPy/moneropy/base58.py and https://github.com/mymonero/mymonero-core-js/cryptonote_utils/cryptonote_base58.js
*
*/
defined( 'ABSPATH' ) || exit;
class Monero_base58 {
/** /**
* @var string *
* monerophp/base58
*
* A PHP Base58 codec
* https://github.com/monero-integrations/monerophp
*
* Using work from
* bigreddmachine [MoneroPy] (https://github.com/bigreddmachine)
* Paul Shapiro [mymonero-core-js] (https://github.com/paulshapiro)
*
* @author Monero Integrations Team <support@monerointegrations.com> (https://github.com/monero-integrations)
* @copyright 2018
* @license MIT
*
* ============================================================================
*
* // Initialize class
* $base58 = new base58();
*
* // Encode a hexadecimal (base16) string as base58
* $encoded = $base58->encode('0137F8F06C971B168745F562AA107B4D172F336271BC0F9D3B510C14D3460DFB27D8CEBE561E73AC1E11833D5EA40200EB3C82E9C66ACAF1AB1A6BB53C40537C0B7A22160B0E');
*
* // Decode
* $decoded = $base58->decode('479cG5opa54beQWSyqNoWw5tna9sHUNmMTtiFqLPaUhDevpJ2YLwXAggSx5ePdeFrYF8cdbmVRSmp1Kn3t4Y9kFu7rZ7pFw');
*
*/ */
use Exception;
class Monero_base58
{
static $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; static $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
static $encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11]; static $encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
static $full_block_size = 8; static $full_block_size = 8;
static $full_encoded_block_size = 11; static $full_encoded_block_size = 11;
/** /**
* *
* Convert a hexadecimal string to a binary array * Convert a hexadecimal string to a binary array
* *
* @param string $hex A hexadecimal string to convert to a binary array * @param string $hex A hexadecimal string to convert to a binary array
*
* @return array * @return array
* *
*/ */
private function hex_to_bin($hex) { private function hex_to_bin($hex)
{
if (gettype($hex) != 'string') { if (gettype($hex) != 'string') {
throw new Exception('base58->hex_to_bin(): Invalid input type (must be a string)'); throw new Exception('base58->hex_to_bin(): Invalid input type (must be a string)');
} }
if (strlen($hex) % 2 != 0) { if (strlen($hex) % 2 != 0) {
throw new Exception('base58->hex_to_bin(): Invalid input length (must be even)'); throw new Exception('base58->hex_to_bin(): Invalid input length (must be even)');
} }
$res = array_fill(0, strlen($hex) / 2, 0); $res = array_fill(0, strlen($hex) / 2, 0);
for ($i = 0; $i < strlen($hex) / 2; $i++) { for ($i = 0; $i < strlen($hex) / 2; $i++) {
$res[$i] = intval(substr($hex, $i * 2, $i * 2 + 2 - $i * 2), 16); $res[$i] = intval(substr($hex, $i * 2, $i * 2 + 2 - $i * 2), 16);
} }
return $res; return $res;
} }
/** /**
* *
* Convert a binary array to a hexadecimal string * Convert a binary array to a hexadecimal string
* *
* @param array $bin A binary array to convert to a hexadecimal string * @param array $bin A binary array to convert to a hexadecimal string
*
* @return string * @return string
* *
*/ */
private function bin_to_hex($bin) { private function bin_to_hex($bin)
{
if (gettype($bin) != 'array') { if (gettype($bin) != 'array') {
throw new Exception('base58->bin_to_hex(): Invalid input type (must be an array)'); throw new Exception('base58->bin_to_hex(): Invalid input type (must be an array)');
} }
$res = []; $res = [];
for ($i = 0; $i < count($bin); $i++) { for ($i = 0; $i < count($bin); $i++) {
$res[] = substr('0'.dechex($bin[$i]), -2); $res[] = substr('0'.dechex($bin[$i]), -2);
} }
return join($res); return join($res);
} }
/** /**
* *
* Convert a string to a binary array * Convert a string to a binary array
* *
* @param string $str A string to convert to a binary array * @param string $str A string to convert to a binary array
*
* @return array * @return array
* *
*/ */
private function str_to_bin($str) { private function str_to_bin($str)
{
if (gettype($str) != 'string') { if (gettype($str) != 'string') {
throw new Exception('base58->str_to_bin(): Invalid input type (must be a string)'); throw new Exception('base58->str_to_bin(): Invalid input type (must be a string)');
} }
$res = array_fill(0, strlen($str), 0); $res = array_fill(0, strlen($str), 0);
for ($i = 0; $i < strlen($str); $i++) { for ($i = 0; $i < strlen($str); $i++) {
$res[$i] = ord($str[$i]); $res[$i] = ord($str[$i]);
} }
return $res; return $res;
} }
/** /**
* *
* Convert a binary array to a string * Convert a binary array to a string
* *
* @param array $bin A binary array to convert to a string * @param array $bin A binary array to convert to a string
*
* @return string * @return string
* *
*/ */
private function bin_to_str($bin) { private function bin_to_str($bin)
{
if (gettype($bin) != 'array') { if (gettype($bin) != 'array') {
throw new Exception('base58->bin_to_str(): Invalid input type (must be an array)'); throw new Exception('base58->bin_to_str(): Invalid input type (must be an array)');
} }
$res = array_fill(0, count($bin), 0); $res = array_fill(0, count($bin), 0);
for ($i = 0; $i < count($bin); $i++) { for ($i = 0; $i < count($bin); $i++) {
$res[$i] = chr($bin[$i]); $res[$i] = chr($bin[$i]);
} }
return preg_replace('/[[:^print:]]/', '', join($res)); // preg_replace necessary to strip errant non-ASCII characters eg. '' return preg_replace('/[[:^print:]]/', '', join($res)); // preg_replace necessary to strip errant non-ASCII characters eg. ''
} }
/** /**
* *
* Convert a UInt8BE (one unsigned big endian byte) array to UInt64 * Convert a UInt8BE (one unsigned big endian byte) array to UInt64
* *
* @param array $data A UInt8BE array to convert to UInt64 * @param array $data A UInt8BE array to convert to UInt64
*
* @return number * @return number
* *
*/ */
private function uint8_be_to_64($data) { private function uint8_be_to_64($data)
{
if (gettype($data) != 'array') { if (gettype($data) != 'array') {
throw new Exception ('base58->uint8_be_to_64(): Invalid input type (must be an array)'); throw new Exception ('base58->uint8_be_to_64(): Invalid input type (must be an array)');
} }
$res = 0; $res = 0;
$i = 0; $i = 0;
switch (9 - count($data)) { switch (9 - count($data)) {
@ -140,17 +155,18 @@ class Monero_base58 {
} }
return $res; return $res;
} }
/** /**
* *
* Convert a UInt64 (unsigned 64 bit integer) to a UInt8BE array * Convert a UInt64 (unsigned 64 bit integer) to a UInt8BE array
* *
* @param number $num A UInt64 number to convert to a UInt8BE array * @param number $num A UInt64 number to convert to a UInt8BE array
* @param integer $size Size of array to return * @param integer $size Size of array to return
*
* @return array * @return array
* *
*/ */
private function uint64_to_8_be($num, $size) { private function uint64_to_8_be($num, $size)
{
if (gettype($num) != ('integer' || 'double')) { if (gettype($num) != ('integer' || 'double')) {
throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($num must be a number)'); throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($num must be a number)');
} }
@ -160,7 +176,6 @@ class Monero_base58 {
if ($size < 1 || $size > 8) { if ($size < 1 || $size > 8) {
throw new Exception ('base58->uint64_to_8_be(): Invalid size (1 <= $size <= 8)'); throw new Exception ('base58->uint64_to_8_be(): Invalid size (1 <= $size <= 8)');
} }
$res = array_fill(0, $size, 0); $res = array_fill(0, $size, 0);
for ($i = $size - 1; $i >= 0; $i--) { for ($i = $size - 1; $i >= 0; $i--) {
$res[$i] = bcmod($num, bcpow(2, 8)); $res[$i] = bcmod($num, bcpow(2, 8));
@ -168,7 +183,6 @@ class Monero_base58 {
} }
return $res; return $res;
} }
/** /**
* *
* Convert a hexadecimal (Base16) array to a Base58 string * Convert a hexadecimal (Base16) array to a Base58 string
@ -176,10 +190,12 @@ class Monero_base58 {
* @param array $data * @param array $data
* @param array $buf * @param array $buf
* @param number $index * @param number $index
*
* @return array * @return array
* *
*/ */
private function encode_block($data, $buf, $index) { private function encode_block($data, $buf, $index)
{
if (gettype($data) != 'array') { if (gettype($data) != 'array') {
throw new Exception('base58->encode_block(): Invalid input type ($data must be an array)'); throw new Exception('base58->encode_block(): Invalid input type ($data must be an array)');
} }
@ -192,7 +208,6 @@ class Monero_base58 {
if (count($data) < 1 or count($data) > self::$full_encoded_block_size) { if (count($data) < 1 or count($data) > self::$full_encoded_block_size) {
throw new Exception('base58->encode_block(): Invalid input length (1 <= count($data) <= 8)'); throw new Exception('base58->encode_block(): Invalid input length (1 <= count($data) <= 8)');
} }
$num = self::uint8_be_to_64($data); $num = self::uint8_be_to_64($data);
$i = self::$encoded_block_sizes[count($data)] - 1; $i = self::$encoded_block_sizes[count($data)] - 1;
while ($num > 0) { while ($num > 0) {
@ -203,45 +218,36 @@ class Monero_base58 {
} }
return $buf; return $buf;
} }
/** /**
* *
* Encode a hexadecimal (Base16) string to Base58 * Encode a hexadecimal (Base16) string to Base58
* *
* @param string $hex A hexadecimal (Base16) string to convert to Base58 * @param string $hex A hexadecimal (Base16) string to convert to Base58
*
* @return string * @return string
* *
*/ */
public function encode($hex) { public function encode($hex)
{
if (gettype($hex) != 'string') { if (gettype($hex) != 'string') {
throw new Exception ('base58->encode(): Invalid input type (must be a string)'); throw new Exception ('base58->encode(): Invalid input type (must be a string)');
} }
$data = self::hex_to_bin($hex); $data = self::hex_to_bin($hex);
if (count($data) == 0) { if (count($data) == 0) {
return ''; return '';
} }
$full_block_count = floor(count($data) / self::$full_block_size); $full_block_count = floor(count($data) / self::$full_block_size);
$last_block_size = count($data) % self::$full_block_size; $last_block_size = count($data) % self::$full_block_size;
$res_size = $full_block_count * self::$full_encoded_block_size + self::$encoded_block_sizes[$last_block_size]; $res_size = $full_block_count * self::$full_encoded_block_size + self::$encoded_block_sizes[$last_block_size];
$res = array_fill(0, $res_size, ord(self::$alphabet[0]));
$res = array_fill(0, $res_size, 0);
for ($i = 0; $i < $res_size; $i++) {
$res[$i] = self::$alphabet[0];
}
for ($i = 0; $i < $full_block_count; $i++) { for ($i = 0; $i < $full_block_count; $i++) {
$res = self::encode_block(array_slice($data, $i * self::$full_block_size, ($i * self::$full_block_size + self::$full_block_size) - ($i * self::$full_block_size)), $res, $i * self::$full_encoded_block_size); $res = self::encode_block(array_slice($data, $i * self::$full_block_size, ($i * self::$full_block_size + self::$full_block_size) - ($i * self::$full_block_size)), $res, $i * self::$full_encoded_block_size);
} }
if ($last_block_size > 0) { if ($last_block_size > 0) {
$res = self::encode_block(array_slice($data, $full_block_count * self::$full_block_size, $full_block_count * self::$full_block_size + $last_block_size), $res, $full_block_count * self::$full_encoded_block_size); $res = self::encode_block(array_slice($data, $full_block_count * self::$full_block_size, $full_block_count * self::$full_block_size + $last_block_size), $res, $full_block_count * self::$full_encoded_block_size);
} }
return self::bin_to_str($res); return self::bin_to_str($res);
} }
/** /**
* *
* Convert a Base58 input to hexadecimal (Base16) * Convert a Base58 input to hexadecimal (Base16)
@ -249,10 +255,12 @@ class Monero_base58 {
* @param array $data * @param array $data
* @param array $buf * @param array $buf
* @param integer $index * @param integer $index
*
* @return array * @return array
* *
*/ */
private function decode_block($data, $buf, $index) { private function decode_block($data, $buf, $index)
{
if (gettype($data) != 'array') { if (gettype($data) != 'array') {
throw new Exception('base58->decode_block(): Invalid input type ($data must be an array)'); throw new Exception('base58->decode_block(): Invalid input type ($data must be an array)');
} }
@ -262,12 +270,10 @@ class Monero_base58 {
if (gettype($index) != ('integer' || 'double')) { if (gettype($index) != ('integer' || 'double')) {
throw new Exception('base58->decode_block(): Invalid input type ($index must be a number)'); throw new Exception('base58->decode_block(): Invalid input type ($index must be a number)');
} }
$res_size = self::index_of(self::$encoded_block_sizes, count($data)); $res_size = self::index_of(self::$encoded_block_sizes, count($data));
if ($res_size <= 0) { if ($res_size <= 0) {
throw new Exception('base58->decode_block(): Invalid input length ($data must be a value from base58::$encoded_block_sizes)'); throw new Exception('base58->decode_block(): Invalid input length ($data must be a value from base58::$encoded_block_sizes)');
} }
$res_num = 0; $res_num = 0;
$order = 1; $order = 1;
for ($i = count($data) - 1; $i >= 0; $i--) { for ($i = count($data) - 1; $i >= 0; $i--) {
@ -275,12 +281,10 @@ class Monero_base58 {
if ($digit < 0) { if ($digit < 0) {
throw new Exception("base58->decode_block(): Invalid character ($digit \"{$digit}\" not found in base58::$alphabet)"); throw new Exception("base58->decode_block(): Invalid character ($digit \"{$digit}\" not found in base58::$alphabet)");
} }
$product = bcadd(bcmul($order, $digit), $res_num); $product = bcadd(bcmul($order, $digit), $res_num);
if ($product > bcpow(2, 64)) { if ($product > bcpow(2, 64)) {
throw new Exception('base58->decode_block(): Integer overflow ($product exceeds the maximum 64bit integer)'); throw new Exception('base58->decode_block(): Integer overflow ($product exceeds the maximum 64bit integer)');
} }
$res_num = $product; $res_num = $product;
$order = bcmul($order, 58); $order = bcmul($order, 58);
} }
@ -294,20 +298,20 @@ class Monero_base58 {
} }
return $buf; return $buf;
} }
/** /**
* *
* Decode a Base58 string to hexadecimal (Base16) * Decode a Base58 string to hexadecimal (Base16)
* *
* @param string $hex A Base58 string to convert to hexadecimal (Base16) * @param string $hex A Base58 string to convert to hexadecimal (Base16)
*
* @return string * @return string
* *
*/ */
public function decode($enc) { public function decode($enc)
{
if (gettype($enc) != 'string') { if (gettype($enc) != 'string') {
throw new Exception ('base58->decode(): Invalid input type (must be a string)'); throw new Exception ('base58->decode(): Invalid input type (must be a string)');
} }
$enc = self::str_to_bin($enc); $enc = self::str_to_bin($enc);
if (count($enc) == 0) { if (count($enc) == 0) {
return ''; return '';
@ -315,21 +319,19 @@ class Monero_base58 {
$full_block_count = floor(bcdiv(count($enc), self::$full_encoded_block_size)); $full_block_count = floor(bcdiv(count($enc), self::$full_encoded_block_size));
$last_block_size = bcmod(count($enc), self::$full_encoded_block_size); $last_block_size = bcmod(count($enc), self::$full_encoded_block_size);
$last_block_decoded_size = self::index_of(self::$encoded_block_sizes, $last_block_size); $last_block_decoded_size = self::index_of(self::$encoded_block_sizes, $last_block_size);
$data_size = $full_block_count * self::$full_block_size + $last_block_decoded_size; $data_size = $full_block_count * self::$full_block_size + $last_block_decoded_size;
if ($data_size == -1) {
return '';
}
$data = array_fill(0, $data_size, 0); $data = array_fill(0, $data_size, 0);
for ($i = 0; $i < $full_block_count; $i++) { for ($i = 0; $i <= $full_block_count; $i++) {
$data = self::decode_block(array_slice($enc, $i * self::$full_encoded_block_size, ($i * self::$full_encoded_block_size + self::$full_encoded_block_size) - ($i * self::$full_encoded_block_size)), $data, $i * self::$full_block_size); $data = self::decode_block(array_slice($enc, $i * self::$full_encoded_block_size, ($i * self::$full_encoded_block_size + self::$full_encoded_block_size) - ($i * self::$full_encoded_block_size)), $data, $i * self::$full_block_size);
} }
if ($last_block_size > 0) { if ($last_block_size > 0) {
$data = self::decode_block(array_slice($enc, $full_block_count * self::$full_encoded_block_size, $full_block_count * self::$full_encoded_block_size + $last_block_size), $data, $full_block_count * self::$full_block_size); $data = self::decode_block(array_slice($enc, $full_block_count * self::$full_encoded_block_size, $full_block_count * self::$full_encoded_block_size + $last_block_size), $data, $full_block_count * self::$full_block_size);
} }
return self::bin_to_hex($data); return self::bin_to_hex($data);
} }
/** /**
* *
* Search an array for a value * Search an array for a value
@ -337,17 +339,18 @@ class Monero_base58 {
* *
* @param array $haystack An array to search * @param array $haystack An array to search
* @param string $needle A string to search for * @param string $needle A string to search for
*)
* @return number The index of the element found (or -1 for no match) * @return number The index of the element found (or -1 for no match)
* *
*/ */
private function index_of($haystack, $needle) { private function index_of($haystack, $needle)
{
if (gettype($haystack) != 'array') { if (gettype($haystack) != 'array') {
throw new Exception ('base58->decode(): Invalid input type ($haystack must be an array)'); throw new Exception ('base58->decode(): Invalid input type ($haystack must be an array)');
} }
// if (gettype($needle) != 'string') { // if (gettype($needle) != 'string') {
// throw new Exception ('base58->decode(): Invalid input type ($needle must be a string)'); // throw new Exception ('base58->decode(): Invalid input type ($needle must be a string)');
// } // }
foreach ($haystack as $key => $value) if ($value === $needle) return $key; foreach ($haystack as $key => $value) if ($value === $needle) return $key;
return -1; return -1;
} }