Completed
Pull Request — master (#221)
by thomas
51:04 queued 47:55
created

EcAdapter::associateSigs()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30
Metric Value
dl 0
loc 19
ccs 0
cts 19
cp 0
rs 8.8571
cc 5
eloc 13
nc 5
nop 3
crap 30
1
<?php
2
3
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Adapter;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Key\PublicKey;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Key\PrivateKey;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\CompactSignature;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\Signature;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\CompactSignatureInterface;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
14
use BitWasp\Bitcoin\Crypto\Random\RbgInterface;
15
use BitWasp\Bitcoin\Math\Math;
16
use BitWasp\Buffertools\Buffer;
17
use Mdanter\Ecc\Primitives\GeneratorPoint;
18
19
class EcAdapter implements EcAdapterInterface
20
{
21
    /**
22
     * @var Math
23
     */
24
    private $math;
25
26
    /**
27
     * @var GeneratorPoint
28
     */
29
    private $generator;
30
31
    /**
32
     * @var resource
33
     */
34
    private $context;
35
36
    /**
37
     * @param Math $math
38
     * @param GeneratorPoint $generator
39
     * @param resource $secp256k1_context_t
40
     */
41 33
    public function __construct(Math $math, GeneratorPoint $generator, $secp256k1_context_t)
42
    {
43 33
        if (!is_resource($secp256k1_context_t) || !get_resource_type($secp256k1_context_t) === SECP256K1_TYPE_CONTEXT) {
44
            throw new \InvalidArgumentException('Secp256k1: Must pass a secp256k1_context_t resource');
45
        }
46 33
        $this->math = $math;
47 33
        $this->generator = $generator;
48 33
        $this->context = $secp256k1_context_t;
49 33
    }
50
51
    /**
52
     * @return Math
53
     */
54 783
    public function getMath()
55
    {
56 783
        return $this->math;
57
    }
58
59
    /**
60
     * @return GeneratorPoint
61
     */
62 48
    public function getGenerator()
63
    {
64 48
        return $this->generator;
65
    }
66
67
    /**
68
     * @param Buffer $privateKey
69
     * @return bool
70
     */
71 231
    public function validatePrivateKey(Buffer $privateKey)
72
    {
73 231
        return (bool) secp256k1_ec_seckey_verify($this->context, $privateKey->getBinary());
74
    }
75
76
    /**
77
     * @param array $signatures
78
     * @param Buffer $messageHash
79
     * @param \BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface[] $publicKeys
80
     * @return array
81
     */
82
    public function associateSigs(array $signatures, Buffer $messageHash, array $publicKeys)
83
    {
84
        $sigCount = count($signatures);
85
        $linked = [];
86
        foreach ($signatures as $c => $signature) {
87
            foreach ($publicKeys as $key) {
88
                $verify = $this->verify($messageHash, $key, $signature);
89
                if ($verify) {
90
                    $linked[$key->getPubKeyHash()->getHex()][] = $signature;
91
                    if (count($linked) === $sigCount) {
92
                        break 2;
93
                    } else {
94
                        break;
95
                    }
96
                }
97
            }
98
        }
99
        return $linked;
100
    }
101
102
    /**
103
     * @param int|string $element
104
     * @param bool $half
105
     * @return bool
106
     */
107 9
    public function validateSignatureElement($element, $half = false)
108
    {
109 9
        $math = $this->getMath();
110 9
        $against = $this->getGenerator()->getOrder();
111 9
        if ($half) {
112 9
            $against = $math->rightShift($against, 1);
113 9
        }
114
115 9
        return $math->cmp($element, $against) < 0 && $math->cmp($element, 0) !== 0;
116
    }
117
118
    /**
119
     * @param int|string $int
120
     * @param bool|false $compressed
121
     * @return PrivateKey
122
     */
123 225
    public function getPrivateKey($int, $compressed = false)
124
    {
125 225
        return new PrivateKey($this, $int, $compressed);
126
    }
127
128
    /**
129
     * @return resource
130
     */
131 429
    public function getContext()
132
    {
133 429
        return $this->context;
134
    }
135
136
    /**
137
     * @param Buffer $msg32
138
     * @param PrivateKey $privateKey
139
     * @return Signature
140
     */
141 27
    private function doSign(Buffer $msg32, PrivateKey $privateKey)
142
    {
143
        /** @var resource $sig_t */
144 27
        $sig_t = '';
145 27
        if (1 !== secp256k1_ecdsa_sign($this->context, $msg32->getBinary(), $privateKey->getBinary(), $sig_t)) {
146
            throw new \RuntimeException('Secp256k1: failed to sign');
147
        }
148
149 27
        $derSig = '';
150 27
        secp256k1_ecdsa_signature_serialize_der($this->context, $sig_t, $derSig);
151
152 27
        $rL = ord($derSig[3]);
153 27
        $r = (new Buffer(substr($derSig, 4, $rL), $rL, $this->math))->getInt();
154
155 27
        $sL = ord($derSig[4+$rL + 1]);
156 27
        $s = (new Buffer(substr($derSig, 4 + $rL + 2, $sL), $rL, $this->math))->getInt();
157
158 27
        return new Signature($this, $r, $s, $sig_t);
159
    }
160
161
    /**
162
     * @param Buffer $msg32
163
     * @param PrivateKeyInterface $privateKey
164
     * @param RbgInterface|null $rbg
165
     * @return Signature
166
     */
167 27
    public function sign(Buffer $msg32, PrivateKeyInterface $privateKey, RbgInterface $rbg = null)
168
    {
169
        /** @var PrivateKey $privateKey */
170 27
        return $this->doSign($msg32, $privateKey);
171
    }
172
173
    /**
174
     * @param Buffer $msg32
175
     * @param PublicKey $publicKey
176
     * @param Signature $signature
177
     * @return bool
178
     */
179 9
    private function doVerify(Buffer $msg32, PublicKey $publicKey, Signature $signature)
180
    {
181 9
        return (bool) secp256k1_ecdsa_verify($this->context, $msg32->getBinary(), $signature->getResource(), $publicKey->getResource());
182
    }
183
184
    /**
185
     * @param Buffer $msg32
186
     * @param PublicKeyInterface $publicKey
187
     * @param SignatureInterface $signature
188
     * @return bool
189
     */
190 9
    public function verify(Buffer $msg32, PublicKeyInterface $publicKey, SignatureInterface $signature)
191
    {
192
        /** @var PublicKey $publicKey */
193
        /** @var Signature $signature */
194 9
        return $this->doVerify($msg32, $publicKey, $signature);
0 ignored issues
show
Compatibility introduced by
$publicKey of type object<BitWasp\Bitcoin\C...Key\PublicKeyInterface> is not a sub-type of object<BitWasp\Bitcoin\C...ecp256k1\Key\PublicKey>. It seems like you assume a concrete implementation of the interface BitWasp\Bitcoin\Crypto\E...\Key\PublicKeyInterface to be always present.

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.

Loading history...
195
    }
196
197
    /**
198
     * @param Buffer $msg32
199
     * @param CompactSignature $compactSig
200
     * @return PublicKey
201
     */
202 21
    private function doRecover(Buffer $msg32, CompactSignature $compactSig)
203
    {
204 21
        $publicKey = '';
205
        /** @var resource $publicKey */
206 21
        $context = $this->context;
207 21
        $sig = $compactSig->getResource();
208 21
        if (1 !== secp256k1_ecdsa_recover($context, $msg32->getBinary(), $sig, $publicKey)) {
209
            throw new \RuntimeException('Unable to recover Public Key');
210
        }
211
212 21
        return new PublicKey($this, $publicKey, $compactSig->isCompressed());
213
    }
214
215
    /**
216
     * @param Buffer $msg32
217
     * @param CompactSignatureInterface $compactSig
218
     * @return PublicKey
219
     */
220 21
    public function recover(Buffer $msg32, CompactSignatureInterface $compactSig)
221
    {
222
        /** @var CompactSignature $compactSig */
223 21
        return $this->doRecover($msg32, $compactSig);
224
    }
225
226
    /**
227
     * @param Buffer $msg32
228
     * @param PrivateKey $privateKey
229
     * @return CompactSignature
230
     */
231 18
    private function doSignCompact(Buffer $msg32, PrivateKey $privateKey)
232
    {
233 18
        $sig_t = '';
234
        /** @var resource $sig_t */
235 18
        if (1 !== secp256k1_ecdsa_sign_recoverable($this->context, $msg32->getBinary(), $privateKey->getBinary(), $sig_t)) {
236
            throw new \RuntimeException('Secp256k1: failed to sign');
237
        }
238
239 18
        $recid = '';
240 18
        $ser = '';
241 18
        if (!secp256k1_ecdsa_recoverable_signature_serialize_compact($this->context, $sig_t, $ser, $recid)) {
242
            throw new \RuntimeException('Failed to obtain recid');
243
        }
244
245 18
        unset($ser);
246 18
        return new CompactSignature(
247 18
            $this,
248 18
            $sig_t,
249 18
            $recid,
250 18
            $privateKey->isCompressed()
251 18
        );
252
    }
253
254
    /**
255
     * @param Buffer $msg32
256
     * @param PrivateKeyInterface $privateKey
257
     * @param RbgInterface|null $rbg
258
     * @return CompactSignatureInterface
259
     */
260 18
    public function signCompact(Buffer $msg32, PrivateKeyInterface $privateKey, RbgInterface $rbg = null)
261
    {
262
        /** @var PrivateKey $privateKey */
263 18
        return $this->doSignCompact($msg32, $privateKey);
264
    }
265
}
266