Passed
Push — master ( f1e8df...a258f1 )
by Milad
03:17
created

AbstractEcdsaVerifier::algorithm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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