示例#1
0
 /**
  * Validate Public Key
  * 
  * Validates a public key by attempting to create a point on the
  * secp256k1 curve. 
  * 
  * @param	string	$public_key
  * @return	boolean
  */
 public static function validate_public_key($public_key)
 {
     if (strlen($public_key) == '66') {
         // Compressed key
         // Attempt to decompress the public key. If the point is not
         // generated, or the function fails, then the key is invalid.
         $decompressed = self::decompress_public_key($public_key);
         return $decompressed == TRUE;
     } else {
         if (strlen($public_key) == '130') {
             // Uncompressed key, try to create the point
             $curve = \SECcurve::curve_secp256k1();
             $generator = \SECcurve::generator_secp256k1();
             $x = substr($public_key, 2, 64);
             $y = substr($public_key, 66, 64);
             // Attempt to create the point. Point returns false in the
             // constructor if anything is invalid.
             try {
                 $point = new \Point($curve, gmp_strval(gmp_init($x, 16), 10), gmp_strval(gmp_init($y, 16), 10), $generator->getOrder());
             } catch (\Exception $e) {
                 return FALSE;
             }
             return TRUE;
         }
     }
     return FALSE;
 }
示例#2
0
 /**
  * Public Key From MPK
  * 
  * This function is used to generate a public key from the supplied
  * $mpk - the master public key, and an $iteration indicating which 
  * address in the sequence should be generated.
  * 
  * @param	string	$mpk
  * @param	int	$iteration
  * @return	string
  */
 public static function public_key_from_mpk($mpk, $iteration, $change = 0, $compressed = FALSE)
 {
     $change = $change == 0 ? '0' : '1';
     // Generate the curve, and the generator point.
     $curve = \SECcurve::curve_secp256k1();
     $gen = \SECcurve::generator_secp256k1();
     // Prepare the input values, by converting the MPK to X and Y coordinates
     $x = gmp_init(substr($mpk, 0, 64), 16);
     $y = gmp_init(substr($mpk, 64, 64), 16);
     // Generate a scalar from the $iteration and $mpk
     $z = gmp_init(hash('sha256', hash('sha256', "{$iteration}:{$change}:" . pack('H*', $mpk), TRUE)), 16);
     try {
         // Add the Point defined by $x and $y, to the result of EC multiplication of $z by $gen
         $pt = \Point::add(new \Point($curve, $x, $y), \Point::mul($z, $gen));
         // Generate the uncompressed public key.
         $keystr = '04' . str_pad(gmp_strval($pt->x, 16), 64, '0', STR_PAD_LEFT) . str_pad(gmp_strval($pt->y, 16), 64, '0', STR_PAD_LEFT);
     } catch (Exception $e) {
         throw new ErrorException($e->getMessage());
     }
     return $compressed == TRUE ? BitcoinLib::compress_public_key($keystr) : $keystr;
 }
示例#3
0
 public function decompress_public_key($key)
 {
     // Initialize
     $y_byte = substr($key, 0, 2);
     $x_coordinate = substr($key, 2);
     // Set variables
     $x = gmp_strval(gmp_init($x_coordinate, 16), 10);
     $curve = SECcurve::curve_secp256k1();
     $generator = SECcurve::generator_secp256k1();
     // Decode
     try {
         $x3 = NumberTheory::modular_exp($x, 3, $curve->getPrime());
         $y2 = gmp_add($x3, $curve->getB());
         $y0 = NumberTheory::square_root_mod_prime(gmp_strval($y2, 10), $curve->getPrime());
         if ($y0 === false) {
             return false;
         }
         $y1 = gmp_strval(gmp_sub($curve->getPrime(), $y0), 10);
         $y_coordinate = $y_byte == '02' ? gmp_Utils::gmp_mod2(gmp_init($y0, 10), 2) == '0' ? $y0 : $y1 : (gmp_Utils::gmp_mod2(gmp_init($y0, 10), 2) !== '0' ? $y0 : $y1);
         $y_coordinate = str_pad(gmp_strval($y_coordinate, 16), 64, '0', STR_PAD_LEFT);
         $point = new Point($curve, gmp_strval(gmp_init($x_coordinate, 16), 10), gmp_strval(gmp_init($y_coordinate, 16), 10), $generator->getOrder());
     } catch (Exception $e) {
         return false;
     }
     // Return
     return array('x' => $x_coordinate, 'y' => $y_coordinate, 'point' => $point, 'public_key' => '04' . $x_coordinate . $y_coordinate);
 }
示例#4
0
 /**
  * CKD
  * 
  * This recursive function accepts $master, a parent extended key, 
  * and an array of address bytes (the $address_definition tuple). It 
  * pop's the next value from the $address_definition tuple and 
  * generates the desired key. If the $address_definition tuple is 
  * empty, then it returns the key. If not, then it calls itself again 
  * with the new key and the tuple with the remaining key indexes to 
  * generate, but will terminate with an array containing the desired
  * key at index 0, and it's human readable definition in the second.
  * 
  * @param	string	$master
  * @param	array	$address_definition
  * @return	array
  */
 public static function CKD($master, $address_definition, $generated = array())
 {
     $previous = self::import($master);
     if ($previous['type'] == 'private') {
         $private_key = $previous['key'];
         $public_key = BitcoinLib::private_key_to_public_key($private_key, TRUE);
     } else {
         if ($previous['type'] == 'public') {
             $public_key = $previous['key'];
         } else {
             // Exception here?
             return FALSE;
         }
     }
     $fingerprint = substr(hash('ripemd160', hash('sha256', pack("H*", $public_key), TRUE)), 0, 8);
     $i = array_pop($address_definition);
     $is_prime = self::check_is_prime_hex($i);
     if ($is_prime == 1) {
         if ($previous['type'] == 'public') {
             return FALSE;
         }
         // Cannot derive private from public key - Exception here?
         $data = '00' . $private_key . $i;
     } else {
         if ($is_prime == 0) {
             $data = $public_key . $i;
         }
     }
     if (!isset($data)) {
         return FALSE;
     }
     $I = hash_hmac('sha512', pack("H*", $data), pack("H*", $previous['chain_code']));
     $I_l = substr($I, 0, 64);
     $I_r = substr($I, 64, 64);
     if (self::check_valid_hmac_key($I_l) == FALSE) {
         // Check the key is in a valid range.
         // calculate the next i in the sequence, and start over with that.
         $new_i = self::calc_address_bytes(self::get_address_number($i) + 1, $is_prime);
         array_push($address_definition, $new_i);
         return self::CKD($master, $address_definition, $generated);
     }
     // Keep a record of the address being built. Done after error
     // checking so only valid keys get to this point.
     if (count($generated) == 0 && $previous['depth'] == 0) {
         array_push($generated, $previous['type'] == 'private' ? 'm' : 'M');
     }
     array_push($generated, self::get_address_number($i, $is_prime) . ($is_prime == 1 ? "'" : NULL));
     $g = \SECcurve::generator_secp256k1();
     $n = $g->getOrder();
     if ($previous['type'] == 'private') {
         // (Il + kpar) mod n
         $key = str_pad(gmp_strval(\gmp_Utils::gmp_mod2(gmp_add(gmp_init($I_l, 16), gmp_init($private_key, 16)), $n), 16), 64, '0', STR_PAD_LEFT);
     } else {
         if ($previous['type'] == 'public') {
             // newPoint + parentPubkeyPoint
             $decompressed = BitcoinLib::decompress_public_key($public_key);
             // Can return FALSE. Throw exception?
             $curve = \SECcurve::curve_secp256k1();
             // Prepare offset, by multiplying Il by g, and adding this to the previous public key point.
             // Create a new point by adding the two.
             $new_point = \Point::add(\Point::mul(gmp_init($I_l, 16), $g), $decompressed['point']);
             $new_x = str_pad(gmp_strval($new_point->getX(), 16), 64, '0', STR_PAD_LEFT);
             $new_y = str_pad(gmp_strval($new_point->getY(), 16), 64, '0', STR_PAD_LEFT);
             $key = BitcoinLib::compress_public_key('04' . $new_x . $new_y);
         }
     }
     if (!isset($key)) {
         return FALSE;
     }
     $data = array('network' => $previous['network'], 'testnet' => $previous['testnet'], 'magic_bytes' => $previous['magic_bytes'], 'type' => $previous['type'], 'depth' => $previous['depth'] + 1, 'fingerprint' => $fingerprint, 'i' => $i, 'address_number' => self::get_address_number($i), 'chain_code' => $I_r, 'key' => $key);
     return count($address_definition) > 0 ? self::CKD(self::encode($data), $address_definition, $generated) : array(self::encode($data), implode('/', $generated));
 }