Completed
Pull Request — master (#591)
by thomas
16:14
created

PrivateKey::tweakAdd()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2.0116

Importance

Changes 0
Metric Value
cc 2
eloc 14
nc 2
nop 1
dl 0
loc 20
ccs 12
cts 14
cp 0.8571
crap 2.0116
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\Bitcoin;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Adapter\EcAdapter;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Serializer\Key\PrivateKeySerializer;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\Signature;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\Key;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\KeyInterface;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
14
use BitWasp\Bitcoin\Crypto\Random\RbgInterface;
15
use BitWasp\Bitcoin\Exceptions\InvalidPrivateKey;
16
use BitWasp\Bitcoin\Network\NetworkInterface;
17
use BitWasp\Bitcoin\Serializer\Key\PrivateKey\WifPrivateKeySerializer;
18
use BitWasp\Buffertools\Buffer;
19
use BitWasp\Buffertools\BufferInterface;
20
21
class PrivateKey extends Key implements PrivateKeyInterface
22
{
23
    /**
24
     * @var \GMP
25
     */
26
    private $secret;
27
28
    /**
29
     * @var string
30
     */
31
    private $secretBin;
32
33
    /**
34
     * @var bool
35
     */
36
    private $compressed;
37
38
    /**
39
     * @var PublicKey
40
     */
41
    private $publicKey;
42
43
    /**
44
     * @var EcAdapter
45
     */
46
    private $ecAdapter;
47
48
    /**
49
     * @param EcAdapter $adapter
50
     * @param \GMP $secret
51
     * @param bool|false $compressed
52
     * @throws \Exception
53
     */
54 76
    public function __construct(EcAdapter $adapter, \GMP $secret, bool $compressed = false)
55
    {
56 76
        $buffer = Buffer::int(gmp_strval($secret, 10), 32, $adapter->getMath());
57 76
        if (!$adapter->validatePrivateKey($buffer)) {
58 1
            throw new InvalidPrivateKey('Invalid private key');
59
        }
60
61 75
        if (false === is_bool($compressed)) {
62
            throw new \InvalidArgumentException('PrivateKey: Compressed argument must be a boolean');
63
        }
64
65 75
        $this->ecAdapter = $adapter;
66 75
        $this->secret = $secret;
67 75
        $this->secretBin = $buffer->getBinary();
68 75
        $this->compressed = $compressed;
69 75
    }
70
71
    /**
72
     * @param BufferInterface $msg32
73
     * @param RbgInterface|null $rbgInterface
74
     * @return Signature
75
     */
76
    public function sign(BufferInterface $msg32, RbgInterface $rbgInterface = null): Signature
77
    {
78
        return $this->ecAdapter->sign($msg32, $this, $rbgInterface);
79
    }
80
81
    /**
82
     * @return bool
83
     */
84 61
    public function isCompressed(): bool
85
    {
86 61
        return $this->compressed;
87
    }
88
89
    /**
90
     * @return \GMP
91
     */
92 52
    public function getSecret()
93
    {
94 52
        return $this->secret;
95
    }
96
97
    /**
98
     * @return string
99
     */
100 120
    public function getSecretBinary(): string
101
    {
102 120
        return $this->secretBin;
103
    }
104
105
    /**
106
     * @return PublicKey
107
     */
108 114
    public function getPublicKey()
109
    {
110 114
        if (null === $this->publicKey) {
111 84
            $context = $this->ecAdapter->getContext();
112 84
            $publicKey_t = '';
113
            /** @var resource $publicKey_t */
114 84
            if (1 !== secp256k1_ec_pubkey_create($context, $publicKey_t, $this->getBinary())) {
115
                throw new \RuntimeException('Failed to create public key');
116
            }
117
118 84
            $this->publicKey = new PublicKey($this->ecAdapter, $publicKey_t, $this->compressed);
119
        }
120
121 114
        return $this->publicKey;
122
    }
123
124
    /**
125
     * @param \GMP $tweak
126
     * @return KeyInterface
127
     */
128 8
    public function tweakAdd(\GMP $tweak): KeyInterface
129
    {
130 8
        $adapter = $this->ecAdapter;
131 8
        $math = $adapter->getMath();
132 8
        $context = $adapter->getContext();
133 8
        $privateKey = $this->getBinary(); // mod by reference
134 8
        $tweak = Buffer::int($math->toString($tweak), 32, $math)->getBinary();
135 8
        $ret = \secp256k1_ec_privkey_tweak_add(
136 8
            $context,
137
            $privateKey,
138 8
            $tweak
139
        );
140
141 8
        if ($ret !== 1) {
142
            throw new \RuntimeException('Secp256k1 privkey tweak add: failed');
143
        }
144
145 8
        $secret = new Buffer($privateKey);
146 8
        return $adapter->getPrivateKey($secret->getGmp(), $this->compressed);
147
    }
148
149
    /**
150
     * @param \GMP $tweak
151
     * @return KeyInterface
152
     */
153 1
    public function tweakMul(\GMP $tweak): KeyInterface
154
    {
155 1
        $privateKey = $this->getBinary();
156 1
        $math = $this->ecAdapter->getMath();
157 1
        $tweak = Buffer::int($math->toString($tweak), 32, $math)->getBinary();
158 1
        $ret = \secp256k1_ec_privkey_tweak_mul(
159 1
            $this->ecAdapter->getContext(),
160
            $privateKey,
161 1
            $tweak
162
        );
163
164 1
        if ($ret !== 1) {
165
            throw new \RuntimeException('Secp256k1 privkey tweak mul: failed');
166
        }
167
168 1
        $secret = new Buffer($privateKey);
169
170 1
        return $this->ecAdapter->getPrivateKey($secret->getGmp(), $this->compressed);
171
    }
172
173
    /**
174
     * @param NetworkInterface $network
175
     * @return string
176
     */
177 3
    public function toWif(NetworkInterface $network = null): string
178
    {
179 3
        $network = $network ?: Bitcoin::getNetwork();
180 3
        $wifSerializer = new WifPrivateKeySerializer($this->ecAdapter, new PrivateKeySerializer($this->ecAdapter));
181 3
        return $wifSerializer->serialize($network, $this);
182
    }
183
184
    /**
185
     * @return BufferInterface
186
     */
187 118
    public function getBuffer(): BufferInterface
188
    {
189 118
        return (new PrivateKeySerializer($this->ecAdapter))->serialize($this);
190
    }
191
}
192