1 | <?php |
||
21 | class EcAdapter implements EcAdapterInterface |
||
22 | { |
||
23 | /** |
||
24 | * @var Math |
||
25 | */ |
||
26 | private $math; |
||
27 | |||
28 | /** |
||
29 | * @var GeneratorPoint |
||
30 | */ |
||
31 | private $generator; |
||
32 | |||
33 | /** |
||
34 | * @param Math $math |
||
35 | * @param GeneratorPoint $generator |
||
36 | */ |
||
37 | 57 | public function __construct(Math $math, GeneratorPoint $generator) |
|
42 | |||
43 | /** |
||
44 | * @return Math |
||
45 | */ |
||
46 | 984 | public function getMath() |
|
50 | |||
51 | /** |
||
52 | * @return GeneratorPoint |
||
53 | */ |
||
54 | 474 | public function getGenerator() |
|
58 | |||
59 | /** |
||
60 | * @param int|string $scalar |
||
61 | * @param bool|false $compressed |
||
62 | * @return PrivateKey |
||
63 | */ |
||
64 | 237 | public function getPrivateKey($scalar, $compressed = false) |
|
68 | |||
69 | /** |
||
70 | * @param PointInterface $point |
||
71 | * @param bool|false $compressed |
||
72 | * @return PublicKey |
||
73 | */ |
||
74 | 204 | public function getPublicKey(PointInterface $point, $compressed = false) |
|
78 | |||
79 | /** |
||
80 | * @param int|string $r |
||
81 | * @param int|string $s |
||
82 | * @return Signature |
||
83 | */ |
||
84 | public function getSignature($r, $s) |
||
88 | |||
89 | /** |
||
90 | * @param Buffer $messageHash |
||
91 | * @param PublicKey $publicKey |
||
92 | * @param Signature $signature |
||
93 | * @return bool |
||
94 | */ |
||
95 | 3 | private function doVerify(Buffer $messageHash, PublicKey $publicKey, Signature $signature) |
|
117 | |||
118 | /** |
||
119 | * @param Buffer $messageHash |
||
120 | * @param PublicKeyInterface $publicKey |
||
121 | 45 | * @param SignatureInterface $signature |
|
122 | * @return bool |
||
123 | 45 | */ |
|
124 | 45 | public function verify(Buffer $messageHash, PublicKeyInterface $publicKey, SignatureInterface $signature) |
|
125 | 45 | { |
|
126 | /** @var PublicKey $publicKey */ |
||
127 | 45 | /** @var Signature $signature */ |
|
128 | 6 | return $this->doVerify($messageHash, $publicKey, $signature); |
|
|
|||
129 | } |
||
130 | |||
131 | 39 | /** |
|
132 | * @param Buffer $messageHash |
||
133 | * @param PrivateKey $privateKey |
||
134 | * @param RbgInterface|null $rbg |
||
135 | 39 | * @return Signature |
|
136 | 39 | */ |
|
137 | 39 | private function doSign(Buffer $messageHash, PrivateKey $privateKey, RbgInterface $rbg = null) |
|
138 | 39 | { |
|
139 | 39 | $rbg = $rbg ?: new Rfc6979($this, $privateKey, $messageHash); |
|
140 | $randomK = $rbg->bytes(32); |
||
141 | 39 | ||
142 | $math = $this->getMath(); |
||
143 | $generator = $this->getGenerator(); |
||
144 | $n = $generator->getOrder(); |
||
145 | |||
146 | $k = $math->mod($randomK->getInt(), $n); |
||
147 | $r = $generator->mul($k)->getX(); |
||
148 | |||
149 | if ($math->cmp($r, 0) === 0) { |
||
150 | 45 | throw new \RuntimeException('Random number r = 0'); |
|
151 | } |
||
152 | |||
153 | $s = $math->mod( |
||
154 | 45 | $math->mul( |
|
155 | $math->inverseMod($k, $n), |
||
156 | $math->mod( |
||
157 | $math->add( |
||
158 | $messageHash->getInt(), |
||
159 | $math->mul( |
||
160 | $privateKey->getSecretMultiplier(), |
||
161 | $r |
||
162 | ) |
||
163 | 69 | ), |
|
164 | $n |
||
165 | 69 | ) |
|
166 | 69 | ), |
|
167 | $n |
||
168 | 69 | ); |
|
169 | 69 | ||
170 | 69 | if ($math->cmp($s, 0) === 0) { |
|
171 | throw new \RuntimeException('Signature s = 0'); |
||
172 | 69 | } |
|
173 | 69 | ||
174 | // if s is less than half the curve order, invert s |
||
175 | 69 | if (!$this->validateSignatureElement($s, true)) { |
|
176 | $s = $math->sub($n, $s); |
||
177 | } |
||
178 | |||
179 | 69 | return new Signature($this, $r, $s); |
|
180 | 69 | } |
|
181 | 69 | ||
182 | 69 | /** |
|
183 | 69 | * @param Buffer $messageHash |
|
184 | 69 | * @param PrivateKeyInterface $privateKey |
|
185 | 69 | * @param RbgInterface $rbg |
|
186 | 69 | * @return SignatureInterface |
|
187 | * @throws \BitWasp\Bitcoin\Exceptions\RandomBytesFailure |
||
188 | 69 | */ |
|
189 | 69 | public function sign(Buffer $messageHash, PrivateKeyInterface $privateKey, RbgInterface $rbg = null) |
|
190 | { |
||
191 | 69 | /** @var PrivateKey $privateKey */ |
|
192 | 69 | return $this->doSign($messageHash, $privateKey, $rbg); |
|
193 | } |
||
194 | 69 | ||
195 | /** |
||
196 | 69 | * @param Buffer $messageHash |
|
197 | * @param CompactSignatureInterface $signature |
||
198 | * @return PublicKey |
||
199 | * @throws \Exception |
||
200 | */ |
||
201 | 69 | public function recover(Buffer $messageHash, CompactSignatureInterface $signature) |
|
202 | 45 | { |
|
203 | 45 | $math = $this->getMath(); |
|
204 | $G = $this->getGenerator(); |
||
205 | 69 | ||
206 | $isYEven = $math->cmp($math->bitwiseAnd($signature->getRecoveryId(), 1), 0) !== 0; |
||
207 | $isSecondKey = $math->cmp($math->bitwiseAnd($signature->getRecoveryId(), 2), 0) !== 0; |
||
208 | $curve = $G->getCurve(); |
||
209 | |||
210 | // Precalculate (p + 1) / 4 where p is the field order |
||
211 | $p_over_four = $math->div($math->add($curve->getPrime(), 1), 4); |
||
212 | |||
213 | // 1.1 Compute x |
||
214 | if (!$isSecondKey) { |
||
215 | 69 | $x = $signature->getR(); |
|
216 | } else { |
||
217 | $x = $math->add($signature->getR(), $G->getOrder()); |
||
218 | 69 | } |
|
219 | |||
220 | // 1.3 Convert x to point |
||
221 | $alpha = $math->mod($math->add($math->add($math->pow($x, 3), $math->mul($curve->getA(), $x)), $curve->getB()), $curve->getPrime()); |
||
222 | $beta = $math->powmod($alpha, $p_over_four, $curve->getPrime()); |
||
223 | |||
224 | // If beta is even, but y isn't or vice versa, then convert it, |
||
225 | // otherwise we're done and y == beta. |
||
226 | if ($math->isEven($beta) === $isYEven) { |
||
227 | 27 | $y = $math->sub($curve->getPrime(), $beta); |
|
228 | } else { |
||
229 | 27 | $y = $beta; |
|
230 | 27 | } |
|
231 | |||
232 | 27 | // 1.4 Check that nR is at infinity (implicitly done in constructor) |
|
233 | 27 | $R = $G->getCurve()->getPoint($x, $y); |
|
234 | 27 | ||
235 | $point_negate = function (PointInterface $p) use ($math, $G) { |
||
236 | return $G->getCurve()->getPoint($p->getX(), $math->mul($p->getY(), -1)); |
||
237 | 27 | }; |
|
238 | |||
239 | // 1.6.1 Compute a candidate public key Q = r^-1 (sR - eG) |
||
240 | 27 | $rInv = $math->inverseMod($signature->getR(), $G->getOrder()); |
|
241 | 27 | $eGNeg = $point_negate($G->mul($messageHash->getInt())); |
|
242 | 27 | $Q = $R->mul($signature->getS())->add($eGNeg)->mul($rInv); |
|
243 | 6 | ||
244 | // 1.6.2 Test Q as a public key |
||
245 | $Qk = new PublicKey($this, $Q, $signature->isCompressed()); |
||
246 | if ($this->verify($messageHash, $Qk, $signature->convert())) { |
||
247 | 27 | return $Qk; |
|
248 | 27 | } |
|
249 | |||
250 | throw new \Exception('Unable to recover public key'); |
||
251 | } |
||
252 | 27 | ||
253 | 22 | /** |
|
254 | 22 | * Attempt to calculate the public key recovery param by trial and error |
|
255 | 17 | * |
|
256 | * @param int|string $r |
||
257 | * @param int|string $s |
||
258 | * @param Buffer $messageHash |
||
259 | 27 | * @param PublicKey $publicKey |
|
260 | * @return int |
||
261 | 27 | * @throws \Exception |
|
262 | 27 | */ |
|
263 | 27 | public function calcPubKeyRecoveryParam($r, $s, Buffer $messageHash, PublicKey $publicKey) |
|
264 | { |
||
265 | $Q = $publicKey->getPoint(); |
||
266 | 27 | for ($i = 0; $i < 4; $i++) { |
|
267 | 27 | try { |
|
268 | 27 | $recover = $this->recover($messageHash, new CompactSignature($this, $r, $s, $i, $publicKey->isCompressed())); |
|
269 | if ($recover->getPoint()->equals($Q)) { |
||
270 | return $i; |
||
271 | 27 | } |
|
272 | 27 | } catch (\Exception $e) { |
|
273 | 21 | continue; |
|
274 | } |
||
275 | } |
||
276 | 6 | ||
277 | throw new \Exception('Failed to find valid recovery factor'); |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * @param Buffer $messageHash |
||
282 | * @param PrivateKey $privateKey |
||
283 | * @param RbgInterface|null $rbg |
||
284 | * @return CompactSignature |
||
285 | * @throws \Exception |
||
286 | */ |
||
287 | private function doSignCompact(Buffer $messageHash, PrivateKey $privateKey, RbgInterface $rbg = null) |
||
301 | 6 | ||
302 | /** |
||
303 | 6 | * @param PrivateKeyInterface $privateKey |
|
304 | * @param Buffer $messageHash |
||
305 | * @param RbgInterface $rbg |
||
306 | * @return CompactSignature |
||
307 | */ |
||
308 | public function signCompact(Buffer $messageHash, PrivateKeyInterface $privateKey, RbgInterface $rbg = null) |
||
313 | 18 | ||
314 | /** |
||
315 | 18 | * @param Buffer $privateKey |
|
316 | * @return bool |
||
317 | */ |
||
318 | public function validatePrivateKey(Buffer $privateKey) |
||
324 | 18 | ||
325 | 18 | /** |
|
326 | * @param int|string $element |
||
327 | * @param bool $half |
||
328 | * @return bool |
||
329 | */ |
||
330 | public function validateSignatureElement($element, $half = false) |
||
340 | |||
341 | /** |
||
342 | * @param Buffer $publicKey |
||
343 | * @return PublicKeyInterface |
||
344 | 243 | * @throws \Exception |
|
345 | */ |
||
346 | 243 | public function publicKeyFromBuffer(Buffer $publicKey) |
|
364 | 75 | ||
365 | /** |
||
366 | * @param int|string $xCoord |
||
367 | * @param string $prefix |
||
368 | * @return int|string |
||
369 | * @throws \Exception |
||
370 | */ |
||
371 | public function recoverYfromX($xCoord, $prefix) |
||
400 | } |
||
401 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.