Completed
Pull Request — master (#591)
by thomas
22:54 queued 09:12
created

PublicKey::equals()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Key;
6
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Adapter\EcAdapter;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Serializer\Key\PublicKeySerializer;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\Signature;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\Key;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\KeyInterface;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
14
use BitWasp\Buffertools\Buffer;
15
use BitWasp\Buffertools\BufferInterface;
16
17
class PublicKey extends Key implements PublicKeyInterface
18
{
19
    /**
20
     * @var EcAdapter
21
     */
22
    private $ecAdapter;
23
24
    /**
25
     * @var bool|false
26
     */
27
    private $compressed;
28
29
    /**
30
     * @var resource
31
     */
32
    private $pubkey_t;
33
34
    /**
35
     * @param EcAdapter $ecAdapter
36
     * @param resource $secp256k1_pubkey_t
37
     * @param bool|false $compressed
38
     */
39 301
    public function __construct(EcAdapter $ecAdapter, $secp256k1_pubkey_t, bool $compressed = false)
40
    {
41 301
        if (!is_resource($secp256k1_pubkey_t) ||
42 301
            !get_resource_type($secp256k1_pubkey_t) === SECP256K1_TYPE_PUBKEY) {
43
            throw new \InvalidArgumentException('Secp256k1\Key\PublicKey expects ' . SECP256K1_TYPE_PUBKEY . ' resource');
44
        }
45
46 301
        if (false === is_bool($compressed)) {
47
            throw new \InvalidArgumentException('PublicKey: Compressed must be a boolean');
48
        }
49
50 301
        $this->ecAdapter = $ecAdapter;
51 301
        $this->pubkey_t = $secp256k1_pubkey_t;
52 301
        $this->compressed = $compressed;
53 301
    }
54
    
55
    /**
56
     * @param BufferInterface $msg32
57
     * @param SignatureInterface $signature
58
     * @return bool
59
     */
60 181
    public function verify(BufferInterface $msg32, SignatureInterface $signature): bool
61
    {
62
        /** @var Signature $signature */
63 181
        return (bool) secp256k1_ecdsa_verify($this->ecAdapter->getContext(), $signature->getResource(), $msg32->getBinary(), $this->pubkey_t);
64
    }
65
66
    /**
67
     * @param PublicKey $other
68
     * @return bool
69
     */
70 58
    private function doEquals(PublicKey $other): bool
71
    {
72 58
        $context = $this->ecAdapter->getContext();
73 58
        $pubA = '';
74 58
        $pubB = '';
75 58
        if (!(secp256k1_ec_pubkey_serialize($context, $pubA, $this->pubkey_t, (int) $this->compressed) && secp256k1_ec_pubkey_serialize($context, $pubB, $other->pubkey_t, (int) $this->compressed))) {
0 ignored issues
show
Documentation introduced by
(int) $this->compressed is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
76
            throw new \RuntimeException('Unable to serialize public key during equals');
77
        }
78
79 58
        return hash_equals($pubA, $pubB);
80
    }
81
82
    /**
83
     * @param PublicKeyInterface $other
84
     * @return bool
85
     */
86 58
    public function equals(PublicKeyInterface $other): bool
87
    {
88
        /** @var PublicKey $other */
89 58
        return $this->doEquals($other);
90
    }
91
92
    /**
93
     * @return bool|false
94
     */
95 153
    public function isCompressed(): bool
96
    {
97 153
        return $this->compressed;
98
    }
99
100
    /**
101
     * @return resource
102
     */
103 140
    public function getResource()
104
    {
105 140
        return $this->pubkey_t;
106
    }
107
108
    /**
109
     * @return resource
110
     * @throws \Exception
111
     */
112 5
    private function clonePubkey()
113
    {
114 5
        $context = $this->ecAdapter->getContext();
115 5
        $serialized = '';
116 5
        if (1 !== secp256k1_ec_pubkey_serialize($context, $serialized, $this->pubkey_t, (int) $this->compressed)) {
0 ignored issues
show
Documentation introduced by
(int) $this->compressed is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
117
            throw new \Exception('Secp256k1: pubkey serialize');
118
        }
119
120
        /** @var resource $clone */
121 5
        $clone = '';
122 5
        if (1 !== secp256k1_ec_pubkey_parse($context, $clone, $serialized)) {
123
            throw new \Exception('Secp256k1 pubkey parse');
124
        }
125
126 5
        return $clone;
127
    }
128
129
    /**
130
     * @param \GMP $tweak
131
     * @return KeyInterface
132
     * @throws \Exception
133
     */
134 4
    public function tweakAdd(\GMP $tweak): KeyInterface
135
    {
136 4
        $context = $this->ecAdapter->getContext();
137 4
        $bin = Buffer::int(gmp_strval($tweak, 10), 32)->getBinary();
138
139 4
        $clone = $this->clonePubkey();
140 4
        if (1 !== secp256k1_ec_pubkey_tweak_add($context, $clone, $bin)) {
141
            throw new \RuntimeException('Secp256k1: tweak add failed.');
142
        }
143
144 4
        return new PublicKey($this->ecAdapter, $clone, $this->compressed);
145
    }
146
147
    /**
148
     * @param \GMP $tweak
149
     * @return KeyInterface
150
     * @throws \Exception
151
     */
152 1
    public function tweakMul(\GMP $tweak): KeyInterface
153
    {
154 1
        $context = $this->ecAdapter->getContext();
155 1
        $bin = Buffer::int(gmp_strval($tweak, 10), 32)->getBinary();
156
157 1
        $clone = $this->clonePubkey();
158 1
        if (1 !== secp256k1_ec_pubkey_tweak_mul($context, $clone, $bin)) {
159
            throw new \RuntimeException('Secp256k1: tweak mul failed.');
160
        }
161
162 1
        return new PublicKey($this->ecAdapter, $clone, $this->compressed);
163
    }
164
165
    /**
166
     * @return BufferInterface
167
     */
168 118
    public function getBuffer(): BufferInterface
169
    {
170 118
        return (new PublicKeySerializer($this->ecAdapter))->serialize($this);
171
    }
172
}
173