/** * @param CompactSignature $signature * @param Buffer $messageHash * @return \BitWasp\Bitcoin\Key\PublicKey * @throws \Exception */ public function recoverCompact(Buffer $messageHash, CompactSignature $signature) { $pubkey = ''; $ret = \secp256k1_ecdsa_recover_compact($messageHash->getBinary(), $signature->getBuffer()->slice(1)->getBinary(), (int) $signature->getRecoveryId(), (int) $signature->isCompressed(), $pubkey); if ($ret === 1) { $publicKey = PublicKeyFactory::fromHex(bin2hex($pubkey)); return $publicKey->setCompressed($signature->isCompressed()); } throw new \Exception('Unable to recover public key from compact signature'); }
/** * @param CompactSignature $signature * @param Buffer $messageHash * @return PublicKey * @throws \Exception */ public function recoverCompact(Buffer $messageHash, CompactSignature $signature) { $math = $this->getMath(); $G = $this->getGenerator(); $isYEven = $math->bitwiseAnd($signature->getRecoveryId(), 1) != 0; $isSecondKey = $math->bitwiseAnd($signature->getRecoveryId(), 2) != 0; $curve = $G->getCurve(); // Precalculate (p + 1) / 4 where p is the field order $p_over_four = $math->div($math->add($curve->getPrime(), 1), 4); // 1.1 Compute x if (!$isSecondKey) { $x = $signature->getR(); } else { $x = $math->add($signature->getR(), $G->getOrder()); } // 1.3 Convert x to point $alpha = $math->mod($math->add($math->add($math->pow($x, 3), $math->mul($curve->getA(), $x)), $curve->getB()), $curve->getPrime()); $beta = $math->powmod($alpha, $p_over_four, $curve->getPrime()); // If beta is even, but y isn't or vice versa, then convert it, // otherwise we're done and y == beta. if ($math->isEven($beta) == $isYEven) { $y = $math->sub($curve->getPrime(), $beta); } else { $y = $beta; } // 1.4 Check that nR is at infinity (implicitly done in constructor) $R = $G->getCurve()->getPoint($x, $y); $point_negate = function (PointInterface $p) use($math, $G) { return $G->getCurve()->getPoint($p->getX(), $math->mul($p->getY(), -1)); }; // 1.6.1 Compute a candidate public key Q = r^-1 (sR - eG) $rInv = $math->inverseMod($signature->getR(), $G->getOrder()); $eGNeg = $point_negate($G->mul($messageHash->getInt())); $Q = $R->mul($signature->getS())->add($eGNeg)->mul($rInv); // 1.6.2 Test Q as a public key $Qk = new PublicKey($this, $Q); if ($this->verify($messageHash, $Qk, new Signature($signature->getR(), $signature->getS()))) { return $Qk->setCompressed($signature->isCompressed()); } throw new \Exception('Unable to recover public key'); }