AbstractEcdsaSigner   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 78
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 37
c 1
b 0
f 0
dl 0
loc 78
rs 10
wmc 13

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getPrivateKey() 0 3 1
A __construct() 0 3 1
A derToSignature() 0 13 1
B decodeDer() 0 28 6
A kid() 0 3 1
A sign() 0 7 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa;
6
7
use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPrivateKey;
8
use MiladRahimi\Jwt\Cryptography\Signer;
9
use MiladRahimi\Jwt\Exceptions\SigningException;
10
11
use function ltrim;
12
use function ord;
13
use function str_pad;
14
use function strlen;
15
use function substr;
16
17
abstract class AbstractEcdsaSigner implements Signer
18
{
19
    use Algorithm;
20
21
    protected const ASN1_BIT_STRING = 0x03;
22
23
    protected EcdsaPrivateKey $privateKey;
24
25
    public function __construct(EcdsaPrivateKey $privateKey)
26
    {
27
        $this->privateKey = $privateKey;
28
    }
29
30
    public function sign(string $message): string
31
    {
32
        if (openssl_sign($message, $signature, $this->privateKey->getResource(), $this->algorithm()) === true) {
33
            return $this->derToSignature($signature, $this->keySize());
34
        }
35
36
        throw new SigningException(openssl_error_string() ?: "OpenSSL cannot sign the token.");
37
    }
38
39
    protected function derToSignature(string $der, int $keySize): string
40
    {
41
        $i = $this->decodeDer($der)[0];
42
        [$i, $r] = $this->decodeDer($der, $i);
43
        $s = $this->decodeDer($der, $i)[1];
44
45
        $r = ltrim($r, "\x00");
46
        $s = ltrim($s, "\x00");
47
48
        $r = str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
49
        $s = str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
50
51
        return $r . $s;
52
    }
53
54
    protected function decodeDer(string $der, int $offset = 0): array
55
    {
56
        $pos = $offset;
57
        $size = strlen($der);
58
        $constructed = (ord($der[$pos]) >> 5) & 0x01;
59
        $type = ord($der[$pos++]) & 0x1f;
60
61
        $len = ord($der[$pos++]);
62
        if ($len & 0x80) {
63
            $n = $len & 0x1f;
64
            $len = 0;
65
            while ($n-- && $pos < $size) {
66
                $len = ($len << 8) | ord($der[$pos++]);
67
            }
68
        }
69
70
        if ($type === self::ASN1_BIT_STRING) {
71
            $pos++;
72
            $data = substr($der, $pos, $len - 1);
73
            $pos += $len - 1;
74
        } elseif (!$constructed) {
75
            $data = substr($der, $pos, $len);
76
            $pos += $len;
77
        } else {
78
            $data = '';
79
        }
80
81
        return [$pos, $data];
82
    }
83
84
    /**
85
     * @inheritDoc
86
     */
87
    public function kid(): ?string
88
    {
89
        return $this->privateKey->getId();
90
    }
91
92
    public function getPrivateKey(): EcdsaPrivateKey
93
    {
94
        return $this->privateKey;
95
    }
96
}
97