Completed
Pull Request — master (#591)
by thomas
15:59
created

CompactSignatureSerializer::parse()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0488

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 6
nop 1
dl 0
loc 28
ccs 14
cts 16
cp 0.875
crap 5.0488
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Serializer\Signature;
6
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Adapter\EcAdapter;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\Secp256k1\Signature\CompactSignature;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Signature\CompactSignatureSerializerInterface;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\CompactSignatureInterface;
11
use BitWasp\Buffertools\Buffer;
12
use BitWasp\Buffertools\BufferInterface;
13
use BitWasp\Buffertools\Parser;
14
15
class CompactSignatureSerializer implements CompactSignatureSerializerInterface
16
{
17
    /**
18
     * @var EcAdapter
19
     */
20
    private $ecAdapter;
21
22
    /**
23
     * @param EcAdapter $ecAdapter
24
     */
25 11
    public function __construct(EcAdapter $ecAdapter)
26
    {
27 11
        $this->ecAdapter = $ecAdapter;
28 11
    }
29
30
    /**
31
     * @param CompactSignature $signature
32
     * @return BufferInterface
33
     */
34 5
    private function doSerialize(CompactSignature $signature)
35
    {
36 5
        $sig_t = '';
37 5
        $recid = '';
38 5
        if (!secp256k1_ecdsa_recoverable_signature_serialize_compact($this->ecAdapter->getContext(), $signature->getResource(), $sig_t, $recid)) {
39
            throw new \RuntimeException('Secp256k1 serialize compact failure');
40
        }
41
42 5
        return new Buffer(chr($signature->getFlags()) . $sig_t, 65, $this->ecAdapter->getMath());
43
    }
44
45
    /**
46
     * @param CompactSignatureInterface $signature
47
     * @return BufferInterface
48
     */
49 5
    public function serialize(CompactSignatureInterface $signature): BufferInterface
50
    {
51
        /** @var CompactSignature $signature */
52 5
        return $this->doSerialize($signature);
53
    }
54
55
    /**
56
     * @param string|BufferInterface $data
57
     * @return CompactSignatureInterface
58
     */
59 7
    public function parse($data): CompactSignatureInterface
60
    {
61 7
        $math = $this->ecAdapter->getMath();
62 7
        $buffer = (new Parser($data, $math))->getBuffer();
63
64 7
        if ($buffer->getSize() !== 65) {
65 1
            throw new \RuntimeException('Compact Sig must be 65 bytes');
66
        }
67
68 6
        $byte = (int) $buffer->slice(0, 1)->getInt();
69 6
        $sig = $buffer->slice(1, 64);
70
71 6
        $recoveryFlags = $byte - 27;
72 6
        if ($recoveryFlags > 7) {
73
            throw new \RuntimeException('Invalid signature type');
74
        }
75
76 6
        $isCompressed = $math->cmp($math->bitwiseAnd(gmp_init($recoveryFlags), gmp_init(4)), gmp_init(0)) !== 0;
77 6
        $recoveryId = $recoveryFlags - ($isCompressed ? 4 : 0);
78
79 6
        $sig_t = '';
80
        /** @var resource $sig_t */
81 6
        if (!secp256k1_ecdsa_recoverable_signature_parse_compact($this->ecAdapter->getContext(), $sig_t, $sig->getBinary(), $recoveryId)) {
82
            throw new \RuntimeException('Unable to parse compact signature');
83
        }
84
85 6
        return new CompactSignature($this->ecAdapter, $sig_t, $recoveryId, $isCompressed);
86
    }
87
}
88