PublicKey   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Test Coverage

Coverage 87.27%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 48
c 1
b 0
f 0
dl 0
loc 160
ccs 48
cts 55
cp 0.8727
rs 10
wmc 21

10 Methods

Rating   Name   Duplication   Size   Complexity  
A tweakAdd() 0 11 2
A getBuffer() 0 3 1
A clonePubkey() 0 16 4
A getResource() 0 3 1
A isCompressed() 0 3 1
A equals() 0 4 1
A __construct() 0 14 4
A doEquals() 0 12 4
A tweakMul() 0 11 2
A verify() 0 7 1
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 333
    public function __construct(EcAdapter $ecAdapter, $secp256k1_pubkey_t, bool $compressed = false)
40
    {
41 333
        if (!is_resource($secp256k1_pubkey_t) ||
42 333
            !get_resource_type($secp256k1_pubkey_t) === SECP256K1_TYPE_PUBKEY) {
43
            throw new \InvalidArgumentException('Secp256k1\Key\PublicKey expects ' . SECP256K1_TYPE_PUBKEY . ' resource');
44
        }
45
46 333
        if (false === is_bool($compressed)) {
0 ignored issues
show
introduced by
The condition false === is_bool($compressed) is always false.
Loading history...
47
            throw new \InvalidArgumentException('PublicKey: Compressed must be a boolean');
48
        }
49
50 333
        $this->ecAdapter = $ecAdapter;
51 333
        $this->pubkey_t = $secp256k1_pubkey_t;
52 333
        $this->compressed = $compressed;
53 333
    }
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 181
        $ctx = $this->ecAdapter->getContext();
63 181
        $normalized = null;
64 181
        secp256k1_ecdsa_signature_normalize($ctx, $normalized, $signature->getResource());
0 ignored issues
show
Bug introduced by
The method getResource() does not exist on BitWasp\Bitcoin\Crypto\E...ture\SignatureInterface. It seems like you code against a sub-type of BitWasp\Bitcoin\Crypto\E...ture\SignatureInterface such as BitWasp\Bitcoin\Crypto\E...6k1\Signature\Signature or BitWasp\Bitcoin\Crypto\E...ture\SignatureInterface or BitWasp\Bitcoin\Crypto\E...nature\CompactSignature. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

64
        secp256k1_ecdsa_signature_normalize($ctx, $normalized, $signature->/** @scrutinizer ignore-call */ getResource());
Loading history...
65
        /** @var Signature $signature */
66 181
        return (bool) secp256k1_ecdsa_verify($ctx, $normalized, $msg32->getBinary(), $this->pubkey_t);
67
    }
68
69
    /**
70
     * @param PublicKey $other
71
     * @return bool
72
     */
73 58
    private function doEquals(PublicKey $other): bool
74
    {
75 58
        $context = $this->ecAdapter->getContext();
76 58
        $pubA = '';
77 58
        $pubB = '';
78 58
        $flags = $this->compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
79
80 58
        if (!(secp256k1_ec_pubkey_serialize($context, $pubA, $this->pubkey_t, $flags) && secp256k1_ec_pubkey_serialize($context, $pubB, $other->pubkey_t, $flags))) {
81
            throw new \RuntimeException('Unable to serialize public key during equals');
82
        }
83
84 58
        return hash_equals($pubA, $pubB);
85
    }
86
87
    /**
88
     * @param PublicKeyInterface $other
89
     * @return bool
90
     */
91 58
    public function equals(PublicKeyInterface $other): bool
92
    {
93
        /** @var PublicKey $other */
94 58
        return $this->doEquals($other);
95
    }
96
97
    /**
98
     * @return bool|false
99
     */
100 185
    public function isCompressed(): bool
101
    {
102 185
        return $this->compressed;
103
    }
104
105
    /**
106
     * @return resource
107
     */
108 170
    public function getResource()
109
    {
110 170
        return $this->pubkey_t;
111
    }
112
113
    /**
114
     * @return resource
115
     * @throws \Exception
116
     */
117 6
    private function clonePubkey()
118
    {
119 6
        $context = $this->ecAdapter->getContext();
120 6
        $serialized = '';
121 6
        $flags = $this->compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
122 6
        if (1 !== secp256k1_ec_pubkey_serialize($context, $serialized, $this->pubkey_t, $flags)) {
123
            throw new \Exception('Secp256k1: pubkey serialize');
124
        }
125
126
        /** @var resource $clone */
127 6
        $clone = null;
128 6
        if (1 !== secp256k1_ec_pubkey_parse($context, $clone, $serialized)) {
129
            throw new \Exception('Secp256k1 pubkey parse');
130
        }
131
132 6
        return $clone;
133
    }
134
135
    /**
136
     * @param \GMP $tweak
137
     * @return KeyInterface
138
     * @throws \Exception
139
     */
140 5
    public function tweakAdd(\GMP $tweak): KeyInterface
141
    {
142 5
        $context = $this->ecAdapter->getContext();
143 5
        $bin = Buffer::int(gmp_strval($tweak, 10), 32)->getBinary();
144
145 5
        $clone = $this->clonePubkey();
146 5
        if (1 !== secp256k1_ec_pubkey_tweak_add($context, $clone, $bin)) {
147
            throw new \RuntimeException('Secp256k1: tweak add failed.');
148
        }
149
150 5
        return new PublicKey($this->ecAdapter, $clone, $this->compressed);
151
    }
152
153
    /**
154
     * @param \GMP $tweak
155
     * @return KeyInterface
156
     * @throws \Exception
157
     */
158 1
    public function tweakMul(\GMP $tweak): KeyInterface
159
    {
160 1
        $context = $this->ecAdapter->getContext();
161 1
        $bin = Buffer::int(gmp_strval($tweak, 10), 32)->getBinary();
162
163 1
        $clone = $this->clonePubkey();
164 1
        if (1 !== secp256k1_ec_pubkey_tweak_mul($context, $clone, $bin)) {
165
            throw new \RuntimeException('Secp256k1: tweak mul failed.');
166
        }
167
168 1
        return new PublicKey($this->ecAdapter, $clone, $this->compressed);
169
    }
170
171
    /**
172
     * @return BufferInterface
173
     */
174 134
    public function getBuffer(): BufferInterface
175
    {
176 134
        return (new PublicKeySerializer($this->ecAdapter))->serialize($this);
177
    }
178
}
179