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

PrivateKey::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 3
dl 0
loc 10
ccs 7
cts 7
cp 1
crap 2
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\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\Signature\CompactSignatureInterface;
12
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Signature\Signature;
13
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\Key;
14
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\KeyInterface;
15
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
16
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
17
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
18
use BitWasp\Bitcoin\Crypto\Random\RbgInterface;
19
use BitWasp\Bitcoin\Crypto\Random\Rfc6979;
20
use BitWasp\Bitcoin\Exceptions\InvalidPrivateKey;
21
use BitWasp\Bitcoin\Network\NetworkInterface;
22
use BitWasp\Bitcoin\Serializer\Key\PrivateKey\WifPrivateKeySerializer;
23
use BitWasp\Buffertools\Buffer;
24
use BitWasp\Buffertools\BufferInterface;
25
use Mdanter\Ecc\Crypto\Signature\Signer;
26
27
class PrivateKey extends Key implements PrivateKeyInterface
28
{
29
    /**
30
     * @var \GMP
31
     */
32
    private $secretMultiplier;
33
34
    /**
35
     * @var bool
36
     */
37
    private $compressed;
38
39
    /**
40
     * @var PublicKey
41
     */
42
    private $publicKey;
43
44
    /**
45
     * @var EcAdapter
46
     */
47
    private $ecAdapter;
48
49
    /**
50
     * @param EcAdapter $ecAdapter
51
     * @param \GMP $int
52
     * @param bool $compressed
53
     * @throws InvalidPrivateKey
54
     */
55 40
    public function __construct(EcAdapter $ecAdapter, \GMP $int, bool $compressed = false)
56
    {
57 40
        if (false === $ecAdapter->validatePrivateKey(Buffer::int(gmp_strval($int, 10), 32))) {
58 1
            throw new InvalidPrivateKey('Invalid private key - must be less than curve order.');
59
        }
60
61 39
        $this->ecAdapter = $ecAdapter;
62 39
        $this->secretMultiplier = $int;
63 39
        $this->compressed = $compressed;
64 39
    }
65
66
    /**
67
     * @return \GMP
68
     */
69 62
    public function getSecret(): \GMP
70
    {
71 62
        return $this->secretMultiplier;
72
    }
73
74
    /**
75
     * @param BufferInterface $msg32
76
     * @param RbgInterface|null $rbg
77
     * @return Signature
78
     */
79 33
    public function sign(BufferInterface $msg32, RbgInterface $rbg = null): SignatureInterface
80
    {
81 33
        $rbg = $rbg ?: new Rfc6979($this->ecAdapter, $this, $msg32);
82 33
        $randomK = gmp_init($rbg->bytes(32)->getHex(), 16);
83 33
        $hash = gmp_init($msg32->getHex(), 16);
84
85 33
        $math = $this->ecAdapter->getMath();
86 33
        $signer = new Signer($math);
87 33
        $signature = $signer->sign($this->ecAdapter->getGenerator()->getPrivateKeyFrom($this->secretMultiplier), $hash, $randomK);
88 33
        $s = $signature->getS();
89
90
        // if s is less than half the curve order, invert s
91 33
        if (!$this->ecAdapter->validateSignatureElement($s, true)) {
92 20
            $s = $math->sub($this->ecAdapter->getOrder(), $s);
93
        }
94
95 33
        return new Signature($this->ecAdapter, $signature->getR(), $s);
96
    }
97
98
    /**
99
     * @param BufferInterface $msg32
100
     * @param RbgInterface|null $rbg
101
     * @return CompactSignatureInterface
102
     * @throws \Exception
103
     */
104 5
    public function signCompact(BufferInterface $msg32, RbgInterface $rbg = null): CompactSignatureInterface
105
    {
106 5
        $sign = $this->sign($msg32, $rbg);
107
108
        // calculate the recovery param
109
        // there should be a way to get this when signing too, but idk how ...
110 5
        return new CompactSignature(
111 5
            $this->ecAdapter,
112 5
            $sign->getR(),
113 5
            $sign->getS(),
114 5
            $this->ecAdapter->calcPubKeyRecoveryParam($sign->getR(), $sign->getS(), $msg32, $this->getPublicKey()),
115 5
            $this->isCompressed()
116
        );
117
    }
118
119
    /**
120
     * @param \GMP $tweak
121
     * @return KeyInterface
122
     */
123 7
    public function tweakAdd(\GMP $tweak): KeyInterface
124
    {
125 7
        $adapter = $this->ecAdapter;
126 7
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
127 7
        return $adapter->getPrivateKey($modMath->add($tweak, $this->getSecret()), $this->compressed);
128
    }
129
130
    /**
131
     * @param \GMP $tweak
132
     * @return KeyInterface
133
     */
134 1
    public function tweakMul(\GMP $tweak): KeyInterface
135
    {
136 1
        $adapter = $this->ecAdapter;
137 1
        $modMath = $adapter->getMath()->getModularArithmetic($adapter->getGenerator()->getOrder());
138 1
        return $adapter->getPrivateKey($modMath->mul($tweak, $this->getSecret()), $this->compressed);
139
    }
140
141
    /**
142
     * {@inheritDoc}
143
     */
144 36
    public function isCompressed(): bool
145
    {
146 36
        return $this->compressed;
147
    }
148
149
    /**
150
     * Return the public key
151
     *
152
     * @return PublicKey
153
     */
154 55
    public function getPublicKey(): PublicKeyInterface
155
    {
156 55
        if (null === $this->publicKey) {
157 55
            $point = $this->ecAdapter->getGenerator()->mul($this->secretMultiplier);
158 55
            $this->publicKey = new PublicKey($this->ecAdapter, $point, $this->compressed);
159
        }
160
161 55
        return $this->publicKey;
162
    }
163
164
    /**
165
     * @param NetworkInterface $network
166
     * @return string
167
     */
168 3
    public function toWif(NetworkInterface $network = null): string
169
    {
170 3
        $network = $network ?: Bitcoin::getNetwork();
171 3
        $serializer = new WifPrivateKeySerializer(
172 3
            $this->ecAdapter,
173 3
            new PrivateKeySerializer($this->ecAdapter)
174
        );
175
176 3
        return $serializer->serialize($network, $this);
177
    }
178
179
    /**
180
     * @return BufferInterface
181
     */
182 50
    public function getBuffer(): BufferInterface
183
    {
184 50
        return (new PrivateKeySerializer($this->ecAdapter))->serialize($this);
185
    }
186
}
187