Completed
Pull Request — master (#371)
by Ruben de
21:14
created

EcAdapter   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 61.39%

Importance

Changes 0
Metric Value
dl 0
loc 221
ccs 62
cts 101
cp 0.6139
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 8

15 Methods

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