Completed
Pull Request — master (#754)
by thomas
39:11
created

TransactionSignature::verifyElement()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 5
nop 4
dl 0
loc 16
ccs 7
cts 7
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Signature;
6
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\EcSerializer;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Signature\DerSignatureSerializerInterface;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
11
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
12
use BitWasp\Bitcoin\Serializable;
13
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
14
use BitWasp\Buffertools\BufferInterface;
15
16
class TransactionSignature extends Serializable implements TransactionSignatureInterface
17
{
18
    /**
19
     * @var EcAdapterInterface
20
     */
21
    private $ecAdapter;
22
23
    /**
24
     * @var SignatureInterface
25
     */
26
    private $signature;
27
28
    /**
29
     * @var int
30
     */
31
    private $hashType;
32
33
    /**
34
     * @param EcAdapterInterface $ecAdapter
35
     * @param SignatureInterface $signature
36
     * @param int $hashType
37
     */
38 358
    public function __construct(EcAdapterInterface $ecAdapter, SignatureInterface $signature, int $hashType)
39
    {
40 358
        $this->ecAdapter = $ecAdapter;
41 358
        $this->signature = $signature;
42 358
        $this->hashType = $hashType;
43 358
    }
44
45
    /**
46
     * @return SignatureInterface
47
     */
48 338
    public function getSignature(): SignatureInterface
49
    {
50 338
        return $this->signature;
51
    }
52
53
    /**
54
     * @return int
55
     */
56 338
    public function getHashType(): int
57
    {
58 338
        return $this->hashType;
59
    }
60
61
    /**
62
     * @param TransactionSignatureInterface $other
63
     * @return bool
64
     */
65 78
    public function equals(TransactionSignatureInterface $other): bool
66
    {
67 78
        return $this->signature->equals($other->getSignature())
68 78
            && $this->hashType === $other->getHashType();
69
    }
70
71
    private static function verifyElement(string $fieldName, int $start, int $length, string $binaryString)
72
    {
73
        if ($length === 0) {
74
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' length is zero');
75
        }
76
        $typePrefix = ord($binaryString[$start - 2]);
77
        if ($typePrefix !== 0x02) {
78 198
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value type mismatch');
79 164
        }
80 9
81
        $first = ord($binaryString[$start + 0]);
82 160
        if (($first & 0x80) === 128) {
83 160
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value is negative');
84 9
        }
85
        if ($length > 1 && $first === 0 && (ord($binaryString[$start + 1]) & 0x80) === 0) {
86 156
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value excessively padded');
87 156
        }
88 156
    }
89 33
90
    /**
91 136
     * @param BufferInterface $sig
92 17
     * @return bool
93
     * @throws SignatureNotCanonical
94 198
     */
95
    public static function isDERSignature(BufferInterface $sig): bool
96 198
    {
97 198
        $bin = $sig->getBinary();
98 198
        $size = $sig->getSize();
99 14
        if ($size < 9) {
100
            throw new SignatureNotCanonical('Signature too short');
101
        }
102 185
103 5
        if ($size > 73) {
104
            throw new SignatureNotCanonical('Signature too long');
105
        }
106 181
107 2
        if (ord($bin[0]) !== 0x30) {
108
            throw new SignatureNotCanonical('Signature has wrong type');
109
        }
110 180
111 9
        if (ord($bin[1]) !== $size - 3) {
112
            throw new SignatureNotCanonical('Signature has wrong length marker');
113
        }
114 172
115 172
        $lenR = ord($bin[3]);
116 172
        $startR = 4;
117 5
        if (5 + $lenR >= $size) {
118
            throw new SignatureNotCanonical('Signature S length misplaced');
119
        }
120 168
121 168
        $lenS = ord($bin[5 + $lenR]);
122 168
        $startS = 4 + $lenR + 2;
123 5
        if (($lenR + $lenS + 7) !== $size) {
124
            throw new SignatureNotCanonical('Signature R+S length mismatch');
125
        }
126 164
127 124
        self::verifyElement('R', $startR, $lenR, $bin);
128
        self::verifyElement('S', $startS, $lenS, $bin);
129 107
130
        return true;
131
    }
132
133
    /**
134
     * @return BufferInterface
135 2
     */
136
    public function getBuffer(): BufferInterface
137 2
    {
138 2
        $txSigSerializer = new TransactionSignatureSerializer(
139
            EcSerializer::getSerializer(DerSignatureSerializerInterface::class, true, $this->ecAdapter)
140
        );
141 2
142
        return $txSigSerializer->serialize($this);
143
    }
144
}
145