Completed
Pull Request — master (#758)
by thomas
22:04
created

PrivateKey::tweakMul()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
     * @throws InvalidPrivateKey
56
     */
57 53
    public function __construct(EcAdapter $ecAdapter, \GMP $int, bool $compressed = false)
58
    {
59 53
        if (false === $ecAdapter->validatePrivateKey(Buffer::int(gmp_strval($int, 10), 32))) {
60 1
            throw new InvalidPrivateKey('Invalid private key - must be less than curve order.');
61
        }
62
63 52
        $this->ecAdapter = $ecAdapter;
64 52
        $this->secretMultiplier = $int;
65 52
        $this->compressed = $compressed;
66 52
    }
67
68
    /**
69
     * @return \GMP
70
     */
71 68
    public function getSecret(): \GMP
72
    {
73 68
        return $this->secretMultiplier;
74
    }
75
76
    /**
77
     * @param BufferInterface $msg32
78
     * @param RbgInterface|null $rbg
79
     * @return Signature
80
     */
81 36
    public function sign(BufferInterface $msg32, RbgInterface $rbg = null): SignatureInterface
82
    {
83 36
        $rbg = $rbg ?: new Rfc6979($this->ecAdapter, $this, $msg32);
84 36
        $randomK = gmp_init($rbg->bytes(32)->getHex(), 16);
85 36
        $hash = gmp_init($msg32->getHex(), 16);
86
87 36
        $math = $this->ecAdapter->getMath();
88 36
        $signer = new Signer($math);
89 36
        $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 36
        $s = $signature->getS();
91
92
        // if s is less than half the curve order, invert s
93 36
        if (!$this->ecAdapter->validateSignatureElement($s, true)) {
94 18
            $s = $math->sub($this->ecAdapter->getOrder(), $s);
95
        }
96
97 36
        return new Signature($this->ecAdapter, $signature->getR(), $s);
98
    }
99
100
    /**
101
     * @param BufferInterface $msg32
102
     * @param RbgInterface|null $rbg
103
     * @return CompactSignatureInterface
104
     * @throws \Exception
105
     */
106 5
    public function signCompact(BufferInterface $msg32, RbgInterface $rbg = null): CompactSignatureInterface
107
    {
108 5
        $sign = $this->sign($msg32, $rbg);
109
110
        // calculate the recovery param
111
        // 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 5
            $this->ecAdapter->calcPubKeyRecoveryParam($sign->getR(), $sign->getS(), $msg32, $this->getPublicKey()),
117 5
            $this->isCompressed()
118
        );
119
    }
120
121
    public function signSchnorr(BufferInterface $msg32): SchnorrSignatureInterface
122
    {
123
        throw new \RuntimeException("not implemented");
124
    }
125
126
    /**
127
     * @param \GMP $tweak
128
     * @return KeyInterface
129
     */
130 17
    public function tweakAdd(\GMP $tweak): KeyInterface
131
    {
132 17
        $adapter = $this->ecAdapter;
133 17
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
134 17
        return $adapter->getPrivateKey($modMath->add($tweak, $this->getSecret()), $this->compressed);
135
    }
136
137
    /**
138
     * @param \GMP $tweak
139
     * @return KeyInterface
140
     */
141 1
    public function tweakMul(\GMP $tweak): KeyInterface
142
    {
143 1
        $adapter = $this->ecAdapter;
144 1
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
145 1
        return $adapter->getPrivateKey($modMath->mul($tweak, $this->getSecret()), $this->compressed);
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 46
    public function isCompressed(): bool
152
    {
153 46
        return $this->compressed;
154
    }
155
156
    /**
157
     * Return the public key
158
     *
159
     * @return PublicKey
160
     */
161 68
    public function getPublicKey(): PublicKeyInterface
162
    {
163 68
        if (null === $this->publicKey) {
164 68
            $point = $this->ecAdapter->getGenerator()->mul($this->secretMultiplier);
165 68
            $this->publicKey = new PublicKey($this->ecAdapter, $point, $this->compressed);
166
        }
167
168 68
        return $this->publicKey;
169
    }
170
171
    public function getXOnlyPublicKey(): XOnlyPubKeyInterface
172
    {
173
        throw new \RuntimeException("not implemented");
174
    }
175
176
    /**
177
     * @param NetworkInterface $network
178
     * @return string
179
     */
180 4
    public function toWif(NetworkInterface $network = null): string
181
    {
182 4
        $network = $network ?: Bitcoin::getNetwork();
183 4
        $serializer = new WifPrivateKeySerializer(
184 4
            new PrivateKeySerializer($this->ecAdapter)
185
        );
186
187 4
        return $serializer->serialize($network, $this);
188
    }
189
190
    /**
191
     * @return BufferInterface
192
     */
193 59
    public function getBuffer(): BufferInterface
194
    {
195 59
        return (new PrivateKeySerializer($this->ecAdapter))->serialize($this);
196
    }
197
}
198