AbstractEcdsaVerifier::encodeDer()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 11
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa;
6
7
use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPublicKey;
8
use MiladRahimi\Jwt\Cryptography\Verifier;
9
use MiladRahimi\Jwt\Exceptions\InvalidSignatureException;
10
11
use function chr;
12
use function ltrim;
13
use function ord;
14
use function str_split;
15
use function strlen;
16
17
abstract class AbstractEcdsaVerifier implements Verifier
18
{
19
    use Algorithm;
20
21
    protected const ASN1_INTEGER = 0x02;
22
    protected const ASN1_SEQUENCE = 0x10;
23
24
    protected EcdsaPublicKey $publicKey;
25
26
    public function __construct(EcdsaPublicKey $publicKey)
27
    {
28
        $this->publicKey = $publicKey;
29
    }
30
31
    /**
32
     * @inheritdoc
33
     */
34
    public function verify(string $plain, string $signature): void
35
    {
36
        $signature = $this->signatureToDer($signature);
37
        if (openssl_verify($plain, $signature, $this->publicKey->getResource(), $this->algorithm()) !== 1) {
38
            throw new InvalidSignatureException(openssl_error_string() ?: "The signature is invalid.");
39
        }
40
    }
41
42
    protected function signatureToDer(string $signature): string
43
    {
44
        $length = max(1, (int)(strlen($signature) / 2));
45
        [$r, $s] = str_split($signature, $length);
46
47
        $r = ltrim($r, "\x00");
48
        $s = ltrim($s, "\x00");
49
50
        if (ord($r[0]) > 0x7f) {
51
            $r = "\x00" . $r;
52
        }
53
        if (ord($s[0]) > 0x7f) {
54
            $s = "\x00" . $s;
55
        }
56
57
        return $this->encodeDer(
58
            self::ASN1_SEQUENCE,
59
            $this->encodeDer(self::ASN1_INTEGER, $r) . $this->encodeDer(self::ASN1_INTEGER, $s),
60
        );
61
    }
62
63
    protected function encodeDer(int $type, string $value): string
64
    {
65
        $tagHeader = 0;
66
        if ($type === self::ASN1_SEQUENCE) {
67
            $tagHeader |= 0x20;
68
        }
69
70
        $der = chr($tagHeader | $type);
71
        $der .= chr(strlen($value));
72
73
        return $der . $value;
74
    }
75
76
    /**
77
     * @inheritDoc
78
     */
79
    public function kid(): ?string
80
    {
81
        return $this->publicKey->getId();
82
    }
83
84
    public function getPublicKey(): EcdsaPublicKey
85
    {
86
        return $this->publicKey;
87
    }
88
}
89