Passed
Push — master ( d759f2...bf67e8 )
by Milad
02:12
created

AbstractEcdsaSigner   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 38
dl 0
loc 80
rs 10
c 1
b 0
f 0
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 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
    use Algorithm;
17
18
    protected const ASN1_BIT_STRING = 0x03;
19
20
    protected EcdsaPrivateKey $privateKey;
21
22
    protected ?string $kid;
23
24
    public function __construct(EcdsaPrivateKey $privateKey)
25
    {
26
        $this->privateKey = $privateKey;
27
    }
28
29
    public function sign(string $message): string
30
    {
31
        if (openssl_sign($message, $signature, $this->privateKey->getResource(), $this->algorithm()) === true) {
32
            return $this->derToSignature($signature, $this->keySize());
33
        }
34
35
        throw new SigningException(openssl_error_string() ?: "OpenSSL cannot sign the token.");
36
    }
37
38
    protected function derToSignature(string $der, int $keySize): string
39
    {
40
        $i = $this->decodeDer($der)[0];
41
        [$i, $r] = $this->decodeDer($der, $i);
42
        $s = $this->decodeDer($der, $i)[1];
43
44
        $r = ltrim($r, "\x00");
45
        $s = ltrim($s, "\x00");
46
47
        $r = str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
48
        $s = str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
49
50
        return $r . $s;
51
    }
52
53
    protected function decodeDer(string $der, int $offset = 0): array
54
    {
55
        $pos = $offset;
56
        $size = strlen($der);
57
        $constructed = (ord($der[$pos]) >> 5) & 0x01;
58
        $type = ord($der[$pos++]) & 0x1f;
59
60
        $len = ord($der[$pos++]);
61
        if ($len & 0x80) {
62
            $n = $len & 0x1f;
63
            $len = 0;
64
            while ($n-- && $pos < $size) {
65
                $len = ($len << 8) | ord($der[$pos++]);
66
            }
67
        }
68
69
        if ($type === self::ASN1_BIT_STRING) {
70
            $pos++;
71
            $data = substr($der, $pos, $len - 1);
72
            $pos += $len - 1;
73
        } elseif (!$constructed) {
74
            $data = substr($der, $pos, $len);
75
            $pos += $len;
76
        } else {
77
            $data = '';
78
        }
79
80
        return [$pos, $data];
81
    }
82
83
    /**
84
     * @inheritDoc
85
     */
86
    public function kid(): ?string
87
    {
88
        return $this->kid;
89
    }
90
91
    public function getPrivateKey(): EcdsaPrivateKey
92
    {
93
        return $this->privateKey;
94
    }
95
}
96