diff --git a/Examples/generateAddress.php b/Examples/generateAddress.php new file mode 100644 index 0000000..c89d75a --- /dev/null +++ b/Examples/generateAddress.php @@ -0,0 +1,17 @@ +generateRandomPrivateKey(); //generate new random private key +$address = $bitcoinECDSA->getAddress(); //compressed Bitcoin address +echo "Address: " . $address . PHP_EOL; + +//Validate an address (Verify the checksum) +if($bitcoinECDSA->validateAddress($address)) { + echo "The address is valid" . PHP_EOL; +} else { + echo "The address is invalid" . PHP_EOL; +} \ No newline at end of file diff --git a/Examples/signMessage.php b/Examples/signMessage.php new file mode 100644 index 0000000..2137b0c --- /dev/null +++ b/Examples/signMessage.php @@ -0,0 +1,36 @@ +generateRandomPrivateKey(); //generate new random private key + +$message = "Test message"; +$signedMessage = $bitcoinECDSA->signMessage($message); + +echo "signed message:" . PHP_EOL; +echo $signedMessage . PHP_EOL; + +/** + * Will print something like this: + +-----BEGIN BITCOIN SIGNED MESSAGE----- +Test message +-----BEGIN SIGNATURE----- +1L56ndSQ1LfrAB2xyo3ZN7egiW4nSs8KWS +HxTqM+b3xj2Qkjhhl+EoUpYsDUz+uTdz6RCY7Z4mV62yOXJ3XCAfkiHV+HGzox7Ba/OC6bC0y6zBX0GhB7UdEM0= +-----END BITCOIN SIGNED MESSAGE----- + */ + + +// If you only want the signature you can do this +$signature = $bitcoinECDSA->signMessage($message, true); + +echo "signature:" . PHP_EOL; +echo $signature . PHP_EOL; +/** + * Will print something like this: +HxTqM+b3xj2Qkjhhl+EoUpYsDUz+uTdz6RCY7Z4mV62yOXJ3XCAfkiHV+HGzox7Ba/OC6bC0y6zBX0GhB7UdEM0= + */ diff --git a/Examples/verifyMessage.php b/Examples/verifyMessage.php new file mode 100644 index 0000000..5fdd619 --- /dev/null +++ b/Examples/verifyMessage.php @@ -0,0 +1,32 @@ +checkSignatureForRawMessage($rawMessage)) { + echo "Message verified" . PHP_EOL; +} else { + echo "Couldn't verify message" . PHP_EOL; +} + +// alternatively +$signature = "HxTqM+b3xj2Qkjhhl+EoUpYsDUz+uTdz6RCY7Z4mV62yOXJ3XCAfkiHV+HGzox7Ba/OC6bC0y6zBX0GhB7UdEM0="; +$address = "1L56ndSQ1LfrAB2xyo3ZN7egiW4nSs8KWS"; +$message = "Test message"; + +if($bitcoinECDSA->checkSignatureForMessage($address, $signature, $message)) { + echo "Message verified" . PHP_EOL; +} else { + echo "Couldn't verify message" . PHP_EOL; +} \ No newline at end of file diff --git a/Examples/wif.php b/Examples/wif.php new file mode 100644 index 0000000..58874cd --- /dev/null +++ b/Examples/wif.php @@ -0,0 +1,25 @@ +generateRandomPrivateKey(); //generate new random private key + +$wif = $bitcoinECDSA->getWif(); +$address = $bitcoinECDSA->getAddress(); +echo "Address : " . $address . PHP_EOL; +echo "WIF : " . $wif . PHP_EOL; + +unset($bitcoinECDSA); //destroy instance + +//import wif +$bitcoinECDSA = new BitcoinECDSA(); +if($bitcoinECDSA->validateWifKey($wif)) { + $bitcoinECDSA->setPrivateKeyWithWif($wif); + $address = $bitcoinECDSA->getAddress(); + echo "imported address : " . $address . PHP_EOL; +} else { + echo "invalid WIF key" . PHP_EOL; +} \ No newline at end of file diff --git a/src/BitcoinPHP/BitcoinECDSA/BitcoinECDSA.php b/src/BitcoinPHP/BitcoinECDSA/BitcoinECDSA.php index f85a99a..d8157ea 100755 --- a/src/BitcoinPHP/BitcoinECDSA/BitcoinECDSA.php +++ b/src/BitcoinPHP/BitcoinECDSA/BitcoinECDSA.php @@ -243,26 +243,6 @@ public function base58_decode($encodedData, $littleEndian = true) return $res; } - /*** - * returns the private key under the Wallet Import Format - * - * @return string (base58) - * @throws \Exception - */ - public function getWif() - { - if(!isset($this->k)) - { - throw new \Exception('No Private Key was defined'); - } - - $k = $this->k; - $secretKey = '80' . $k; - $secretKey .= substr($this->hash256(hex2bin($secretKey)), 0, 8); - - return $this->base58_encode($secretKey); - } - /*** * Computes the result of a point addition and returns the resulting point as an Array. * @@ -602,9 +582,9 @@ public function getDerPubKeyWithPubKeyPoints($pubKey, $compressed = true) else { if(gmp_strval(gmp_mod(gmp_init($pubKey['y'], 16), gmp_init(2, 10))) === '0') - $pubKey = '02' . $pubKey['x']; //if $pubKey['y'] is even + $pubKey = '02' . $pubKey['x']; //if $pubKey['y'] is even else - $pubKey = '03' . $pubKey['x']; //if $pubKey['y'] is odd + $pubKey = '03' . $pubKey['x']; //if $pubKey['y'] is odd return $pubKey; } @@ -658,13 +638,13 @@ public function getPubKeyPoints() throw new \Exception('No Private Key was defined'); } - $pubKey = $this->mulPoint( + $pubKey = $this->mulPoint( $k, ['x' => $G['x'], 'y' => $G['y']] ); - $pubKey['x'] = gmp_strval($pubKey['x'], 16); - $pubKey['y'] = gmp_strval($pubKey['y'], 16); + $pubKey['x'] = gmp_strval($pubKey['x'], 16); + $pubKey['y'] = gmp_strval($pubKey['y'], 16); while(strlen($pubKey['x']) < 64) { @@ -708,9 +688,9 @@ public function getPubKey(array $pubKeyPts = []) $pubKeyPts = $this->getPubKeyPoints(); if(gmp_strval(gmp_mod(gmp_init($pubKeyPts['y'], 16), gmp_init(2, 10))) === '0') - $compressedPubKey = '02' . $pubKeyPts['x']; //if $pubKey['y'] is even + $compressedPubKey = '02' . $pubKeyPts['x']; //if $pubKey['y'] is even else - $compressedPubKey = '03' . $pubKeyPts['x']; //if $pubKey['y'] is odd + $compressedPubKey = '03' . $pubKeyPts['x']; //if $pubKey['y'] is odd return $compressedPubKey; } @@ -729,27 +709,27 @@ public function getUncompressedAddress($compressed = false, $derPubKey = null) if($derPubKey !== null) { if($compressed === true) { - $address = $this->getPubKey($this->getPubKeyPointsWithDerPubKey($derPubKey)); + $address = $this->getPubKey($this->getPubKeyPointsWithDerPubKey($derPubKey)); } else { - $address = $this->getUncompressedPubKey($this->getPubKeyPointsWithDerPubKey($derPubKey)); + $address = $this->getUncompressedPubKey($this->getPubKeyPointsWithDerPubKey($derPubKey)); } } else { if($compressed === true) { - $address = $this->getPubKey(); + $address = $this->getPubKey(); } else { - $address = $this->getUncompressedPubKey(); + $address = $this->getUncompressedPubKey(); } } - $address = $this->getNetworkPrefix() . $this->hash160(hex2bin($address)); + $address = $this->getNetworkPrefix() . $this->hash160(hex2bin($address)); //checksum - $address = $address.substr($this->hash256(hex2bin($address)), 0, 8); - $address = $this->base58_encode($address); + $address = $address.substr($this->hash256(hex2bin($address)), 0, 8); + $address = $this->base58_encode($address); if($this->validateAddress($address)) return $address; @@ -827,6 +807,26 @@ public function validateAddress($address) return false; } + /*** + * returns the private key under the Wallet Import Format + * + * @return string (base58) + * @throws \Exception + */ + public function getWif() + { + if(!isset($this->k)) + { + throw new \Exception('No Private Key was defined'); + } + + $k = $this->k; + $secretKey = '80' . $k; + $secretKey .= substr($this->hash256(hex2bin($secretKey)), 0, 8); + + return $this->base58_encode($secretKey); + } + /*** * Tests if the Wif key (Wallet Import Format) is valid or not. * @@ -835,8 +835,8 @@ public function validateAddress($address) */ public function validateWifKey($wif) { - $key = $this->base58_decode($wif, false); - $length = strlen($key); + $key = $this->base58_decode($wif, true); + $length = strlen($key); $checksum = $this->hash256(hex2bin(substr($key, 0, $length - 8))); if(substr($checksum, 0, 8) === substr($key, $length - 8, 8)) return true; @@ -844,6 +844,20 @@ public function validateWifKey($wif) return false; } + /** + * @param string $wif (base58) + * @return bool + */ + public function setPrivateKeyWithWif($wif) + { + if(!$this->validateWifKey($wif)) { + throw new \Exception('Invalid WIF'); + } + + $key = $this->base58_decode($wif, true); + $this->setPrivateKey(substr($key, 1, strlen($key) - 9)); + } + /*** * Sign a hash with the private key that was set and returns signatures as an array (R,S) * @@ -945,15 +959,16 @@ public function signHash($hash, $nonce = null) * Satoshi client's standard message signature implementation. * * @param string $message + * @param bool $onlySignature * @param bool $compressed * @param null $nonce * @return string * @throws \Exception */ - public function signMessage($message, $compressed = true, $nonce = null) + public function signMessage($message, $onlySignature = false ,$compressed = true, $nonce = null) { - $hash = $this->hash256("\x18Bitcoin Signed Message:\n" . $this->numToVarIntString(strlen($message)). $message); + $hash = $this->hash256("\x18Bitcoin Signed Message:\n" . $this->numToVarIntString(strlen($message)). $message); $points = $this->getSignatureHashPoints( $hash, $nonce @@ -985,12 +1000,8 @@ public function signMessage($message, $compressed = true, $nonce = null) $flag += $i; $pubKeyPts = $this->getPubKeyPoints(); - //echo "\nReal pubKey : \n"; - //print_r($pubKeyPts); $recoveredPubKey = $this->getPubKeyWithRS($flag, $R, $S, $hash); - //echo "\nRecovered PubKey : \n"; - //print_r($recoveredPubKey); if($this->getDerPubKeyWithPubKeyPoints($pubKeyPts, $compressed) === $recoveredPubKey) { @@ -1004,8 +1015,13 @@ public function signMessage($message, $compressed = true, $nonce = null) throw new \Exception('Unable to get a valid signature flag.'); } + $signature = base64_encode(hex2bin(dechex($finalFlag) . $R . $S)); + + if($onlySignature) { + return $signature; + } - $res .= base64_encode(hex2bin(dechex($finalFlag) . $R . $S)); + $res .= $signature; $res .= "\n-----END BITCOIN SIGNED MESSAGE-----"; return $res;