/** * Decodes $data from BASE-58 format * * @param string $data * * @return string */ public static function decode($data) { for ($return = '0', $i = 0; $i < strlen($data); $i++) { $current = strpos(self::BASE58_CHARS, $data[$i]); $return = Math::mul($return, '58'); $return = Math::add($return, $current); } $return = Util::encodeHex($return); for ($i = 0; $i < strlen($data) && substr($data, $i, 1) == '1'; $i++) { $return = '00' . $return; } if (strlen($return) % 2 != 0) { $return = '0' . $return; } return $return; }
/** * Point addition method P + Q = R where: * s = (yP - yQ)/(xP - xQ) mod p * xR = s2 - xP - xQ mod p * yR = -yP + s(xP - xR) mod p * * @param PointInterface * @param PointInterface * * @return PointInterface */ public static function pointAdd(PointInterface $P, PointInterface $Q) { if ($P->isInfinity()) { return $Q; } if ($Q->isInfinity()) { return $P; } if ($P->getX() == $Q->getX() && $P->getY() == $Q->getY()) { return self::pointDouble(new Point($P->getX(), $P->getY())); } $p = '0x' . Secp256k1::P; $a = '0x' . Secp256k1::A; $s = 0; $R = array('x' => 0, 'y' => 0, 's' => 0); // Critical math section try { $m = Math::sub($P->getY(), $Q->getY()); $n = Math::sub($P->getX(), $Q->getX()); $o = Math::invertm($n, $p); $st = Math::mul($m, $o); $s = Math::mod($st, $p); $R['x'] = Math::mod(Math::sub(Math::sub(Math::mul($s, $s), $P->getX()), $Q->getX()), $p); $R['y'] = Math::mod(Math::add(Math::sub(0, $P->getY()), Math::mul($s, Math::sub($P->getX(), $R['x']))), $p); $R['s'] = $s; } catch (Exception $e) { throw new \Exception('Error in Util::pointAdd(): ' . $e->getMessage()); } return new Point($R['x'], $R['y']); }
/** * Creates an ECDSA signature of $data. * * @param string * @return string * @throws \Exception */ public function sign($data) { if (!ctype_xdigit($this->hex)) { throw new \Exception('The private key must be in hex format.'); } if (empty($data)) { throw new \Exception('You did not provide any data to sign.'); } $e = Util::decodeHex(hash('sha256', $data)); do { if (substr(strtolower($this->hex), 0, 2) != '0x') { $d = '0x' . $this->hex; } else { $d = $this->hex; } $k = SecureRandom::generateRandom(32); $k_hex = '0x' . strtolower(bin2hex($k)); $n_hex = '0x' . Secp256k1::N; $Gx = '0x' . substr(Secp256k1::G, 2, 64); $Gy = '0x' . substr(Secp256k1::G, 66, 64); $P = new Point($Gx, $Gy); // Calculate a new curve point from Q=k*G (x1,y1) $R = Util::doubleAndAdd($k_hex, $P); $Rx_hex = Util::encodeHex($R->getX()); $Rx_hex = str_pad($Rx_hex, 64, '0', STR_PAD_LEFT); // r = x1 mod n $r = Math::mod('0x' . $Rx_hex, $n_hex); // s = k^-1 * (e+d*r) mod n $edr = Math::add($e, Math::mul($d, $r)); $invk = Math::invertm($k_hex, $n_hex); $kedr = Math::mul($invk, $edr); $s = Math::mod($kedr, $n_hex); // The signature is the pair (r,s) $signature = array('r' => Util::encodeHex($r), 's' => Util::encodeHex($s)); $signature['r'] = str_pad($signature['r'], 64, '0', STR_PAD_LEFT); $signature['s'] = str_pad($signature['s'], 64, '0', STR_PAD_LEFT); } while (Math::cmp($r, '0') <= 0 || Math::cmp($s, '0') <= 0); $sig = array('sig_rs' => $signature, 'sig_hex' => self::serializeSig($signature['r'], $signature['s'])); return $sig['sig_hex']['seq']; }