Passed
Branch master (a258f1)
by Milad
06:34 queued 03:16
created

AbstractEcdsaSigner::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\EcdsaPrivateKey;
6
use MiladRahimi\Jwt\Cryptography\Signer;
7
use MiladRahimi\Jwt\Exceptions\SigningException;
8
use function ltrim;
9
use function ord;
10
use function str_pad;
11
use function strlen;
12
use function substr;
13
14
abstract class AbstractEcdsaSigner implements Signer
15
{
16
    protected const ASN1_BIT_STRING = 0x03;
17
18
    protected static string $name;
19
20
    protected EcdsaPrivateKey $privateKey;
21
22
    public function __construct(EcdsaPrivateKey $privateKey)
23
    {
24
        $this->privateKey = $privateKey;
25
    }
26
27
    /**
28
     * @inheritdoc
29
     */
30
    public function name(): string
31
    {
32
        return static::$name;
33
    }
34
35
    public function sign(string $message): string
36
    {
37
        if (openssl_sign($message, $signature, $this->privateKey->getResource(), $this->algorithm()) === true) {
38
            return $this->derToSignature($signature, $this->keySize());
39
        }
40
41
        throw new SigningException(openssl_error_string() ?: "OpenSSL cannot sign the token.");
42
    }
43
44
    protected function algorithm(): int
45
    {
46
        return [
47
            'ES256' => OPENSSL_ALGO_SHA256,
48
            'ES256K' => OPENSSL_ALGO_SHA256,
49
            'ES384' => OPENSSL_ALGO_SHA384,
50
        ][static::$name];
51
    }
52
53
    protected function keySize(): int
54
    {
55
        return [
56
            'ES256' => 256,
57
            'ES256K' => 256,
58
            'ES384' => 384,
59
        ][static::$name];
60
    }
61
62
    protected function derToSignature(string $der, int $keySize): string
63
    {
64
        $i = $this->decodeDer($der)[0];
65
        [$i, $r] = $this->decodeDer($der, $i);
66
        $s = $this->decodeDer($der, $i)[1];
67
68
        $r = ltrim($r, "\x00");
69
        $s = ltrim($s, "\x00");
70
71
        $r = str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
72
        $s = str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
73
74
        return $r . $s;
75
    }
76
77
    protected function decodeDer(string $der, int $offset = 0): array
78
    {
79
        $pos = $offset;
80
        $size = strlen($der);
81
        $constructed = (ord($der[$pos]) >> 5) & 0x01;
82
        $type = ord($der[$pos++]) & 0x1f;
83
84
        $len = ord($der[$pos++]);
85
        if ($len & 0x80) {
86
            $n = $len & 0x1f;
87
            $len = 0;
88
            while ($n-- && $pos < $size) {
89
                $len = ($len << 8) | ord($der[$pos++]);
90
            }
91
        }
92
93
        if ($type === self::ASN1_BIT_STRING) {
94
            $pos++;
95
            $data = substr($der, $pos, $len - 1);
96
            $pos += $len - 1;
97
        } elseif (!$constructed) {
98
            $data = substr($der, $pos, $len);
99
            $pos += $len;
100
        } else {
101
            $data = '';
102
        }
103
104
        return [$pos, $data];
105
    }
106
107
    public function getPrivateKey(): EcdsaPrivateKey
108
    {
109
        return $this->privateKey;
110
    }
111
}
112