Completed
Pull Request — master (#593)
by thomas
15:02
created

DerSignatureSerializer::serialize()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3.0017

Importance

Changes 0
Metric Value
cc 3
eloc 17
nc 4
nop 1
dl 0
loc 30
ccs 16
cts 17
cp 0.9412
crap 3.0017
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Serializer\Signature;
6
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Adapter\EcAdapter;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Signature\Signature;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Signature\DerSignatureSerializerInterface;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
12
use BitWasp\Buffertools\Buffer;
13
use BitWasp\Buffertools\BufferInterface;
14
use BitWasp\Buffertools\Exceptions\ParserOutOfRange;
15
use BitWasp\Buffertools\Parser;
16
use BitWasp\Buffertools\Template;
17
use BitWasp\Buffertools\TemplateFactory;
18
19
class DerSignatureSerializer implements DerSignatureSerializerInterface
20
{
21
    /**
22
     * @var EcAdapter
23
     */
24
    private $ecAdapter;
25
26
    /**
27
     * @param EcAdapter $adapter
28
     */
29 2361
    public function __construct(EcAdapter $adapter)
30
    {
31 2361
        $this->ecAdapter = $adapter;
32 2361
    }
33
34
    /**
35
     * @return EcAdapterInterface
36
     */
37 184
    public function getEcAdapter(): EcAdapterInterface
38
    {
39 184
        return $this->ecAdapter;
40
    }
41
42
    /**
43
     * @return Template
44
     */
45 142
    private function getInnerTemplate(): Template
46
    {
47 142
        return (new TemplateFactory())
48 142
            ->uint8()
49 142
            ->varstring()
50 142
            ->uint8()
51 142
            ->varstring()
52 142
            ->getTemplate();
53
    }
54
55
    /**
56
     * @return Template
57
     */
58 149
    private function getOuterTemplate(): Template
59
    {
60 149
        return (new TemplateFactory())
61 149
            ->uint8()
62 149
            ->varstring()
63 149
            ->getTemplate();
64
    }
65
66
    /**
67
     * @param SignatureInterface|\Mdanter\Ecc\Crypto\Signature\Signature $signature
68
     * @return BufferInterface
69
     */
70 1
    public function serialize(SignatureInterface $signature): BufferInterface
71
    {
72 1
        $math = $this->ecAdapter->getMath();
73
74
        // Ensure that the R and S hex's are of even length
75 1
        $rBin = $math->intToString($signature->getR());
76 1
        $sBin = $math->intToString($signature->getS());
77
78
        // Pad R and S if their highest bit is flipped, ie,
79
        // they are negative.
80 1
        $rt = $rBin[0] & pack('H*', '80');
81 1
        if (ord($rt) === 128) {
82
            $rBin = pack('H*', '00') . $rBin;
83
        }
84
85 1
        $st = $sBin[0] & pack('H*', '80');
86 1
        if (ord($st) === 128) {
87 1
            $sBin = pack('H*', '00') . $sBin;
88
        }
89
90 1
        return $this->getOuterTemplate()->write([
91 1
            0x30,
92 1
            $this->getInnerTemplate()->write([
93 1
                0x02,
94 1
                new Buffer($rBin, null),
95 1
                0x02,
96 1
                new Buffer($sBin, null)
97
            ])
98
        ]);
99
    }
100
101
    /**
102
     * @param Parser $parser
103
     * @return SignatureInterface
104
     * @throws ParserOutOfRange
105
     */
106 148
    public function fromParser(Parser $parser): SignatureInterface
107
    {
108
        try {
109 148
            list (, $inner) = $this->getOuterTemplate()->parse($parser);
110 141
            list (, $r, , $s) = $this->getInnerTemplate()->parse(new Parser($inner));
111
            /** @var Buffer $r */
112
            /** @var Buffer $s */
113
114 137
            return new Signature(
115 137
                $this->ecAdapter,
116 137
                $r->getGmp(),
117 135
                $s->getGmp()
118
            );
119 15
        } catch (ParserOutOfRange $e) {
120 11
            throw new ParserOutOfRange('Failed to extract full signature from parser');
121
        }
122
    }
123
124
    /**
125
     * @param BufferInterface $derSignature
126
     * @return SignatureInterface
127
     * @throws ParserOutOfRange
128
     */
129 148
    public function parse(BufferInterface $derSignature): SignatureInterface
130
    {
131 148
        return $this->fromParser(new Parser($derSignature));
132
    }
133
}
134