Completed
Pull Request — master (#758)
by thomas
35:59
created

PrivateKey::isCompressed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key;
6
7
use BitWasp\Bitcoin\Bitcoin;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Adapter\EcAdapter;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Serializer\Key\PrivateKeySerializer;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Signature\CompactSignature;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\XOnlyPubKeyInterface;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\CompactSignatureInterface;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Signature\Signature;
14
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\Key;
15
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\KeyInterface;
16
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
17
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
18
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SchnorrSignatureInterface;
19
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
20
use BitWasp\Bitcoin\Crypto\Random\RbgInterface;
21
use BitWasp\Bitcoin\Crypto\Random\Rfc6979;
22
use BitWasp\Bitcoin\Exceptions\InvalidPrivateKey;
23
use BitWasp\Bitcoin\Network\NetworkInterface;
24
use BitWasp\Bitcoin\Serializer\Key\PrivateKey\WifPrivateKeySerializer;
25
use BitWasp\Buffertools\Buffer;
26
use BitWasp\Buffertools\BufferInterface;
27
use Mdanter\Ecc\Crypto\Signature\Signer;
28
29
class PrivateKey extends Key implements PrivateKeyInterface
30
{
31
    /**
32
     * @var \GMP
33
     */
34
    private $secretMultiplier;
35
36
    /**
37
     * @var bool
38
     */
39
    private $compressed;
40
41
    /**
42
     * @var PublicKey
43
     */
44
    private $publicKey;
45
46
    /**
47
     * @var EcAdapter
48
     */
49
    private $ecAdapter;
50
51
    /**
52
     * @param EcAdapter $ecAdapter
53
     * @param \GMP $int
54
     * @param bool $compressed
55 50
     * @throws InvalidPrivateKey
56
     */
57 50
    public function __construct(EcAdapter $ecAdapter, \GMP $int, bool $compressed = false)
58 1
    {
59
        if (false === $ecAdapter->validatePrivateKey(Buffer::int(gmp_strval($int, 10), 32))) {
60
            throw new InvalidPrivateKey('Invalid private key - must be less than curve order.');
61 49
        }
62 49
63 49
        $this->ecAdapter = $ecAdapter;
64 49
        $this->secretMultiplier = $int;
65
        $this->compressed = $compressed;
66
    }
67
68
    /**
69 65
     * @return \GMP
70
     */
71 65
    public function getSecret(): \GMP
72
    {
73
        return $this->secretMultiplier;
74
    }
75
76
    /**
77
     * @param BufferInterface $msg32
78
     * @param RbgInterface|null $rbg
79 36
     * @return Signature
80
     */
81 36
    public function sign(BufferInterface $msg32, RbgInterface $rbg = null): SignatureInterface
82 36
    {
83 36
        $rbg = $rbg ?: new Rfc6979($this->ecAdapter, $this, $msg32);
84
        $randomK = gmp_init($rbg->bytes(32)->getHex(), 16);
85 36
        $hash = gmp_init($msg32->getHex(), 16);
86 36
87 36
        $math = $this->ecAdapter->getMath();
88 36
        $signer = new Signer($math);
89
        $signature = $signer->sign($this->ecAdapter->getGenerator()->getPrivateKeyFrom($this->secretMultiplier), $hash, $randomK);
0 ignored issues
show
Bug introduced by
It seems like $hash can also be of type resource; however, parameter $truncatedHash of Mdanter\Ecc\Crypto\Signature\Signer::sign() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

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

89
        $signature = $signer->sign($this->ecAdapter->getGenerator()->getPrivateKeyFrom($this->secretMultiplier), /** @scrutinizer ignore-type */ $hash, $randomK);
Loading history...
Bug introduced by
It seems like $randomK can also be of type resource; however, parameter $randomK of Mdanter\Ecc\Crypto\Signature\Signer::sign() does only seem to accept GMP, maybe add an additional type check? ( Ignorable by Annotation )

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

89
        $signature = $signer->sign($this->ecAdapter->getGenerator()->getPrivateKeyFrom($this->secretMultiplier), $hash, /** @scrutinizer ignore-type */ $randomK);
Loading history...
90
        $s = $signature->getS();
91 36
92 19
        // if s is less than half the curve order, invert s
93
        if (!$this->ecAdapter->validateSignatureElement($s, true)) {
94
            $s = $math->sub($this->ecAdapter->getOrder(), $s);
95 36
        }
96
97
        return new Signature($this->ecAdapter, $signature->getR(), $s);
98
    }
99
100
    /**
101
     * @param BufferInterface $msg32
102
     * @param RbgInterface|null $rbg
103
     * @return CompactSignatureInterface
104 5
     * @throws \Exception
105
     */
106 5
    public function signCompact(BufferInterface $msg32, RbgInterface $rbg = null): CompactSignatureInterface
107
    {
108
        $sign = $this->sign($msg32, $rbg);
109
110 5
        // calculate the recovery param
111 5
        // there should be a way to get this when signing too, but idk how ...
112 5
        return new CompactSignature(
113 5
            $this->ecAdapter,
114 5
            $sign->getR(),
115 5
            $sign->getS(),
116
            $this->ecAdapter->calcPubKeyRecoveryParam($sign->getR(), $sign->getS(), $msg32, $this->getPublicKey()),
117
            $this->isCompressed()
118
        );
119
    }
120
121
    public function signSchnorr(BufferInterface $msg32): SchnorrSignatureInterface
122
    {
123 17
        throw new \RuntimeException("not implemented");
124
    }
125 17
126 17
    /**
127 17
     * @param \GMP $tweak
128
     * @return KeyInterface
129
     */
130
    public function tweakAdd(\GMP $tweak): KeyInterface
131
    {
132
        $adapter = $this->ecAdapter;
133
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
134 1
        return $adapter->getPrivateKey($modMath->add($tweak, $this->getSecret()), $this->compressed);
135
    }
136 1
137 1
    /**
138 1
     * @param \GMP $tweak
139
     * @return KeyInterface
140
     */
141
    public function tweakMul(\GMP $tweak): KeyInterface
142
    {
143
        $adapter = $this->ecAdapter;
144 46
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
145
        return $adapter->getPrivateKey($modMath->mul($tweak, $this->getSecret()), $this->compressed);
146 46
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151
    public function isCompressed(): bool
152
    {
153
        return $this->compressed;
154 65
    }
155
156 65
    /**
157 65
     * Return the public key
158 65
     *
159
     * @return PublicKey
160
     */
161 65
    public function getPublicKey(): PublicKeyInterface
162
    {
163
        if (null === $this->publicKey) {
164
            $point = $this->ecAdapter->getGenerator()->mul($this->secretMultiplier);
165
            $this->publicKey = new PublicKey($this->ecAdapter, $point, $this->compressed);
166
        }
167
168 4
        return $this->publicKey;
169
    }
170 4
171 4
    public function getXOnlyPublicKey(): XOnlyPubKeyInterface
172 4
    {
173
        throw new \RuntimeException("not implemented");
174
    }
175 4
176
    /**
177
     * @param NetworkInterface $network
178
     * @return string
179
     */
180
    public function toWif(NetworkInterface $network = null): string
181 59
    {
182
        $network = $network ?: Bitcoin::getNetwork();
183 59
        $serializer = new WifPrivateKeySerializer(
184
            new PrivateKeySerializer($this->ecAdapter)
185
        );
186
187
        return $serializer->serialize($network, $this);
188
    }
189
190
    /**
191
     * @return BufferInterface
192
     */
193
    public function getBuffer(): BufferInterface
194
    {
195
        return (new PrivateKeySerializer($this->ecAdapter))->serialize($this);
196
    }
197
}
198