Completed
Pull Request — master (#591)
by thomas
21:10
created

EcAdapter::verify()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 6
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Adapter;
6
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Key\PrivateKey;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Key\PublicKey;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\CompactSignature;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\Signature;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
14
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\CompactSignatureInterface;
15
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
16
use BitWasp\Bitcoin\Crypto\Random\RbgInterface;
17
use BitWasp\Bitcoin\Math\Math;
18
use BitWasp\Buffertools\Buffer;
19
use BitWasp\Buffertools\BufferInterface;
20
use Mdanter\Ecc\Primitives\GeneratorPoint;
21
22
class EcAdapter implements EcAdapterInterface
23
{
24
    /**
25
     * @var Math
26
     */
27
    private $math;
28
29
    /**
30
     * @var GeneratorPoint
31
     */
32
    private $generator;
33
34
    /**
35
     * @var resource
36
     */
37
    private $context;
38
39
    /**
40
     * @param Math $math
41
     * @param GeneratorPoint $generator
42
     * @param resource $secp256k1_context_t
43
     */
44 2
    public function __construct(Math $math, GeneratorPoint $generator, $secp256k1_context_t)
45
    {
46 2
        if (!is_resource($secp256k1_context_t) || !get_resource_type($secp256k1_context_t) === SECP256K1_TYPE_CONTEXT) {
47
            throw new \InvalidArgumentException('Secp256k1: Must pass a secp256k1_context_t resource');
48
        }
49 2
        $this->math = $math;
50 2
        $this->generator = $generator;
51 2
        $this->context = $secp256k1_context_t;
52 2
    }
53
54
    /**
55
     * @return Math
56
     */
57 1526
    public function getMath(): Math
58
    {
59 1526
        return $this->math;
60
    }
61
62
    /**
63
     * @return GeneratorPoint
64
     */
65 74
    public function getGenerator()
66
    {
67 74
        return $this->generator;
68
    }
69
70
    /**
71
     * @param BufferInterface $privateKey
72
     * @return bool
73
     */
74 82
    public function validatePrivateKey(BufferInterface $privateKey): bool
75
    {
76 82
        return (bool) secp256k1_ec_seckey_verify($this->context, $privateKey->getBinary());
77
    }
78
79
    /**
80
     * @param \GMP $element
81
     * @param bool $half
82
     * @return bool
83
     */
84 4
    public function validateSignatureElement(\GMP $element, bool $half = false): bool
85
    {
86 4
        $math = $this->getMath();
87 4
        $against = $this->getGenerator()->getOrder();
88 4
        if ($half) {
89 4
            $against = $math->rightShift($against, 1);
90
        }
91
92 4
        return $math->cmp($element, $against) < 0 && $math->cmp($element, gmp_init(0)) !== 0;
93
    }
94
95
    /**
96
     * @param \GMP $int
97
     * @param bool $compressed
98
     * @return PrivateKeyInterface
99
     */
100 76
    public function getPrivateKey(\GMP $int, bool $compressed = false): PrivateKeyInterface
101
    {
102 76
        return new PrivateKey($this, $int, $compressed);
103
    }
104
105
    /**
106
     * @return resource
107
     */
108 336
    public function getContext()
109
    {
110 336
        return $this->context;
111
    }
112
113
    /**
114
     * @param BufferInterface $msg32
115
     * @param PrivateKey $privateKey
116
     * @return Signature
117
     */
118 66
    private function doSign(BufferInterface $msg32, PrivateKey $privateKey): Signature
119
    {
120
        /** @var resource $sig_t */
121 66
        $sig_t = '';
122 66
        if (1 !== secp256k1_ecdsa_sign($this->context, $sig_t, $msg32->getBinary(), $privateKey->getBinary())) {
123
            throw new \RuntimeException('Secp256k1: failed to sign');
124
        }
125
126 66
        $derSig = '';
127 66
        secp256k1_ecdsa_signature_serialize_der($this->context, $derSig, $sig_t);
128
129 66
        $rL = ord($derSig[3]);
130 66
        $r = (new Buffer(substr($derSig, 4, $rL), $rL, $this->math))->getGmp();
131
132 66
        $sL = ord($derSig[4+$rL + 1]);
133 66
        $s = (new Buffer(substr($derSig, 4 + $rL + 2, $sL), $sL, $this->math))->getGmp();
134
135 66
        return new Signature($this, $r, $s, $sig_t);
136
    }
137
138
    /**
139
     * @param BufferInterface $msg32
140
     * @param PrivateKeyInterface $privateKey
141
     * @param RbgInterface|null $rbg
142
     * @return SignatureInterface
143
     */
144 66
    public function sign(BufferInterface $msg32, PrivateKeyInterface $privateKey, RbgInterface $rbg = null): SignatureInterface
145
    {
146
        /** @var PrivateKey $privateKey */
147 66
        return $this->doSign($msg32, $privateKey);
148
    }
149
150
    /**
151
     * @param BufferInterface $msg32
152
     * @param PublicKey $publicKey
153
     * @param Signature $signature
154
     * @return bool
155
     */
156 181
    private function doVerify(BufferInterface $msg32, PublicKey $publicKey, Signature $signature): bool
157
    {
158 181
        return (bool) secp256k1_ecdsa_verify($this->context, $signature->getResource(), $msg32->getBinary(), $publicKey->getResource());
159
    }
160
161
    /**
162
     * @param BufferInterface $msg32
163
     * @param PublicKeyInterface $publicKey
164
     * @param SignatureInterface $signature
165
     * @return bool
166
     */
167 181
    public function verify(BufferInterface $msg32, PublicKeyInterface $publicKey, SignatureInterface $signature): bool
168
    {
169
        /** @var PublicKey $publicKey */
170
        /** @var Signature $signature */
171 181
        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...
172
    }
173
174
    /**
175
     * @param BufferInterface $msg32
176
     * @param CompactSignature $compactSig
177
     * @return PublicKey
178
     */
179 6
    private function doRecover(BufferInterface $msg32, CompactSignature $compactSig): PublicKey
180
    {
181 6
        $publicKey = '';
182
        /** @var resource $publicKey */
183 6
        $context = $this->context;
184 6
        $sig = $compactSig->getResource();
185 6
        if (1 !== secp256k1_ecdsa_recover($context, $publicKey, $sig, $msg32->getBinary())) {
186
            throw new \RuntimeException('Unable to recover Public Key');
187
        }
188
189 6
        return new PublicKey($this, $publicKey, $compactSig->isCompressed());
190
    }
191
192
    /**
193
     * @param BufferInterface $msg32
194
     * @param CompactSignatureInterface $compactSig
195
     * @return PublicKeyInterface
196
     */
197 6
    public function recover(BufferInterface $msg32, CompactSignatureInterface $compactSig): PublicKeyInterface
198
    {
199
        /** @var CompactSignature $compactSig */
200 6
        return $this->doRecover($msg32, $compactSig);
201
    }
202
203
    /**
204
     * @param BufferInterface $msg32
205
     * @param PrivateKey $privateKey
206
     * @return CompactSignature
207
     */
208 5
    private function doSignCompact(BufferInterface $msg32, PrivateKey $privateKey): CompactSignature
209
    {
210 5
        $sig_t = '';
211
        /** @var resource $sig_t */
212 5
        if (1 !== secp256k1_ecdsa_sign_recoverable($this->context, $sig_t, $msg32->getBinary(), $privateKey->getBinary())) {
213
            throw new \RuntimeException('Secp256k1: failed to sign');
214
        }
215
216 5
        $recid = '';
217 5
        $ser = '';
218 5
        if (!secp256k1_ecdsa_recoverable_signature_serialize_compact($this->context, $sig_t, $ser, $recid)) {
219
            throw new \RuntimeException('Failed to obtain recid');
220
        }
221
222 5
        unset($ser);
223 5
        return new CompactSignature(
224 5
            $this,
225 5
            $sig_t,
226 5
            $recid,
227 5
            $privateKey->isCompressed()
228
        );
229
    }
230
231
    /**
232
     * @param BufferInterface $msg32
233
     * @param PrivateKeyInterface $privateKey
234
     * @param RbgInterface|null $rbg
235
     * @return CompactSignatureInterface
236
     */
237 5
    public function signCompact(BufferInterface $msg32, PrivateKeyInterface $privateKey, RbgInterface $rbg = null): CompactSignatureInterface
238
    {
239
        /** @var PrivateKey $privateKey */
240 5
        return $this->doSignCompact($msg32, $privateKey);
241
    }
242
}
243