Passed
Push — master ( 280df4...3ba82a )
by thomas
51:58 queued 49:57
created

TransactionSignature::verifyElement()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 5
nop 4
dl 0
loc 16
ccs 11
cts 11
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 354
    public function __construct(EcAdapterInterface $ecAdapter, SignatureInterface $signature, int $hashType)
39
    {
40 354
        $this->ecAdapter = $ecAdapter;
41 354
        $this->signature = $signature;
42 354
        $this->hashType = $hashType;
43 354
    }
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 164
    private static function verifyElement(string $fieldName, int $start, int $length, string $binaryString)
72
    {
73 164
        if ($length === 0) {
74 9
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' length is zero');
75
        }
76 160
        $typePrefix = ord($binaryString[$start - 2]);
77 160
        if ($typePrefix !== 0x02) {
78 9
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value type mismatch');
79
        }
80
81 156
        $first = ord($binaryString[$start + 0]);
82 156
        if (($first & 0x80) === 128) {
83 33
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value is negative');
84
        }
85 136
        if ($length > 1 && $first === 0 && (ord($binaryString[$start + 1]) & 0x80) === 0) {
86 17
            throw new SignatureNotCanonical('Signature ' . $fieldName . ' value excessively padded');
87
        }
88 124
    }
89
90
    /**
91
     * @param BufferInterface $sig
92
     * @return bool
93
     * @throws SignatureNotCanonical
94
     */
95 198
    public static function isDERSignature(BufferInterface $sig): bool
96
    {
97 198
        $bin = $sig->getBinary();
98 198
        $size = $sig->getSize();
99 198
        if ($size < 9) {
100 14
            throw new SignatureNotCanonical('Signature too short');
101
        }
102
103 185
        if ($size > 73) {
104 5
            throw new SignatureNotCanonical('Signature too long');
105
        }
106
107 181
        if (ord($bin[0]) !== 0x30) {
108 2
            throw new SignatureNotCanonical('Signature has wrong type');
109
        }
110
111 180
        if (ord($bin[1]) !== $size - 3) {
112 9
            throw new SignatureNotCanonical('Signature has wrong length marker');
113
        }
114
115 172
        $lenR = ord($bin[3]);
116 172
        $startR = 4;
117 172
        if (5 + $lenR >= $size) {
118 5
            throw new SignatureNotCanonical('Signature S length misplaced');
119
        }
120
121 168
        $lenS = ord($bin[5 + $lenR]);
122 168
        $startS = 4 + $lenR + 2;
123 168
        if (($lenR + $lenS + 7) !== $size) {
124 5
            throw new SignatureNotCanonical('Signature R+S length mismatch');
125
        }
126
127 164
        self::verifyElement('R', $startR, $lenR, $bin);
128 124
        self::verifyElement('S', $startS, $lenS, $bin);
129
130 107
        return true;
131
    }
132
133
    /**
134
     * @return BufferInterface
135
     */
136 2
    public function getBuffer(): BufferInterface
137
    {
138 2
        $txSigSerializer = new TransactionSignatureSerializer(
139 2
            EcSerializer::getSerializer(DerSignatureSerializerInterface::class, true, $this->ecAdapter)
140
        );
141
142 2
        return $txSigSerializer->serialize($this);
143
    }
144
}
145